Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
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/FloatingPoint.h"
11 : #include "mozilla/Move.h"
12 : #include "mozilla/Sprintf.h"
13 : #include "mozilla/Unused.h"
14 :
15 : #include <cmath>
16 :
17 : #include "jsapi.h"
18 : #include "jscntxt.h"
19 : #include "jsfriendapi.h"
20 : #include "jsgc.h"
21 : #include "jsobj.h"
22 : #include "jsprf.h"
23 : #include "jswrapper.h"
24 :
25 : #include "builtin/Promise.h"
26 : #include "builtin/SelfHostingDefines.h"
27 : #ifdef DEBUG
28 : #include "frontend/TokenStream.h"
29 : #include "irregexp/RegExpAST.h"
30 : #include "irregexp/RegExpEngine.h"
31 : #include "irregexp/RegExpParser.h"
32 : #endif
33 : #include "jit/InlinableNatives.h"
34 : #include "jit/JitFrameIterator.h"
35 : #include "js/Debug.h"
36 : #include "js/HashTable.h"
37 : #include "js/StructuredClone.h"
38 : #include "js/UbiNode.h"
39 : #include "js/UbiNodeBreadthFirst.h"
40 : #include "js/UbiNodeShortestPaths.h"
41 : #include "js/UniquePtr.h"
42 : #include "js/Vector.h"
43 : #include "vm/GlobalObject.h"
44 : #include "vm/Interpreter.h"
45 : #include "vm/ProxyObject.h"
46 : #include "vm/SavedStacks.h"
47 : #include "vm/Stack.h"
48 : #include "vm/StringBuffer.h"
49 : #include "vm/TraceLogging.h"
50 : #include "wasm/AsmJS.h"
51 : #include "wasm/WasmBinaryToExperimentalText.h"
52 : #include "wasm/WasmBinaryToText.h"
53 : #include "wasm/WasmJS.h"
54 : #include "wasm/WasmModule.h"
55 : #include "wasm/WasmSignalHandlers.h"
56 : #include "wasm/WasmTextToBinary.h"
57 :
58 : #include "jscntxtinlines.h"
59 : #include "jsobjinlines.h"
60 :
61 : #include "vm/EnvironmentObject-inl.h"
62 : #include "vm/NativeObject-inl.h"
63 :
64 : using namespace js;
65 :
66 : using mozilla::ArrayLength;
67 : using mozilla::Move;
68 :
69 : // If fuzzingSafe is set, remove functionality that could cause problems with
70 : // fuzzers. Set this via the environment variable MOZ_FUZZING_SAFE.
71 : static mozilla::Atomic<bool> fuzzingSafe(false);
72 :
73 : // If disableOOMFunctions is set, disable functionality that causes artificial
74 : // OOM conditions.
75 : static mozilla::Atomic<bool> disableOOMFunctions(false);
76 :
77 : static bool
78 0 : EnvVarIsDefined(const char* name)
79 : {
80 0 : const char* value = getenv(name);
81 0 : return value && *value;
82 : }
83 :
84 : #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
85 : static bool
86 0 : EnvVarAsInt(const char* name, int* valueOut)
87 : {
88 0 : if (!EnvVarIsDefined(name))
89 0 : return false;
90 :
91 0 : *valueOut = atoi(getenv(name));
92 0 : return true;
93 : }
94 : #endif
95 :
96 : static bool
97 0 : GetBuildConfiguration(JSContext* cx, unsigned argc, Value* vp)
98 : {
99 0 : CallArgs args = CallArgsFromVp(argc, vp);
100 0 : RootedObject info(cx, JS_NewPlainObject(cx));
101 0 : if (!info)
102 0 : return false;
103 :
104 0 : if (!JS_SetProperty(cx, info, "rooting-analysis", FalseHandleValue))
105 0 : return false;
106 :
107 0 : if (!JS_SetProperty(cx, info, "exact-rooting", TrueHandleValue))
108 0 : return false;
109 :
110 0 : if (!JS_SetProperty(cx, info, "trace-jscalls-api", FalseHandleValue))
111 0 : return false;
112 :
113 0 : if (!JS_SetProperty(cx, info, "incremental-gc", TrueHandleValue))
114 0 : return false;
115 :
116 0 : if (!JS_SetProperty(cx, info, "generational-gc", TrueHandleValue))
117 0 : return false;
118 :
119 0 : RootedValue value(cx);
120 : #ifdef DEBUG
121 0 : value = BooleanValue(true);
122 : #else
123 : value = BooleanValue(false);
124 : #endif
125 0 : if (!JS_SetProperty(cx, info, "debug", value))
126 0 : return false;
127 :
128 : #ifdef RELEASE_OR_BETA
129 : value = BooleanValue(true);
130 : #else
131 0 : value = BooleanValue(false);
132 : #endif
133 0 : if (!JS_SetProperty(cx, info, "release_or_beta", value))
134 0 : return false;
135 :
136 : #ifdef JS_HAS_CTYPES
137 0 : value = BooleanValue(true);
138 : #else
139 : value = BooleanValue(false);
140 : #endif
141 0 : if (!JS_SetProperty(cx, info, "has-ctypes", value))
142 0 : return false;
143 :
144 : #if defined(_M_IX86) || defined(__i386__)
145 : value = BooleanValue(true);
146 : #else
147 0 : value = BooleanValue(false);
148 : #endif
149 0 : if (!JS_SetProperty(cx, info, "x86", value))
150 0 : return false;
151 :
152 : #if defined(_M_X64) || defined(__x86_64__)
153 0 : value = BooleanValue(true);
154 : #else
155 : value = BooleanValue(false);
156 : #endif
157 0 : if (!JS_SetProperty(cx, info, "x64", value))
158 0 : return false;
159 :
160 : #ifdef JS_SIMULATOR_ARM
161 : value = BooleanValue(true);
162 : #else
163 0 : value = BooleanValue(false);
164 : #endif
165 0 : if (!JS_SetProperty(cx, info, "arm-simulator", value))
166 0 : return false;
167 :
168 : #ifdef JS_SIMULATOR_ARM64
169 : value = BooleanValue(true);
170 : #else
171 0 : value = BooleanValue(false);
172 : #endif
173 0 : if (!JS_SetProperty(cx, info, "arm64-simulator", value))
174 0 : return false;
175 :
176 : #ifdef MOZ_ASAN
177 : value = BooleanValue(true);
178 : #else
179 0 : value = BooleanValue(false);
180 : #endif
181 0 : if (!JS_SetProperty(cx, info, "asan", value))
182 0 : return false;
183 :
184 : #ifdef MOZ_TSAN
185 : value = BooleanValue(true);
186 : #else
187 0 : value = BooleanValue(false);
188 : #endif
189 0 : if (!JS_SetProperty(cx, info, "tsan", value))
190 0 : return false;
191 :
192 : #ifdef JS_GC_ZEAL
193 0 : value = BooleanValue(true);
194 : #else
195 : value = BooleanValue(false);
196 : #endif
197 0 : if (!JS_SetProperty(cx, info, "has-gczeal", value))
198 0 : return false;
199 :
200 : #ifdef JS_MORE_DETERMINISTIC
201 : value = BooleanValue(true);
202 : #else
203 0 : value = BooleanValue(false);
204 : #endif
205 0 : if (!JS_SetProperty(cx, info, "more-deterministic", value))
206 0 : return false;
207 :
208 : #ifdef MOZ_PROFILING
209 0 : value = BooleanValue(true);
210 : #else
211 : value = BooleanValue(false);
212 : #endif
213 0 : if (!JS_SetProperty(cx, info, "profiling", value))
214 0 : return false;
215 :
216 : #ifdef INCLUDE_MOZILLA_DTRACE
217 : value = BooleanValue(true);
218 : #else
219 0 : value = BooleanValue(false);
220 : #endif
221 0 : if (!JS_SetProperty(cx, info, "dtrace", value))
222 0 : return false;
223 :
224 : #ifdef MOZ_VALGRIND
225 : value = BooleanValue(true);
226 : #else
227 0 : value = BooleanValue(false);
228 : #endif
229 0 : if (!JS_SetProperty(cx, info, "valgrind", value))
230 0 : return false;
231 :
232 : #ifdef JS_OOM_DO_BACKTRACES
233 : value = BooleanValue(true);
234 : #else
235 0 : value = BooleanValue(false);
236 : #endif
237 0 : if (!JS_SetProperty(cx, info, "oom-backtraces", value))
238 0 : return false;
239 :
240 : #ifdef ENABLE_BINARYDATA
241 0 : value = BooleanValue(true);
242 : #else
243 : value = BooleanValue(false);
244 : #endif
245 0 : if (!JS_SetProperty(cx, info, "binary-data", value))
246 0 : return false;
247 :
248 : #ifdef EXPOSE_INTL_API
249 0 : value = BooleanValue(true);
250 : #else
251 : value = BooleanValue(false);
252 : #endif
253 0 : if (!JS_SetProperty(cx, info, "intl-api", value))
254 0 : return false;
255 :
256 : #if defined(SOLARIS)
257 : value = BooleanValue(false);
258 : #else
259 0 : value = BooleanValue(true);
260 : #endif
261 0 : if (!JS_SetProperty(cx, info, "mapped-array-buffer", value))
262 0 : return false;
263 :
264 : #ifdef MOZ_MEMORY
265 0 : value = BooleanValue(true);
266 : #else
267 : value = BooleanValue(false);
268 : #endif
269 0 : if (!JS_SetProperty(cx, info, "moz-memory", value))
270 0 : return false;
271 :
272 0 : value.setInt32(sizeof(void*));
273 0 : if (!JS_SetProperty(cx, info, "pointer-byte-size", value))
274 0 : return false;
275 :
276 0 : args.rval().setObject(*info);
277 0 : return true;
278 : }
279 :
280 : static bool
281 0 : GC(JSContext* cx, unsigned argc, Value* vp)
282 : {
283 0 : CallArgs args = CallArgsFromVp(argc, vp);
284 :
285 : /*
286 : * If the first argument is 'zone', we collect any zones previously
287 : * scheduled for GC via schedulegc. If the first argument is an object, we
288 : * collect the object's zone (and any other zones scheduled for
289 : * GC). Otherwise, we collect all zones.
290 : */
291 0 : bool zone = false;
292 0 : if (args.length() >= 1) {
293 0 : Value arg = args[0];
294 0 : if (arg.isString()) {
295 0 : if (!JS_StringEqualsAscii(cx, arg.toString(), "zone", &zone))
296 0 : return false;
297 0 : } else if (arg.isObject()) {
298 0 : PrepareZoneForGC(UncheckedUnwrap(&arg.toObject())->zone());
299 0 : zone = true;
300 : }
301 : }
302 :
303 0 : bool shrinking = false;
304 0 : if (args.length() >= 2) {
305 0 : Value arg = args[1];
306 0 : if (arg.isString()) {
307 0 : if (!JS_StringEqualsAscii(cx, arg.toString(), "shrinking", &shrinking))
308 0 : return false;
309 : }
310 : }
311 :
312 : #ifndef JS_MORE_DETERMINISTIC
313 0 : size_t preBytes = cx->runtime()->gc.usage.gcBytes();
314 : #endif
315 :
316 0 : if (zone)
317 0 : PrepareForDebugGC(cx->runtime());
318 : else
319 0 : JS::PrepareForFullGC(cx);
320 :
321 0 : JSGCInvocationKind gckind = shrinking ? GC_SHRINK : GC_NORMAL;
322 0 : JS::GCForReason(cx, gckind, JS::gcreason::API);
323 :
324 0 : char buf[256] = { '\0' };
325 : #ifndef JS_MORE_DETERMINISTIC
326 0 : SprintfLiteral(buf, "before %" PRIuSIZE ", after %" PRIuSIZE "\n",
327 0 : preBytes, cx->runtime()->gc.usage.gcBytes());
328 : #endif
329 0 : JSString* str = JS_NewStringCopyZ(cx, buf);
330 0 : if (!str)
331 0 : return false;
332 0 : args.rval().setString(str);
333 0 : return true;
334 : }
335 :
336 : static bool
337 0 : MinorGC(JSContext* cx, unsigned argc, Value* vp)
338 : {
339 0 : CallArgs args = CallArgsFromVp(argc, vp);
340 0 : if (args.get(0) == BooleanValue(true))
341 0 : cx->zone()->group()->storeBuffer().setAboutToOverflow();
342 :
343 0 : cx->minorGC(JS::gcreason::API);
344 0 : args.rval().setUndefined();
345 0 : return true;
346 : }
347 :
348 : #define FOR_EACH_GC_PARAM(_) \
349 : _("maxBytes", JSGC_MAX_BYTES, true) \
350 : _("maxMallocBytes", JSGC_MAX_MALLOC_BYTES, true) \
351 : _("maxNurseryBytes", JSGC_MAX_NURSERY_BYTES, true) \
352 : _("gcBytes", JSGC_BYTES, false) \
353 : _("gcNumber", JSGC_NUMBER, false) \
354 : _("mode", JSGC_MODE, true) \
355 : _("unusedChunks", JSGC_UNUSED_CHUNKS, false) \
356 : _("totalChunks", JSGC_TOTAL_CHUNKS, false) \
357 : _("sliceTimeBudget", JSGC_SLICE_TIME_BUDGET, true) \
358 : _("markStackLimit", JSGC_MARK_STACK_LIMIT, true) \
359 : _("highFrequencyTimeLimit", JSGC_HIGH_FREQUENCY_TIME_LIMIT, true) \
360 : _("highFrequencyLowLimit", JSGC_HIGH_FREQUENCY_LOW_LIMIT, true) \
361 : _("highFrequencyHighLimit", JSGC_HIGH_FREQUENCY_HIGH_LIMIT, true) \
362 : _("highFrequencyHeapGrowthMax", JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX, true) \
363 : _("highFrequencyHeapGrowthMin", JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN, true) \
364 : _("lowFrequencyHeapGrowth", JSGC_LOW_FREQUENCY_HEAP_GROWTH, true) \
365 : _("dynamicHeapGrowth", JSGC_DYNAMIC_HEAP_GROWTH, true) \
366 : _("dynamicMarkSlice", JSGC_DYNAMIC_MARK_SLICE, true) \
367 : _("allocationThreshold", JSGC_ALLOCATION_THRESHOLD, true) \
368 : _("minEmptyChunkCount", JSGC_MIN_EMPTY_CHUNK_COUNT, true) \
369 : _("maxEmptyChunkCount", JSGC_MAX_EMPTY_CHUNK_COUNT, true) \
370 : _("compactingEnabled", JSGC_COMPACTING_ENABLED, true) \
371 : _("refreshFrameSlicesEnabled", JSGC_REFRESH_FRAME_SLICES_ENABLED, true)
372 :
373 : static const struct ParamInfo {
374 : const char* name;
375 : JSGCParamKey param;
376 : bool writable;
377 : } paramMap[] = {
378 : #define DEFINE_PARAM_INFO(name, key, writable) \
379 : {name, key, writable},
380 : FOR_EACH_GC_PARAM(DEFINE_PARAM_INFO)
381 : #undef DEFINE_PARAM_INFO
382 : };
383 :
384 : #define PARAM_NAME_LIST_ENTRY(name, key, writable) \
385 : " " name
386 : #define GC_PARAMETER_ARGS_LIST FOR_EACH_GC_PARAM(PARAM_NAME_LIST_ENTRY)
387 :
388 : static bool
389 0 : GCParameter(JSContext* cx, unsigned argc, Value* vp)
390 : {
391 0 : CallArgs args = CallArgsFromVp(argc, vp);
392 :
393 0 : JSString* str = ToString(cx, args.get(0));
394 0 : if (!str)
395 0 : return false;
396 :
397 0 : JSFlatString* flatStr = JS_FlattenString(cx, str);
398 0 : if (!flatStr)
399 0 : return false;
400 :
401 0 : size_t paramIndex = 0;
402 0 : for (;; paramIndex++) {
403 0 : if (paramIndex == ArrayLength(paramMap)) {
404 : JS_ReportErrorASCII(cx,
405 0 : "the first argument must be one of:" GC_PARAMETER_ARGS_LIST);
406 0 : return false;
407 : }
408 0 : if (JS_FlatStringEqualsAscii(flatStr, paramMap[paramIndex].name))
409 0 : break;
410 : }
411 0 : const ParamInfo& info = paramMap[paramIndex];
412 0 : JSGCParamKey param = info.param;
413 :
414 : // Request mode.
415 0 : if (args.length() == 1) {
416 0 : uint32_t value = JS_GetGCParameter(cx, param);
417 0 : args.rval().setNumber(value);
418 0 : return true;
419 : }
420 :
421 0 : if (!info.writable) {
422 0 : JS_ReportErrorASCII(cx, "Attempt to change read-only parameter %s", info.name);
423 0 : return false;
424 : }
425 :
426 0 : if (disableOOMFunctions) {
427 0 : switch (param) {
428 : case JSGC_MAX_BYTES:
429 : case JSGC_MAX_MALLOC_BYTES:
430 : case JSGC_MAX_NURSERY_BYTES:
431 0 : args.rval().setUndefined();
432 0 : return true;
433 : default:
434 0 : break;
435 : }
436 : }
437 :
438 : double d;
439 0 : if (!ToNumber(cx, args[1], &d))
440 0 : return false;
441 :
442 0 : if (d < 0 || d > UINT32_MAX) {
443 0 : JS_ReportErrorASCII(cx, "Parameter value out of range");
444 0 : return false;
445 : }
446 :
447 0 : uint32_t value = floor(d);
448 0 : if (param == JSGC_MARK_STACK_LIMIT && JS::IsIncrementalGCInProgress(cx)) {
449 0 : JS_ReportErrorASCII(cx, "attempt to set markStackLimit while a GC is in progress");
450 0 : return false;
451 : }
452 :
453 0 : if (param == JSGC_MAX_BYTES) {
454 0 : uint32_t gcBytes = JS_GetGCParameter(cx, JSGC_BYTES);
455 0 : if (value < gcBytes) {
456 : JS_ReportErrorASCII(cx,
457 : "attempt to set maxBytes to the value less than the current "
458 : "gcBytes (%u)",
459 0 : gcBytes);
460 0 : return false;
461 : }
462 : }
463 :
464 : bool ok;
465 : {
466 0 : JSRuntime* rt = cx->runtime();
467 0 : AutoLockGC lock(rt);
468 0 : ok = rt->gc.setParameter(param, value, lock);
469 : }
470 :
471 0 : if (!ok) {
472 0 : JS_ReportErrorASCII(cx, "Parameter value out of range");
473 0 : return false;
474 : }
475 :
476 0 : args.rval().setUndefined();
477 0 : return true;
478 : }
479 :
480 : static void
481 0 : SetAllowRelazification(JSContext* cx, bool allow)
482 : {
483 0 : JSRuntime* rt = cx->runtime();
484 0 : MOZ_ASSERT(rt->allowRelazificationForTesting != allow);
485 0 : rt->allowRelazificationForTesting = allow;
486 :
487 0 : for (AllScriptFramesIter i(cx); !i.done(); ++i)
488 0 : i.script()->setDoNotRelazify(allow);
489 0 : }
490 :
491 : static bool
492 0 : RelazifyFunctions(JSContext* cx, unsigned argc, Value* vp)
493 : {
494 : // Relazifying functions on GC is usually only done for compartments that are
495 : // not active. To aid fuzzing, this testing function allows us to relazify
496 : // even if the compartment is active.
497 :
498 0 : CallArgs args = CallArgsFromVp(argc, vp);
499 0 : SetAllowRelazification(cx, true);
500 :
501 0 : JS::PrepareForFullGC(cx);
502 0 : JS::GCForReason(cx, GC_SHRINK, JS::gcreason::API);
503 :
504 0 : SetAllowRelazification(cx, false);
505 0 : args.rval().setUndefined();
506 0 : return true;
507 : }
508 :
509 : static bool
510 0 : IsProxy(JSContext* cx, unsigned argc, Value* vp)
511 : {
512 0 : CallArgs args = CallArgsFromVp(argc, vp);
513 0 : if (args.length() != 1) {
514 0 : JS_ReportErrorASCII(cx, "the function takes exactly one argument");
515 0 : return false;
516 : }
517 0 : if (!args[0].isObject()) {
518 0 : args.rval().setBoolean(false);
519 0 : return true;
520 : }
521 0 : args.rval().setBoolean(args[0].toObject().is<ProxyObject>());
522 0 : return true;
523 : }
524 :
525 : static bool
526 0 : WasmIsSupported(JSContext* cx, unsigned argc, Value* vp)
527 : {
528 0 : CallArgs args = CallArgsFromVp(argc, vp);
529 0 : args.rval().setBoolean(wasm::HasSupport(cx));
530 0 : return true;
531 : }
532 :
533 : static bool
534 0 : WasmTextToBinary(JSContext* cx, unsigned argc, Value* vp)
535 : {
536 0 : CallArgs args = CallArgsFromVp(argc, vp);
537 0 : RootedObject callee(cx, &args.callee());
538 :
539 0 : if (!args.requireAtLeast(cx, "wasmTextToBinary", 1))
540 0 : return false;
541 :
542 0 : if (!args[0].isString()) {
543 0 : ReportUsageErrorASCII(cx, callee, "First argument must be a String");
544 0 : return false;
545 : }
546 :
547 0 : AutoStableStringChars twoByteChars(cx);
548 0 : if (!twoByteChars.initTwoByte(cx, args[0].toString()))
549 0 : return false;
550 :
551 0 : if (args.hasDefined(1)) {
552 0 : if (!args[1].isString()) {
553 0 : ReportUsageErrorASCII(cx, callee, "Second argument, if present, must be a String");
554 0 : return false;
555 : }
556 : }
557 :
558 0 : wasm::Bytes bytes;
559 0 : UniqueChars error;
560 0 : if (!wasm::TextToBinary(twoByteChars.twoByteChars(), &bytes, &error)) {
561 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_TEXT_FAIL,
562 0 : error.get() ? error.get() : "out of memory");
563 0 : return false;
564 : }
565 :
566 0 : RootedObject obj(cx, JS_NewUint8Array(cx, bytes.length()));
567 0 : if (!obj)
568 0 : return false;
569 :
570 0 : memcpy(obj->as<TypedArrayObject>().viewDataUnshared(), bytes.begin(), bytes.length());
571 :
572 0 : args.rval().setObject(*obj);
573 0 : return true;
574 : }
575 :
576 : static bool
577 0 : WasmBinaryToText(JSContext* cx, unsigned argc, Value* vp)
578 : {
579 0 : if (!cx->options().wasm()) {
580 0 : JS_ReportErrorASCII(cx, "wasm support unavailable");
581 0 : return false;
582 : }
583 :
584 0 : CallArgs args = CallArgsFromVp(argc, vp);
585 :
586 0 : if (!args.get(0).isObject() || !args.get(0).toObject().is<TypedArrayObject>()) {
587 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_ARG);
588 0 : return false;
589 : }
590 :
591 0 : Rooted<TypedArrayObject*> code(cx, &args[0].toObject().as<TypedArrayObject>());
592 :
593 0 : if (!TypedArrayObject::ensureHasBuffer(cx, code))
594 0 : return false;
595 :
596 0 : if (code->isSharedMemory()) {
597 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_ARG);
598 0 : return false;
599 : }
600 :
601 0 : const uint8_t* bufferStart = code->bufferUnshared()->dataPointer();
602 0 : const uint8_t* bytes = bufferStart + code->byteOffset();
603 0 : uint32_t length = code->byteLength();
604 :
605 0 : Vector<uint8_t> copy(cx);
606 0 : if (code->bufferUnshared()->hasInlineData()) {
607 0 : if (!copy.append(bytes, length))
608 0 : return false;
609 0 : bytes = copy.begin();
610 : }
611 :
612 0 : bool experimental = false;
613 0 : if (args.length() > 1) {
614 0 : JSString* opt = JS::ToString(cx, args[1]);
615 0 : if (!opt)
616 0 : return false;
617 : bool match;
618 0 : if (!JS_StringEqualsAscii(cx, opt, "experimental", &match))
619 0 : return false;
620 0 : experimental = match;
621 : }
622 :
623 0 : StringBuffer buffer(cx);
624 : bool ok;
625 0 : if (experimental)
626 0 : ok = wasm::BinaryToExperimentalText(cx, bytes, length, buffer);
627 : else
628 0 : ok = wasm::BinaryToText(cx, bytes, length, buffer);
629 :
630 0 : if (!ok) {
631 0 : if (!cx->isExceptionPending())
632 0 : JS_ReportErrorASCII(cx, "wasm binary to text print error");
633 0 : return false;
634 : }
635 :
636 0 : JSString* result = buffer.finishString();
637 0 : if (!result)
638 0 : return false;
639 :
640 0 : args.rval().setString(result);
641 0 : return true;
642 : }
643 :
644 : static bool
645 0 : WasmExtractCode(JSContext* cx, unsigned argc, Value* vp)
646 : {
647 0 : if (!cx->options().wasm()) {
648 0 : JS_ReportErrorASCII(cx, "wasm support unavailable");
649 0 : return false;
650 : }
651 :
652 0 : CallArgs args = CallArgsFromVp(argc, vp);
653 :
654 0 : if (!args.get(0).isObject()) {
655 0 : JS_ReportErrorASCII(cx, "argument is not an object");
656 0 : return false;
657 : }
658 :
659 0 : JSObject* unwrapped = CheckedUnwrap(&args.get(0).toObject());
660 0 : if (!unwrapped || !unwrapped->is<WasmModuleObject>()) {
661 0 : JS_ReportErrorASCII(cx, "argument is not a WebAssembly.Module");
662 0 : return false;
663 : }
664 :
665 0 : Rooted<WasmModuleObject*> module(cx, &unwrapped->as<WasmModuleObject>());
666 0 : RootedValue result(cx);
667 0 : if (!module->module().extractCode(cx, &result))
668 0 : return false;
669 :
670 0 : args.rval().set(result);
671 0 : return true;
672 : }
673 :
674 : static bool
675 0 : IsLazyFunction(JSContext* cx, unsigned argc, Value* vp)
676 : {
677 0 : CallArgs args = CallArgsFromVp(argc, vp);
678 0 : if (args.length() != 1) {
679 0 : JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
680 0 : return false;
681 : }
682 0 : if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
683 0 : JS_ReportErrorASCII(cx, "The first argument should be a function.");
684 0 : return false;
685 : }
686 0 : args.rval().setBoolean(args[0].toObject().as<JSFunction>().isInterpretedLazy());
687 0 : return true;
688 : }
689 :
690 : static bool
691 0 : IsRelazifiableFunction(JSContext* cx, unsigned argc, Value* vp)
692 : {
693 0 : CallArgs args = CallArgsFromVp(argc, vp);
694 0 : if (args.length() != 1) {
695 0 : JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
696 0 : return false;
697 : }
698 0 : if (!args[0].isObject() ||
699 0 : !args[0].toObject().is<JSFunction>())
700 : {
701 0 : JS_ReportErrorASCII(cx, "The first argument should be a function.");
702 0 : return false;
703 : }
704 :
705 0 : JSFunction* fun = &args[0].toObject().as<JSFunction>();
706 0 : args.rval().setBoolean(fun->hasScript() && fun->nonLazyScript()->isRelazifiable());
707 0 : return true;
708 : }
709 :
710 : static bool
711 0 : InternalConst(JSContext* cx, unsigned argc, Value* vp)
712 : {
713 0 : CallArgs args = CallArgsFromVp(argc, vp);
714 0 : if (args.length() == 0) {
715 0 : JS_ReportErrorASCII(cx, "the function takes exactly one argument");
716 0 : return false;
717 : }
718 :
719 0 : JSString* str = ToString(cx, args[0]);
720 0 : if (!str)
721 0 : return false;
722 0 : JSFlatString* flat = JS_FlattenString(cx, str);
723 0 : if (!flat)
724 0 : return false;
725 :
726 0 : if (JS_FlatStringEqualsAscii(flat, "INCREMENTAL_MARK_STACK_BASE_CAPACITY")) {
727 0 : args.rval().setNumber(uint32_t(js::INCREMENTAL_MARK_STACK_BASE_CAPACITY));
728 : } else {
729 0 : JS_ReportErrorASCII(cx, "unknown const name");
730 0 : return false;
731 : }
732 0 : return true;
733 : }
734 :
735 : static bool
736 0 : GCPreserveCode(JSContext* cx, unsigned argc, Value* vp)
737 : {
738 0 : CallArgs args = CallArgsFromVp(argc, vp);
739 :
740 0 : if (args.length() != 0) {
741 0 : RootedObject callee(cx, &args.callee());
742 0 : ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
743 0 : return false;
744 : }
745 :
746 0 : cx->runtime()->gc.setAlwaysPreserveCode();
747 :
748 0 : args.rval().setUndefined();
749 0 : return true;
750 : }
751 :
752 : #ifdef JS_GC_ZEAL
753 :
754 : static bool
755 0 : GCZeal(JSContext* cx, unsigned argc, Value* vp)
756 : {
757 0 : CallArgs args = CallArgsFromVp(argc, vp);
758 :
759 0 : if (args.length() > 2) {
760 0 : RootedObject callee(cx, &args.callee());
761 0 : ReportUsageErrorASCII(cx, callee, "Too many arguments");
762 0 : return false;
763 : }
764 :
765 : uint32_t zeal;
766 0 : if (!ToUint32(cx, args.get(0), &zeal))
767 0 : return false;
768 :
769 0 : if (zeal > uint32_t(gc::ZealMode::Limit)) {
770 0 : JS_ReportErrorASCII(cx, "gczeal argument out of range");
771 0 : return false;
772 : }
773 :
774 0 : uint32_t frequency = JS_DEFAULT_ZEAL_FREQ;
775 0 : if (args.length() >= 2) {
776 0 : if (!ToUint32(cx, args.get(1), &frequency))
777 0 : return false;
778 : }
779 :
780 0 : JS_SetGCZeal(cx, (uint8_t)zeal, frequency);
781 0 : args.rval().setUndefined();
782 0 : return true;
783 : }
784 :
785 : static bool
786 0 : ScheduleGC(JSContext* cx, unsigned argc, Value* vp)
787 : {
788 0 : CallArgs args = CallArgsFromVp(argc, vp);
789 :
790 0 : if (args.length() > 1) {
791 0 : RootedObject callee(cx, &args.callee());
792 0 : ReportUsageErrorASCII(cx, callee, "Too many arguments");
793 0 : return false;
794 : }
795 :
796 0 : if (args.length() == 0) {
797 : /* Fetch next zeal trigger only. */
798 0 : } else if (args[0].isNumber()) {
799 : /* Schedule a GC to happen after |arg| allocations. */
800 0 : JS_ScheduleGC(cx, std::max(int(args[0].toNumber()), 0));
801 0 : } else if (args[0].isObject()) {
802 : /* Ensure that |zone| is collected during the next GC. */
803 0 : Zone* zone = UncheckedUnwrap(&args[0].toObject())->zone();
804 0 : PrepareZoneForGC(zone);
805 0 : } else if (args[0].isString()) {
806 : /* This allows us to schedule the atoms zone for GC. */
807 0 : Zone* zone = args[0].toString()->zoneFromAnyThread();
808 0 : if (!CurrentThreadCanAccessZone(zone)) {
809 0 : RootedObject callee(cx, &args.callee());
810 0 : ReportUsageErrorASCII(cx, callee, "Specified zone not accessible for GC");
811 0 : return false;
812 : }
813 0 : PrepareZoneForGC(zone);
814 : } else {
815 0 : RootedObject callee(cx, &args.callee());
816 0 : ReportUsageErrorASCII(cx, callee, "Bad argument - expecting number, object or string");
817 0 : return false;
818 : }
819 :
820 : uint32_t zealBits;
821 : uint32_t freq;
822 : uint32_t next;
823 0 : JS_GetGCZealBits(cx, &zealBits, &freq, &next);
824 0 : args.rval().setInt32(next);
825 0 : return true;
826 : }
827 :
828 : static bool
829 0 : SelectForGC(JSContext* cx, unsigned argc, Value* vp)
830 : {
831 0 : CallArgs args = CallArgsFromVp(argc, vp);
832 :
833 : /*
834 : * The selectedForMarking set is intended to be manually marked at slice
835 : * start to detect missing pre-barriers. It is invalid for nursery things
836 : * to be in the set, so evict the nursery before adding items.
837 : */
838 0 : cx->runtime()->gc.evictNursery();
839 :
840 0 : for (unsigned i = 0; i < args.length(); i++) {
841 0 : if (args[i].isObject()) {
842 0 : if (!cx->runtime()->gc.selectForMarking(&args[i].toObject()))
843 0 : return false;
844 : }
845 : }
846 :
847 0 : args.rval().setUndefined();
848 0 : return true;
849 : }
850 :
851 : static bool
852 0 : VerifyPreBarriers(JSContext* cx, unsigned argc, Value* vp)
853 : {
854 0 : CallArgs args = CallArgsFromVp(argc, vp);
855 :
856 0 : if (args.length() > 0) {
857 0 : RootedObject callee(cx, &args.callee());
858 0 : ReportUsageErrorASCII(cx, callee, "Too many arguments");
859 0 : return false;
860 : }
861 :
862 0 : gc::VerifyBarriers(cx->runtime(), gc::PreBarrierVerifier);
863 0 : args.rval().setUndefined();
864 0 : return true;
865 : }
866 :
867 : static bool
868 0 : VerifyPostBarriers(JSContext* cx, unsigned argc, Value* vp)
869 : {
870 : // This is a no-op since the post barrier verifier was removed.
871 0 : CallArgs args = CallArgsFromVp(argc, vp);
872 0 : if (args.length()) {
873 0 : RootedObject callee(cx, &args.callee());
874 0 : ReportUsageErrorASCII(cx, callee, "Too many arguments");
875 0 : return false;
876 : }
877 0 : args.rval().setUndefined();
878 0 : return true;
879 : }
880 :
881 : static bool
882 0 : GCState(JSContext* cx, unsigned argc, Value* vp)
883 : {
884 0 : CallArgs args = CallArgsFromVp(argc, vp);
885 :
886 0 : if (args.length() != 0) {
887 0 : RootedObject callee(cx, &args.callee());
888 0 : ReportUsageErrorASCII(cx, callee, "Too many arguments");
889 0 : return false;
890 : }
891 :
892 0 : const char* state = StateName(cx->runtime()->gc.state());
893 0 : JSString* str = JS_NewStringCopyZ(cx, state);
894 0 : if (!str)
895 0 : return false;
896 0 : args.rval().setString(str);
897 0 : return true;
898 : }
899 :
900 : static bool
901 0 : DeterministicGC(JSContext* cx, unsigned argc, Value* vp)
902 : {
903 0 : CallArgs args = CallArgsFromVp(argc, vp);
904 :
905 0 : if (args.length() != 1) {
906 0 : RootedObject callee(cx, &args.callee());
907 0 : ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
908 0 : return false;
909 : }
910 :
911 0 : cx->runtime()->gc.setDeterministic(ToBoolean(args[0]));
912 0 : args.rval().setUndefined();
913 0 : return true;
914 : }
915 :
916 : static bool
917 0 : DumpGCArenaInfo(JSContext* cx, unsigned argc, Value* vp)
918 : {
919 0 : CallArgs args = CallArgsFromVp(argc, vp);
920 0 : js::gc::DumpArenaInfo();
921 0 : args.rval().setUndefined();
922 0 : return true;
923 : }
924 :
925 : #endif /* JS_GC_ZEAL */
926 :
927 : static bool
928 0 : StartGC(JSContext* cx, unsigned argc, Value* vp)
929 : {
930 0 : CallArgs args = CallArgsFromVp(argc, vp);
931 :
932 0 : if (args.length() > 2) {
933 0 : RootedObject callee(cx, &args.callee());
934 0 : ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
935 0 : return false;
936 : }
937 :
938 0 : auto budget = SliceBudget::unlimited();
939 0 : if (args.length() >= 1) {
940 0 : uint32_t work = 0;
941 0 : if (!ToUint32(cx, args[0], &work))
942 0 : return false;
943 0 : budget = SliceBudget(WorkBudget(work));
944 : }
945 :
946 0 : bool shrinking = false;
947 0 : if (args.length() >= 2) {
948 0 : Value arg = args[1];
949 0 : if (arg.isString()) {
950 0 : if (!JS_StringEqualsAscii(cx, arg.toString(), "shrinking", &shrinking))
951 0 : return false;
952 : }
953 : }
954 :
955 0 : JSRuntime* rt = cx->runtime();
956 0 : if (rt->gc.isIncrementalGCInProgress()) {
957 0 : RootedObject callee(cx, &args.callee());
958 0 : JS_ReportErrorASCII(cx, "Incremental GC already in progress");
959 0 : return false;
960 : }
961 :
962 0 : JSGCInvocationKind gckind = shrinking ? GC_SHRINK : GC_NORMAL;
963 0 : rt->gc.startDebugGC(gckind, budget);
964 :
965 0 : args.rval().setUndefined();
966 0 : return true;
967 : }
968 :
969 : static bool
970 0 : GCSlice(JSContext* cx, unsigned argc, Value* vp)
971 : {
972 0 : CallArgs args = CallArgsFromVp(argc, vp);
973 :
974 0 : if (args.length() > 1) {
975 0 : RootedObject callee(cx, &args.callee());
976 0 : ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
977 0 : return false;
978 : }
979 :
980 0 : auto budget = SliceBudget::unlimited();
981 0 : if (args.length() == 1) {
982 0 : uint32_t work = 0;
983 0 : if (!ToUint32(cx, args[0], &work))
984 0 : return false;
985 0 : budget = SliceBudget(WorkBudget(work));
986 : }
987 :
988 0 : JSRuntime* rt = cx->runtime();
989 0 : if (!rt->gc.isIncrementalGCInProgress())
990 0 : rt->gc.startDebugGC(GC_NORMAL, budget);
991 : else
992 0 : rt->gc.debugGCSlice(budget);
993 :
994 0 : args.rval().setUndefined();
995 0 : return true;
996 : }
997 :
998 : static bool
999 0 : AbortGC(JSContext* cx, unsigned argc, Value* vp)
1000 : {
1001 0 : CallArgs args = CallArgsFromVp(argc, vp);
1002 :
1003 0 : if (args.length() != 0) {
1004 0 : RootedObject callee(cx, &args.callee());
1005 0 : ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1006 0 : return false;
1007 : }
1008 :
1009 0 : JS::AbortIncrementalGC(cx);
1010 0 : args.rval().setUndefined();
1011 0 : return true;
1012 : }
1013 :
1014 : static bool
1015 0 : FullCompartmentChecks(JSContext* cx, unsigned argc, Value* vp)
1016 : {
1017 0 : CallArgs args = CallArgsFromVp(argc, vp);
1018 :
1019 0 : if (args.length() != 1) {
1020 0 : RootedObject callee(cx, &args.callee());
1021 0 : ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1022 0 : return false;
1023 : }
1024 :
1025 0 : cx->runtime()->gc.setFullCompartmentChecks(ToBoolean(args[0]));
1026 0 : args.rval().setUndefined();
1027 0 : return true;
1028 : }
1029 :
1030 : static bool
1031 0 : NondeterministicGetWeakMapKeys(JSContext* cx, unsigned argc, Value* vp)
1032 : {
1033 0 : CallArgs args = CallArgsFromVp(argc, vp);
1034 :
1035 0 : if (args.length() != 1) {
1036 0 : RootedObject callee(cx, &args.callee());
1037 0 : ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1038 0 : return false;
1039 : }
1040 0 : if (!args[0].isObject()) {
1041 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
1042 : "nondeterministicGetWeakMapKeys", "WeakMap",
1043 0 : InformalValueTypeName(args[0]));
1044 0 : return false;
1045 : }
1046 0 : RootedObject arr(cx);
1047 0 : RootedObject mapObj(cx, &args[0].toObject());
1048 0 : if (!JS_NondeterministicGetWeakMapKeys(cx, mapObj, &arr))
1049 0 : return false;
1050 0 : if (!arr) {
1051 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
1052 : "nondeterministicGetWeakMapKeys", "WeakMap",
1053 0 : args[0].toObject().getClass()->name);
1054 0 : return false;
1055 : }
1056 0 : args.rval().setObject(*arr);
1057 0 : return true;
1058 : }
1059 :
1060 0 : class HasChildTracer : public JS::CallbackTracer
1061 : {
1062 : RootedValue child_;
1063 : bool found_;
1064 :
1065 0 : void onChild(const JS::GCCellPtr& thing) override {
1066 0 : if (thing.asCell() == child_.toGCThing())
1067 0 : found_ = true;
1068 0 : }
1069 :
1070 : public:
1071 0 : HasChildTracer(JSContext* cx, HandleValue child)
1072 0 : : JS::CallbackTracer(cx, TraceWeakMapKeysValues), child_(cx, child), found_(false)
1073 0 : {}
1074 :
1075 0 : bool found() const { return found_; }
1076 : };
1077 :
1078 : static bool
1079 0 : HasChild(JSContext* cx, unsigned argc, Value* vp)
1080 : {
1081 0 : CallArgs args = CallArgsFromVp(argc, vp);
1082 0 : RootedValue parent(cx, args.get(0));
1083 0 : RootedValue child(cx, args.get(1));
1084 :
1085 0 : if (!parent.isGCThing() || !child.isGCThing()) {
1086 0 : args.rval().setBoolean(false);
1087 0 : return true;
1088 : }
1089 :
1090 0 : HasChildTracer trc(cx, child);
1091 0 : TraceChildren(&trc, parent.toGCThing(), parent.traceKind());
1092 0 : args.rval().setBoolean(trc.found());
1093 0 : return true;
1094 : }
1095 :
1096 : static bool
1097 0 : SetSavedStacksRNGState(JSContext* cx, unsigned argc, Value* vp)
1098 : {
1099 0 : CallArgs args = CallArgsFromVp(argc, vp);
1100 0 : if (!args.requireAtLeast(cx, "setSavedStacksRNGState", 1))
1101 0 : return false;
1102 :
1103 : int32_t seed;
1104 0 : if (!ToInt32(cx, args[0], &seed))
1105 0 : return false;
1106 :
1107 : // Either one or the other of the seed arguments must be non-zero;
1108 : // make this true no matter what value 'seed' has.
1109 0 : cx->compartment()->savedStacks().setRNGState(seed, (seed + 1) * 33);
1110 0 : return true;
1111 : }
1112 :
1113 : static bool
1114 0 : GetSavedFrameCount(JSContext* cx, unsigned argc, Value* vp)
1115 : {
1116 0 : CallArgs args = CallArgsFromVp(argc, vp);
1117 0 : args.rval().setNumber(cx->compartment()->savedStacks().count());
1118 0 : return true;
1119 : }
1120 :
1121 : static bool
1122 0 : SaveStack(JSContext* cx, unsigned argc, Value* vp)
1123 : {
1124 0 : CallArgs args = CallArgsFromVp(argc, vp);
1125 :
1126 0 : JS::StackCapture capture((JS::AllFrames()));
1127 0 : if (args.length() >= 1) {
1128 : double maxDouble;
1129 0 : if (!ToNumber(cx, args[0], &maxDouble))
1130 0 : return false;
1131 0 : if (mozilla::IsNaN(maxDouble) || maxDouble < 0 || maxDouble > UINT32_MAX) {
1132 0 : ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
1133 : JSDVG_SEARCH_STACK, args[0], nullptr,
1134 0 : "not a valid maximum frame count", NULL);
1135 0 : return false;
1136 : }
1137 0 : uint32_t max = uint32_t(maxDouble);
1138 0 : if (max > 0)
1139 0 : capture = JS::StackCapture(JS::MaxFrames(max));
1140 : }
1141 :
1142 0 : RootedObject compartmentObject(cx);
1143 0 : if (args.length() >= 2) {
1144 0 : if (!args[1].isObject()) {
1145 0 : ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
1146 : JSDVG_SEARCH_STACK, args[0], nullptr,
1147 0 : "not an object", NULL);
1148 0 : return false;
1149 : }
1150 0 : compartmentObject = UncheckedUnwrap(&args[1].toObject());
1151 0 : if (!compartmentObject)
1152 0 : return false;
1153 : }
1154 :
1155 0 : RootedObject stack(cx);
1156 : {
1157 0 : Maybe<AutoCompartment> ac;
1158 0 : if (compartmentObject)
1159 0 : ac.emplace(cx, compartmentObject);
1160 0 : if (!JS::CaptureCurrentStack(cx, &stack, mozilla::Move(capture)))
1161 0 : return false;
1162 : }
1163 :
1164 0 : if (stack && !cx->compartment()->wrap(cx, &stack))
1165 0 : return false;
1166 :
1167 0 : args.rval().setObjectOrNull(stack);
1168 0 : return true;
1169 : }
1170 :
1171 : static bool
1172 0 : CaptureFirstSubsumedFrame(JSContext* cx, unsigned argc, JS::Value* vp)
1173 : {
1174 0 : CallArgs args = CallArgsFromVp(argc, vp);
1175 0 : if (!args.requireAtLeast(cx, "captureFirstSubsumedFrame", 1))
1176 0 : return false;
1177 :
1178 0 : if (!args[0].isObject()) {
1179 0 : JS_ReportErrorASCII(cx, "The argument must be an object");
1180 0 : return false;
1181 : }
1182 :
1183 0 : RootedObject obj(cx, &args[0].toObject());
1184 0 : obj = CheckedUnwrap(obj);
1185 0 : if (!obj) {
1186 0 : JS_ReportErrorASCII(cx, "Denied permission to object.");
1187 0 : return false;
1188 : }
1189 :
1190 0 : JS::StackCapture capture(JS::FirstSubsumedFrame(cx, obj->compartment()->principals()));
1191 0 : if (args.length() > 1)
1192 0 : capture.as<JS::FirstSubsumedFrame>().ignoreSelfHosted = JS::ToBoolean(args[1]);
1193 :
1194 0 : JS::RootedObject capturedStack(cx);
1195 0 : if (!JS::CaptureCurrentStack(cx, &capturedStack, mozilla::Move(capture)))
1196 0 : return false;
1197 :
1198 0 : args.rval().setObjectOrNull(capturedStack);
1199 0 : return true;
1200 : }
1201 :
1202 : static bool
1203 0 : CallFunctionFromNativeFrame(JSContext* cx, unsigned argc, Value* vp)
1204 : {
1205 0 : CallArgs args = CallArgsFromVp(argc, vp);
1206 :
1207 0 : if (args.length() != 1) {
1208 0 : JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
1209 0 : return false;
1210 : }
1211 0 : if (!args[0].isObject() || !IsCallable(args[0])) {
1212 0 : JS_ReportErrorASCII(cx, "The first argument should be a function.");
1213 0 : return false;
1214 : }
1215 :
1216 0 : RootedObject function(cx, &args[0].toObject());
1217 0 : return Call(cx, UndefinedHandleValue, function,
1218 0 : JS::HandleValueArray::empty(), args.rval());
1219 : }
1220 :
1221 : static bool
1222 0 : CallFunctionWithAsyncStack(JSContext* cx, unsigned argc, Value* vp)
1223 : {
1224 0 : CallArgs args = CallArgsFromVp(argc, vp);
1225 :
1226 0 : if (args.length() != 3) {
1227 0 : JS_ReportErrorASCII(cx, "The function takes exactly three arguments.");
1228 0 : return false;
1229 : }
1230 0 : if (!args[0].isObject() || !IsCallable(args[0])) {
1231 0 : JS_ReportErrorASCII(cx, "The first argument should be a function.");
1232 0 : return false;
1233 : }
1234 0 : if (!args[1].isObject() || !args[1].toObject().is<SavedFrame>()) {
1235 0 : JS_ReportErrorASCII(cx, "The second argument should be a SavedFrame.");
1236 0 : return false;
1237 : }
1238 0 : if (!args[2].isString() || args[2].toString()->empty()) {
1239 0 : JS_ReportErrorASCII(cx, "The third argument should be a non-empty string.");
1240 0 : return false;
1241 : }
1242 :
1243 0 : RootedObject function(cx, &args[0].toObject());
1244 0 : RootedObject stack(cx, &args[1].toObject());
1245 0 : RootedString asyncCause(cx, args[2].toString());
1246 0 : JSAutoByteString utf8Cause;
1247 0 : if (!utf8Cause.encodeUtf8(cx, asyncCause)) {
1248 0 : MOZ_ASSERT(cx->isExceptionPending());
1249 0 : return false;
1250 : }
1251 :
1252 0 : JS::AutoSetAsyncStackForNewCalls sas(cx, stack, utf8Cause.ptr(),
1253 0 : JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT);
1254 0 : return Call(cx, UndefinedHandleValue, function,
1255 0 : JS::HandleValueArray::empty(), args.rval());
1256 : }
1257 :
1258 : static bool
1259 0 : EnableTrackAllocations(JSContext* cx, unsigned argc, Value* vp)
1260 : {
1261 0 : SetAllocationMetadataBuilder(cx, &SavedStacks::metadataBuilder);
1262 0 : return true;
1263 : }
1264 :
1265 : static bool
1266 0 : DisableTrackAllocations(JSContext* cx, unsigned argc, Value* vp)
1267 : {
1268 0 : SetAllocationMetadataBuilder(cx, nullptr);
1269 0 : return true;
1270 : }
1271 :
1272 : static void
1273 : FinalizeExternalString(const JSStringFinalizer* fin, char16_t* chars);
1274 :
1275 : static const JSStringFinalizer ExternalStringFinalizer =
1276 : { FinalizeExternalString };
1277 :
1278 : static void
1279 0 : FinalizeExternalString(const JSStringFinalizer* fin, char16_t* chars)
1280 : {
1281 0 : MOZ_ASSERT(fin == &ExternalStringFinalizer);
1282 0 : js_free(chars);
1283 0 : }
1284 :
1285 : static bool
1286 0 : NewExternalString(JSContext* cx, unsigned argc, Value* vp)
1287 : {
1288 0 : CallArgs args = CallArgsFromVp(argc, vp);
1289 :
1290 0 : if (args.length() != 1 || !args[0].isString()) {
1291 0 : JS_ReportErrorASCII(cx, "newExternalString takes exactly one string argument.");
1292 0 : return false;
1293 : }
1294 :
1295 0 : RootedString str(cx, args[0].toString());
1296 0 : size_t len = str->length();
1297 :
1298 0 : UniqueTwoByteChars buf(cx->pod_malloc<char16_t>(len));
1299 0 : if (!buf)
1300 0 : return false;
1301 :
1302 0 : if (!JS_CopyStringChars(cx, mozilla::Range<char16_t>(buf.get(), len), str))
1303 0 : return false;
1304 :
1305 0 : JSString* res = JS_NewExternalString(cx, buf.get(), len, &ExternalStringFinalizer);
1306 0 : if (!res)
1307 0 : return false;
1308 :
1309 0 : mozilla::Unused << buf.release();
1310 0 : args.rval().setString(res);
1311 0 : return true;
1312 : }
1313 :
1314 : static bool
1315 0 : NewMaybeExternalString(JSContext* cx, unsigned argc, Value* vp)
1316 : {
1317 0 : CallArgs args = CallArgsFromVp(argc, vp);
1318 :
1319 0 : if (args.length() != 1 || !args[0].isString()) {
1320 0 : JS_ReportErrorASCII(cx, "newMaybeExternalString takes exactly one string argument.");
1321 0 : return false;
1322 : }
1323 :
1324 0 : RootedString str(cx, args[0].toString());
1325 0 : size_t len = str->length();
1326 :
1327 0 : UniqueTwoByteChars buf(cx->pod_malloc<char16_t>(len));
1328 0 : if (!buf)
1329 0 : return false;
1330 :
1331 0 : if (!JS_CopyStringChars(cx, mozilla::Range<char16_t>(buf.get(), len), str))
1332 0 : return false;
1333 :
1334 : bool allocatedExternal;
1335 0 : JSString* res = JS_NewMaybeExternalString(cx, buf.get(), len, &ExternalStringFinalizer,
1336 0 : &allocatedExternal);
1337 0 : if (!res)
1338 0 : return false;
1339 :
1340 0 : mozilla::Unused << buf.release();
1341 0 : args.rval().setString(res);
1342 0 : return true;
1343 : }
1344 :
1345 : static bool
1346 0 : EnsureFlatString(JSContext* cx, unsigned argc, Value* vp)
1347 : {
1348 0 : CallArgs args = CallArgsFromVp(argc, vp);
1349 :
1350 0 : if (args.length() != 1 || !args[0].isString()) {
1351 0 : JS_ReportErrorASCII(cx, "ensureFlatString takes exactly one string argument.");
1352 0 : return false;
1353 : }
1354 :
1355 0 : JSFlatString* flat = args[0].toString()->ensureFlat(cx);
1356 0 : if (!flat)
1357 0 : return false;
1358 :
1359 0 : args.rval().setString(flat);
1360 0 : return true;
1361 : }
1362 :
1363 : static bool
1364 0 : RepresentativeStringArray(JSContext* cx, unsigned argc, Value* vp)
1365 : {
1366 0 : CallArgs args = CallArgsFromVp(argc, vp);
1367 :
1368 0 : RootedObject array(cx, JS_NewArrayObject(cx, 0));
1369 0 : if (!array)
1370 0 : return false;
1371 :
1372 0 : if (!JSString::fillWithRepresentatives(cx, array.as<ArrayObject>()))
1373 0 : return false;
1374 :
1375 0 : args.rval().setObject(*array);
1376 0 : return true;
1377 : }
1378 :
1379 : #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
1380 : static bool
1381 0 : OOMThreadTypes(JSContext* cx, unsigned argc, Value* vp)
1382 : {
1383 0 : CallArgs args = CallArgsFromVp(argc, vp);
1384 0 : args.rval().setInt32(js::oom::THREAD_TYPE_MAX);
1385 0 : return true;
1386 : }
1387 :
1388 : static bool
1389 0 : SetupOOMFailure(JSContext* cx, bool failAlways, unsigned argc, Value* vp)
1390 : {
1391 0 : CallArgs args = CallArgsFromVp(argc, vp);
1392 :
1393 0 : if (disableOOMFunctions) {
1394 0 : args.rval().setUndefined();
1395 0 : return true;
1396 : }
1397 :
1398 0 : if (args.length() < 1) {
1399 0 : JS_ReportErrorASCII(cx, "Count argument required");
1400 0 : return false;
1401 : }
1402 :
1403 0 : if (args.length() > 2) {
1404 0 : JS_ReportErrorASCII(cx, "Too many arguments");
1405 0 : return false;
1406 : }
1407 :
1408 : int32_t count;
1409 0 : if (!JS::ToInt32(cx, args.get(0), &count))
1410 0 : return false;
1411 :
1412 0 : if (count <= 0) {
1413 0 : JS_ReportErrorASCII(cx, "OOM cutoff should be positive");
1414 0 : return false;
1415 : }
1416 :
1417 0 : uint32_t targetThread = js::oom::THREAD_TYPE_COOPERATING;
1418 0 : if (args.length() > 1 && !ToUint32(cx, args[1], &targetThread))
1419 0 : return false;
1420 :
1421 0 : if (targetThread == js::oom::THREAD_TYPE_NONE || targetThread >= js::oom::THREAD_TYPE_MAX) {
1422 0 : JS_ReportErrorASCII(cx, "Invalid thread type specified");
1423 0 : return false;
1424 : }
1425 :
1426 0 : HelperThreadState().waitForAllThreads();
1427 0 : js::oom::SimulateOOMAfter(count, targetThread, failAlways);
1428 0 : args.rval().setUndefined();
1429 0 : return true;
1430 : }
1431 :
1432 : static bool
1433 0 : OOMAfterAllocations(JSContext* cx, unsigned argc, Value* vp)
1434 : {
1435 0 : return SetupOOMFailure(cx, true, argc, vp);
1436 : }
1437 :
1438 : static bool
1439 0 : OOMAtAllocation(JSContext* cx, unsigned argc, Value* vp)
1440 : {
1441 0 : return SetupOOMFailure(cx, false, argc, vp);
1442 : }
1443 :
1444 : static bool
1445 0 : ResetOOMFailure(JSContext* cx, unsigned argc, Value* vp)
1446 : {
1447 0 : CallArgs args = CallArgsFromVp(argc, vp);
1448 0 : args.rval().setBoolean(js::oom::HadSimulatedOOM());
1449 0 : js::oom::ResetSimulatedOOM();
1450 0 : return true;
1451 : }
1452 :
1453 : static size_t
1454 0 : CountCompartments(JSContext* cx)
1455 : {
1456 0 : size_t count = 0;
1457 0 : ZoneGroup* group = cx->compartment()->zone()->group();
1458 0 : for (auto zone : group->zones())
1459 0 : count += zone->compartments().length();
1460 0 : return count;
1461 : }
1462 :
1463 : static bool
1464 0 : OOMTest(JSContext* cx, unsigned argc, Value* vp)
1465 : {
1466 0 : CallArgs args = CallArgsFromVp(argc, vp);
1467 :
1468 0 : if (args.length() < 1 || args.length() > 2) {
1469 0 : JS_ReportErrorASCII(cx, "oomTest() takes between 1 and 2 arguments.");
1470 0 : return false;
1471 : }
1472 :
1473 0 : if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
1474 0 : JS_ReportErrorASCII(cx, "The first argument to oomTest() must be a function.");
1475 0 : return false;
1476 : }
1477 :
1478 0 : if (args.length() == 2 && !args[1].isBoolean()) {
1479 0 : JS_ReportErrorASCII(cx, "The optional second argument to oomTest() must be a boolean.");
1480 0 : return false;
1481 : }
1482 :
1483 0 : bool expectExceptionOnFailure = true;
1484 0 : if (args.length() == 2)
1485 0 : expectExceptionOnFailure = args[1].toBoolean();
1486 :
1487 : // There are some places where we do fail without raising an exception, so
1488 : // we can't expose this to the fuzzers by default.
1489 0 : if (fuzzingSafe)
1490 0 : expectExceptionOnFailure = false;
1491 :
1492 0 : if (disableOOMFunctions) {
1493 0 : args.rval().setUndefined();
1494 0 : return true;
1495 : }
1496 :
1497 0 : RootedFunction function(cx, &args[0].toObject().as<JSFunction>());
1498 :
1499 0 : bool verbose = EnvVarIsDefined("OOM_VERBOSE");
1500 :
1501 0 : unsigned threadStart = oom::THREAD_TYPE_COOPERATING;
1502 0 : unsigned threadEnd = oom::THREAD_TYPE_MAX;
1503 :
1504 : // Test a single thread type if specified by the OOM_THREAD environment variable.
1505 0 : int threadOption = 0;
1506 0 : if (EnvVarAsInt("OOM_THREAD", &threadOption)) {
1507 0 : if (threadOption < oom::THREAD_TYPE_COOPERATING || threadOption > oom::THREAD_TYPE_MAX) {
1508 0 : JS_ReportErrorASCII(cx, "OOM_THREAD value out of range.");
1509 0 : return false;
1510 : }
1511 :
1512 0 : threadStart = threadOption;
1513 0 : threadEnd = threadOption + 1;
1514 : }
1515 :
1516 0 : if (cx->runningOOMTest) {
1517 0 : JS_ReportErrorASCII(cx, "Nested call to oomTest() is not allowed.");
1518 0 : return false;
1519 : }
1520 0 : cx->runningOOMTest = true;
1521 :
1522 0 : MOZ_ASSERT(!cx->isExceptionPending());
1523 0 : cx->runtime()->hadOutOfMemory = false;
1524 :
1525 0 : size_t compartmentCount = CountCompartments(cx);
1526 :
1527 0 : JS_SetGCZeal(cx, 0, JS_DEFAULT_ZEAL_FREQ);
1528 :
1529 0 : for (unsigned thread = threadStart; thread < threadEnd; thread++) {
1530 0 : if (verbose)
1531 0 : fprintf(stderr, "thread %d\n", thread);
1532 :
1533 0 : HelperThreadState().waitForAllThreads();
1534 0 : js::oom::targetThread = thread;
1535 :
1536 0 : unsigned allocation = 1;
1537 : bool handledOOM;
1538 0 : do {
1539 0 : if (verbose)
1540 0 : fprintf(stderr, " allocation %d\n", allocation);
1541 :
1542 0 : MOZ_ASSERT(!cx->isExceptionPending());
1543 0 : MOZ_ASSERT(!cx->runtime()->hadOutOfMemory);
1544 :
1545 0 : js::oom::SimulateOOMAfter(allocation, thread, false);
1546 :
1547 0 : RootedValue result(cx);
1548 0 : bool ok = JS_CallFunction(cx, cx->global(), function,
1549 0 : HandleValueArray::empty(), &result);
1550 :
1551 0 : handledOOM = js::oom::HadSimulatedOOM();
1552 0 : js::oom::ResetSimulatedOOM();
1553 :
1554 0 : MOZ_ASSERT_IF(ok, !cx->isExceptionPending());
1555 :
1556 0 : if (ok) {
1557 0 : MOZ_ASSERT(!cx->isExceptionPending(),
1558 : "Thunk execution succeeded but an exception was raised - "
1559 : "missing error check?");
1560 0 : } else if (expectExceptionOnFailure) {
1561 0 : MOZ_ASSERT(cx->isExceptionPending(),
1562 : "Thunk execution failed but no exception was raised - "
1563 : "missing call to js::ReportOutOfMemory()?");
1564 : }
1565 :
1566 : // Note that it is possible that the function throws an exception
1567 : // unconnected to OOM, in which case we ignore it. More correct
1568 : // would be to have the caller pass some kind of exception
1569 : // specification and to check the exception against it.
1570 :
1571 0 : cx->clearPendingException();
1572 0 : cx->runtime()->hadOutOfMemory = false;
1573 :
1574 : // Some tests create a new compartment or zone on every
1575 : // iteration. Our GC is triggered by GC allocations and not by
1576 : // number of copmartments or zones, so these won't normally get
1577 : // cleaned up. The check here stops some tests running out of
1578 : // memory.
1579 0 : if (CountCompartments(cx) > compartmentCount + 100) {
1580 0 : JS_GC(cx);
1581 0 : compartmentCount = CountCompartments(cx);
1582 : }
1583 :
1584 : #ifdef JS_TRACE_LOGGING
1585 : // Reset the TraceLogger state if enabled.
1586 0 : TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
1587 0 : if (logger->enabled()) {
1588 0 : while (logger->enabled())
1589 0 : logger->disable();
1590 0 : logger->enable(cx);
1591 : }
1592 : #endif
1593 :
1594 0 : allocation++;
1595 : } while (handledOOM);
1596 :
1597 0 : if (verbose) {
1598 0 : fprintf(stderr, " finished after %d allocations\n", allocation - 2);
1599 : }
1600 : }
1601 :
1602 0 : cx->runningOOMTest = false;
1603 0 : args.rval().setUndefined();
1604 0 : return true;
1605 : }
1606 : #endif
1607 :
1608 : static bool
1609 0 : SettlePromiseNow(JSContext* cx, unsigned argc, Value* vp)
1610 : {
1611 0 : CallArgs args = CallArgsFromVp(argc, vp);
1612 0 : if (!args.requireAtLeast(cx, "settlePromiseNow", 1))
1613 0 : return false;
1614 0 : if (!args[0].isObject() || !args[0].toObject().is<PromiseObject>()) {
1615 0 : JS_ReportErrorASCII(cx, "first argument must be a Promise object");
1616 0 : return false;
1617 : }
1618 :
1619 0 : RootedNativeObject promise(cx, &args[0].toObject().as<NativeObject>());
1620 0 : int32_t flags = promise->getFixedSlot(PromiseSlot_Flags).toInt32();
1621 0 : promise->setFixedSlot(PromiseSlot_Flags,
1622 0 : Int32Value(flags | PROMISE_FLAG_RESOLVED | PROMISE_FLAG_FULFILLED));
1623 0 : promise->setFixedSlot(PromiseSlot_ReactionsOrResult, UndefinedValue());
1624 :
1625 0 : JS::dbg::onPromiseSettled(cx, promise);
1626 0 : return true;
1627 : }
1628 :
1629 : static bool
1630 0 : GetWaitForAllPromise(JSContext* cx, unsigned argc, Value* vp)
1631 : {
1632 0 : CallArgs args = CallArgsFromVp(argc, vp);
1633 0 : if (!args.requireAtLeast(cx, "getWaitForAllPromise", 1))
1634 0 : return false;
1635 0 : if (!args[0].isObject() || !IsPackedArray(&args[0].toObject())) {
1636 0 : JS_ReportErrorASCII(cx, "first argument must be a dense Array of Promise objects");
1637 0 : return false;
1638 : }
1639 0 : RootedNativeObject list(cx, &args[0].toObject().as<NativeObject>());
1640 0 : AutoObjectVector promises(cx);
1641 0 : uint32_t count = list->getDenseInitializedLength();
1642 0 : if (!promises.resize(count))
1643 0 : return false;
1644 :
1645 0 : for (uint32_t i = 0; i < count; i++) {
1646 0 : RootedValue elem(cx, list->getDenseElement(i));
1647 0 : if (!elem.isObject() || !elem.toObject().is<PromiseObject>()) {
1648 0 : JS_ReportErrorASCII(cx, "Each entry in the passed-in Array must be a Promise");
1649 0 : return false;
1650 : }
1651 0 : promises[i].set(&elem.toObject());
1652 : }
1653 :
1654 0 : RootedObject resultPromise(cx, JS::GetWaitForAllPromise(cx, promises));
1655 0 : if (!resultPromise)
1656 0 : return false;
1657 :
1658 0 : args.rval().set(ObjectValue(*resultPromise));
1659 0 : return true;
1660 : }
1661 :
1662 : static bool
1663 0 : ResolvePromise(JSContext* cx, unsigned argc, Value* vp)
1664 : {
1665 0 : CallArgs args = CallArgsFromVp(argc, vp);
1666 0 : if (!args.requireAtLeast(cx, "resolvePromise", 2))
1667 0 : return false;
1668 0 : if (!args[0].isObject() || !UncheckedUnwrap(&args[0].toObject())->is<PromiseObject>()) {
1669 0 : JS_ReportErrorASCII(cx, "first argument must be a maybe-wrapped Promise object");
1670 0 : return false;
1671 : }
1672 :
1673 0 : RootedObject promise(cx, &args[0].toObject());
1674 0 : RootedValue resolution(cx, args[1]);
1675 0 : mozilla::Maybe<AutoCompartment> ac;
1676 0 : if (IsWrapper(promise)) {
1677 0 : promise = UncheckedUnwrap(promise);
1678 0 : ac.emplace(cx, promise);
1679 0 : if (!cx->compartment()->wrap(cx, &resolution))
1680 0 : return false;
1681 : }
1682 :
1683 0 : bool result = JS::ResolvePromise(cx, promise, resolution);
1684 0 : if (result)
1685 0 : args.rval().setUndefined();
1686 0 : return result;
1687 : }
1688 :
1689 : static bool
1690 0 : RejectPromise(JSContext* cx, unsigned argc, Value* vp)
1691 : {
1692 0 : CallArgs args = CallArgsFromVp(argc, vp);
1693 0 : if (!args.requireAtLeast(cx, "rejectPromise", 2))
1694 0 : return false;
1695 0 : if (!args[0].isObject() || !UncheckedUnwrap(&args[0].toObject())->is<PromiseObject>()) {
1696 0 : JS_ReportErrorASCII(cx, "first argument must be a maybe-wrapped Promise object");
1697 0 : return false;
1698 : }
1699 :
1700 0 : RootedObject promise(cx, &args[0].toObject());
1701 0 : RootedValue reason(cx, args[1]);
1702 0 : mozilla::Maybe<AutoCompartment> ac;
1703 0 : if (IsWrapper(promise)) {
1704 0 : promise = UncheckedUnwrap(promise);
1705 0 : ac.emplace(cx, promise);
1706 0 : if (!cx->compartment()->wrap(cx, &reason))
1707 0 : return false;
1708 : }
1709 :
1710 0 : bool result = JS::RejectPromise(cx, promise, reason);
1711 0 : if (result)
1712 0 : args.rval().setUndefined();
1713 0 : return result;
1714 : }
1715 :
1716 : static unsigned finalizeCount = 0;
1717 :
1718 : static void
1719 0 : finalize_counter_finalize(JSFreeOp* fop, JSObject* obj)
1720 : {
1721 0 : ++finalizeCount;
1722 0 : }
1723 :
1724 : static const JSClassOps FinalizeCounterClassOps = {
1725 : nullptr, /* addProperty */
1726 : nullptr, /* delProperty */
1727 : nullptr, /* getProperty */
1728 : nullptr, /* setProperty */
1729 : nullptr, /* enumerate */
1730 : nullptr, /* newEnumerate */
1731 : nullptr, /* resolve */
1732 : nullptr, /* mayResolve */
1733 : finalize_counter_finalize
1734 : };
1735 :
1736 : static const JSClass FinalizeCounterClass = {
1737 : "FinalizeCounter",
1738 : JSCLASS_IS_ANONYMOUS |
1739 : JSCLASS_FOREGROUND_FINALIZE,
1740 : &FinalizeCounterClassOps
1741 : };
1742 :
1743 : static bool
1744 0 : MakeFinalizeObserver(JSContext* cx, unsigned argc, Value* vp)
1745 : {
1746 0 : CallArgs args = CallArgsFromVp(argc, vp);
1747 :
1748 0 : JSObject* obj = JS_NewObjectWithGivenProto(cx, &FinalizeCounterClass, nullptr);
1749 0 : if (!obj)
1750 0 : return false;
1751 :
1752 0 : args.rval().setObject(*obj);
1753 0 : return true;
1754 : }
1755 :
1756 : static bool
1757 0 : FinalizeCount(JSContext* cx, unsigned argc, Value* vp)
1758 : {
1759 0 : CallArgs args = CallArgsFromVp(argc, vp);
1760 0 : args.rval().setInt32(finalizeCount);
1761 0 : return true;
1762 : }
1763 :
1764 : static bool
1765 0 : ResetFinalizeCount(JSContext* cx, unsigned argc, Value* vp)
1766 : {
1767 0 : CallArgs args = CallArgsFromVp(argc, vp);
1768 0 : finalizeCount = 0;
1769 0 : args.rval().setUndefined();
1770 0 : return true;
1771 : }
1772 :
1773 : static bool
1774 0 : DumpHeap(JSContext* cx, unsigned argc, Value* vp)
1775 : {
1776 0 : CallArgs args = CallArgsFromVp(argc, vp);
1777 :
1778 0 : DumpHeapNurseryBehaviour nurseryBehaviour = js::IgnoreNurseryObjects;
1779 0 : FILE* dumpFile = nullptr;
1780 :
1781 0 : unsigned i = 0;
1782 0 : if (args.length() > i) {
1783 0 : Value v = args[i];
1784 0 : if (v.isString()) {
1785 0 : JSString* str = v.toString();
1786 0 : bool same = false;
1787 0 : if (!JS_StringEqualsAscii(cx, str, "collectNurseryBeforeDump", &same))
1788 0 : return false;
1789 0 : if (same) {
1790 0 : nurseryBehaviour = js::CollectNurseryBeforeDump;
1791 0 : ++i;
1792 : }
1793 : }
1794 : }
1795 :
1796 0 : if (args.length() > i) {
1797 0 : Value v = args[i];
1798 0 : if (v.isString()) {
1799 0 : if (!fuzzingSafe) {
1800 0 : RootedString str(cx, v.toString());
1801 0 : JSAutoByteString fileNameBytes;
1802 0 : if (!fileNameBytes.encodeLatin1(cx, str))
1803 0 : return false;
1804 0 : const char* fileName = fileNameBytes.ptr();
1805 0 : dumpFile = fopen(fileName, "w");
1806 0 : if (!dumpFile) {
1807 0 : fileNameBytes.clear();
1808 0 : if (!fileNameBytes.encodeUtf8(cx, str))
1809 0 : return false;
1810 0 : JS_ReportErrorUTF8(cx, "can't open %s", fileNameBytes.ptr());
1811 0 : return false;
1812 : }
1813 : }
1814 0 : ++i;
1815 : }
1816 : }
1817 :
1818 0 : if (i != args.length()) {
1819 0 : JS_ReportErrorASCII(cx, "bad arguments passed to dumpHeap");
1820 0 : if (dumpFile)
1821 0 : fclose(dumpFile);
1822 0 : return false;
1823 : }
1824 :
1825 0 : js::DumpHeap(cx, dumpFile ? dumpFile : stdout, nurseryBehaviour);
1826 :
1827 0 : if (dumpFile)
1828 0 : fclose(dumpFile);
1829 :
1830 0 : args.rval().setUndefined();
1831 0 : return true;
1832 : }
1833 :
1834 : static bool
1835 0 : Terminate(JSContext* cx, unsigned arg, Value* vp)
1836 : {
1837 : #ifdef JS_MORE_DETERMINISTIC
1838 : // Print a message to stderr in more-deterministic builds to help jsfunfuzz
1839 : // find uncatchable-exception bugs.
1840 : fprintf(stderr, "terminate called\n");
1841 : #endif
1842 :
1843 0 : JS_ClearPendingException(cx);
1844 0 : return false;
1845 : }
1846 :
1847 : static bool
1848 0 : ReadGeckoProfilingStack(JSContext* cx, unsigned argc, Value* vp)
1849 : {
1850 0 : CallArgs args = CallArgsFromVp(argc, vp);
1851 0 : args.rval().setUndefined();
1852 :
1853 : // Return boolean 'false' if profiler is not enabled.
1854 0 : if (!cx->runtime()->geckoProfiler().enabled()) {
1855 0 : args.rval().setBoolean(false);
1856 0 : return true;
1857 : }
1858 :
1859 : // Array holding physical jit stack frames.
1860 0 : RootedObject stack(cx, NewDenseEmptyArray(cx));
1861 0 : if (!stack)
1862 0 : return false;
1863 :
1864 : // If profiler sampling has been suppressed, return an empty
1865 : // stack.
1866 0 : if (!cx->isProfilerSamplingEnabled()) {
1867 0 : args.rval().setObject(*stack);
1868 0 : return true;
1869 : }
1870 :
1871 0 : struct InlineFrameInfo
1872 : {
1873 0 : InlineFrameInfo(const char* kind, char* label)
1874 0 : : kind(kind), label(label) {}
1875 : const char* kind;
1876 : UniqueChars label;
1877 : };
1878 :
1879 0 : Vector<Vector<InlineFrameInfo, 0, TempAllocPolicy>, 0, TempAllocPolicy> frameInfo(cx);
1880 :
1881 0 : JS::ProfilingFrameIterator::RegisterState state;
1882 0 : for (JS::ProfilingFrameIterator i(cx, state); !i.done(); ++i) {
1883 0 : MOZ_ASSERT(i.stackAddress() != nullptr);
1884 :
1885 0 : if (!frameInfo.emplaceBack(cx))
1886 0 : return false;
1887 :
1888 0 : const size_t MaxInlineFrames = 16;
1889 : JS::ProfilingFrameIterator::Frame frames[MaxInlineFrames];
1890 0 : uint32_t nframes = i.extractStack(frames, 0, MaxInlineFrames);
1891 0 : MOZ_ASSERT(nframes <= MaxInlineFrames);
1892 0 : for (uint32_t i = 0; i < nframes; i++) {
1893 0 : const char* frameKindStr = nullptr;
1894 0 : switch (frames[i].kind) {
1895 : case JS::ProfilingFrameIterator::Frame_Baseline:
1896 0 : frameKindStr = "baseline";
1897 0 : break;
1898 : case JS::ProfilingFrameIterator::Frame_Ion:
1899 0 : frameKindStr = "ion";
1900 0 : break;
1901 : case JS::ProfilingFrameIterator::Frame_Wasm:
1902 0 : frameKindStr = "wasm";
1903 0 : break;
1904 : default:
1905 0 : frameKindStr = "unknown";
1906 : }
1907 :
1908 0 : char* label = JS_strdup(cx, frames[i].label);
1909 0 : if (!label)
1910 0 : return false;
1911 :
1912 0 : if (!frameInfo.back().emplaceBack(frameKindStr, label))
1913 0 : return false;
1914 : }
1915 : }
1916 :
1917 0 : RootedObject inlineFrameInfo(cx);
1918 0 : RootedString frameKind(cx);
1919 0 : RootedString frameLabel(cx);
1920 0 : RootedId idx(cx);
1921 :
1922 0 : const unsigned propAttrs = JSPROP_ENUMERATE;
1923 :
1924 0 : uint32_t physicalFrameNo = 0;
1925 0 : for (auto& frame : frameInfo) {
1926 : // Array holding all inline frames in a single physical jit stack frame.
1927 0 : RootedObject inlineStack(cx, NewDenseEmptyArray(cx));
1928 0 : if (!inlineStack)
1929 0 : return false;
1930 :
1931 0 : uint32_t inlineFrameNo = 0;
1932 0 : for (auto& inlineFrame : frame) {
1933 : // Object holding frame info.
1934 0 : RootedObject inlineFrameInfo(cx, NewBuiltinClassInstance<PlainObject>(cx));
1935 0 : if (!inlineFrameInfo)
1936 0 : return false;
1937 :
1938 0 : frameKind = NewStringCopyZ<CanGC>(cx, inlineFrame.kind);
1939 0 : if (!frameKind)
1940 0 : return false;
1941 :
1942 0 : if (!JS_DefineProperty(cx, inlineFrameInfo, "kind", frameKind, propAttrs))
1943 0 : return false;
1944 :
1945 0 : size_t length = strlen(inlineFrame.label.get());
1946 0 : auto label = reinterpret_cast<Latin1Char*>(inlineFrame.label.release());
1947 0 : frameLabel = NewString<CanGC>(cx, label, length);
1948 0 : if (!frameLabel)
1949 0 : return false;
1950 :
1951 0 : if (!JS_DefineProperty(cx, inlineFrameInfo, "label", frameLabel, propAttrs))
1952 0 : return false;
1953 :
1954 0 : idx = INT_TO_JSID(inlineFrameNo);
1955 0 : if (!JS_DefinePropertyById(cx, inlineStack, idx, inlineFrameInfo, 0))
1956 0 : return false;
1957 :
1958 0 : ++inlineFrameNo;
1959 : }
1960 :
1961 : // Push inline array into main array.
1962 0 : idx = INT_TO_JSID(physicalFrameNo);
1963 0 : if (!JS_DefinePropertyById(cx, stack, idx, inlineStack, 0))
1964 0 : return false;
1965 :
1966 0 : ++physicalFrameNo;
1967 : }
1968 :
1969 0 : args.rval().setObject(*stack);
1970 0 : return true;
1971 : }
1972 :
1973 : static bool
1974 0 : EnableOsiPointRegisterChecks(JSContext*, unsigned argc, Value* vp)
1975 : {
1976 0 : CallArgs args = CallArgsFromVp(argc, vp);
1977 : #ifdef CHECK_OSIPOINT_REGISTERS
1978 0 : jit::JitOptions.checkOsiPointRegisters = true;
1979 : #endif
1980 0 : args.rval().setUndefined();
1981 0 : return true;
1982 : }
1983 :
1984 : static bool
1985 0 : DisplayName(JSContext* cx, unsigned argc, Value* vp)
1986 : {
1987 0 : CallArgs args = CallArgsFromVp(argc, vp);
1988 0 : if (!args.get(0).isObject() || !args[0].toObject().is<JSFunction>()) {
1989 0 : RootedObject arg(cx, &args.callee());
1990 0 : ReportUsageErrorASCII(cx, arg, "Must have one function argument");
1991 0 : return false;
1992 : }
1993 :
1994 0 : JSFunction* fun = &args[0].toObject().as<JSFunction>();
1995 0 : JSString* str = fun->displayAtom();
1996 0 : args.rval().setString(str ? str : cx->runtime()->emptyString.ref());
1997 0 : return true;
1998 : }
1999 :
2000 : class ShellAllocationMetadataBuilder : public AllocationMetadataBuilder {
2001 : public:
2002 3 : ShellAllocationMetadataBuilder() : AllocationMetadataBuilder() { }
2003 :
2004 : virtual JSObject* build(JSContext *cx, HandleObject,
2005 : AutoEnterOOMUnsafeRegion& oomUnsafe) const override;
2006 :
2007 : static const ShellAllocationMetadataBuilder metadataBuilder;
2008 : };
2009 :
2010 : JSObject*
2011 0 : ShellAllocationMetadataBuilder::build(JSContext* cx, HandleObject,
2012 : AutoEnterOOMUnsafeRegion& oomUnsafe) const
2013 : {
2014 0 : RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
2015 0 : if (!obj)
2016 0 : oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
2017 :
2018 0 : RootedObject stack(cx, NewDenseEmptyArray(cx));
2019 0 : if (!stack)
2020 0 : oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
2021 :
2022 : static int createdIndex = 0;
2023 0 : createdIndex++;
2024 :
2025 0 : if (!JS_DefineProperty(cx, obj, "index", createdIndex, 0,
2026 : JS_STUBGETTER, JS_STUBSETTER))
2027 : {
2028 0 : oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
2029 : }
2030 :
2031 0 : if (!JS_DefineProperty(cx, obj, "stack", stack, 0,
2032 : JS_STUBGETTER, JS_STUBSETTER))
2033 : {
2034 0 : oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
2035 : }
2036 :
2037 0 : int stackIndex = 0;
2038 0 : RootedId id(cx);
2039 0 : RootedValue callee(cx);
2040 0 : for (NonBuiltinScriptFrameIter iter(cx); !iter.done(); ++iter) {
2041 0 : if (iter.isFunctionFrame() && iter.compartment() == cx->compartment()) {
2042 0 : id = INT_TO_JSID(stackIndex);
2043 0 : RootedObject callee(cx, iter.callee(cx));
2044 0 : if (!JS_DefinePropertyById(cx, stack, id, callee, 0,
2045 : JS_STUBGETTER, JS_STUBSETTER))
2046 : {
2047 0 : oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
2048 : }
2049 0 : stackIndex++;
2050 : }
2051 : }
2052 :
2053 0 : return obj;
2054 : }
2055 :
2056 3 : const ShellAllocationMetadataBuilder ShellAllocationMetadataBuilder::metadataBuilder;
2057 :
2058 : static bool
2059 0 : EnableShellAllocationMetadataBuilder(JSContext* cx, unsigned argc, Value* vp)
2060 : {
2061 0 : CallArgs args = CallArgsFromVp(argc, vp);
2062 :
2063 0 : SetAllocationMetadataBuilder(cx, &ShellAllocationMetadataBuilder::metadataBuilder);
2064 :
2065 0 : args.rval().setUndefined();
2066 0 : return true;
2067 : }
2068 :
2069 : static bool
2070 0 : GetAllocationMetadata(JSContext* cx, unsigned argc, Value* vp)
2071 : {
2072 0 : CallArgs args = CallArgsFromVp(argc, vp);
2073 0 : if (args.length() != 1 || !args[0].isObject()) {
2074 0 : JS_ReportErrorASCII(cx, "Argument must be an object");
2075 0 : return false;
2076 : }
2077 :
2078 0 : args.rval().setObjectOrNull(GetAllocationMetadata(&args[0].toObject()));
2079 0 : return true;
2080 : }
2081 :
2082 : static bool
2083 0 : testingFunc_bailout(JSContext* cx, unsigned argc, Value* vp)
2084 : {
2085 0 : CallArgs args = CallArgsFromVp(argc, vp);
2086 :
2087 : // NOP when not in IonMonkey
2088 0 : args.rval().setUndefined();
2089 0 : return true;
2090 : }
2091 :
2092 : static bool
2093 0 : testingFunc_bailAfter(JSContext* cx, unsigned argc, Value* vp)
2094 : {
2095 0 : CallArgs args = CallArgsFromVp(argc, vp);
2096 0 : if (args.length() != 1 || !args[0].isInt32() || args[0].toInt32() < 0) {
2097 0 : JS_ReportErrorASCII(cx, "Argument must be a positive number that fits in an int32");
2098 0 : return false;
2099 : }
2100 :
2101 : #ifdef DEBUG
2102 0 : cx->zone()->group()->setIonBailAfter(args[0].toInt32());
2103 : #endif
2104 :
2105 0 : args.rval().setUndefined();
2106 0 : return true;
2107 : }
2108 :
2109 : static bool
2110 0 : testingFunc_inJit(JSContext* cx, unsigned argc, Value* vp)
2111 : {
2112 0 : CallArgs args = CallArgsFromVp(argc, vp);
2113 :
2114 0 : if (!jit::IsBaselineEnabled(cx)) {
2115 0 : JSString* error = JS_NewStringCopyZ(cx, "Baseline is disabled.");
2116 0 : if(!error)
2117 0 : return false;
2118 :
2119 0 : args.rval().setString(error);
2120 0 : return true;
2121 : }
2122 :
2123 0 : JSScript* script = cx->currentScript();
2124 0 : if (script && script->getWarmUpResetCount() >= 20) {
2125 0 : JSString* error = JS_NewStringCopyZ(cx, "Compilation is being repeatedly prevented. Giving up.");
2126 0 : if (!error)
2127 0 : return false;
2128 :
2129 0 : args.rval().setString(error);
2130 0 : return true;
2131 : }
2132 :
2133 0 : args.rval().setBoolean(cx->currentlyRunningInJit());
2134 0 : return true;
2135 : }
2136 :
2137 : static bool
2138 0 : testingFunc_inIon(JSContext* cx, unsigned argc, Value* vp)
2139 : {
2140 0 : CallArgs args = CallArgsFromVp(argc, vp);
2141 :
2142 0 : if (!jit::IsIonEnabled(cx)) {
2143 0 : JSString* error = JS_NewStringCopyZ(cx, "Ion is disabled.");
2144 0 : if (!error)
2145 0 : return false;
2146 :
2147 0 : args.rval().setString(error);
2148 0 : return true;
2149 : }
2150 :
2151 0 : ScriptFrameIter iter(cx);
2152 0 : if (!iter.done() && iter.isIon()) {
2153 : // Reset the counter of the IonScript's script.
2154 0 : jit::JitFrameIterator jitIter(cx);
2155 0 : ++jitIter;
2156 0 : jitIter.script()->resetWarmUpResetCounter();
2157 : } else {
2158 : // Check if we missed multiple attempts at compiling the innermost script.
2159 0 : JSScript* script = cx->currentScript();
2160 0 : if (script && script->getWarmUpResetCount() >= 20) {
2161 0 : JSString* error = JS_NewStringCopyZ(cx, "Compilation is being repeatedly prevented. Giving up.");
2162 0 : if (!error)
2163 0 : return false;
2164 :
2165 0 : args.rval().setString(error);
2166 0 : return true;
2167 : }
2168 : }
2169 :
2170 0 : args.rval().setBoolean(!iter.done() && iter.isIon());
2171 0 : return true;
2172 : }
2173 :
2174 : bool
2175 0 : js::testingFunc_assertFloat32(JSContext* cx, unsigned argc, Value* vp)
2176 : {
2177 0 : CallArgs args = CallArgsFromVp(argc, vp);
2178 0 : if (args.length() != 2) {
2179 0 : JS_ReportErrorASCII(cx, "Expects only 2 arguments");
2180 0 : return false;
2181 : }
2182 :
2183 : // NOP when not in IonMonkey
2184 0 : args.rval().setUndefined();
2185 0 : return true;
2186 : }
2187 :
2188 : static bool
2189 0 : TestingFunc_assertJitStackInvariants(JSContext* cx, unsigned argc, Value* vp)
2190 : {
2191 0 : CallArgs args = CallArgsFromVp(argc, vp);
2192 :
2193 0 : jit::AssertJitStackInvariants(cx);
2194 0 : args.rval().setUndefined();
2195 0 : return true;
2196 : }
2197 :
2198 : bool
2199 0 : js::testingFunc_assertRecoveredOnBailout(JSContext* cx, unsigned argc, Value* vp)
2200 : {
2201 0 : CallArgs args = CallArgsFromVp(argc, vp);
2202 0 : if (args.length() != 2) {
2203 0 : JS_ReportErrorASCII(cx, "Expects only 2 arguments");
2204 0 : return false;
2205 : }
2206 :
2207 : // NOP when not in IonMonkey
2208 0 : args.rval().setUndefined();
2209 0 : return true;
2210 : }
2211 :
2212 : static bool
2213 0 : SetJitCompilerOption(JSContext* cx, unsigned argc, Value* vp)
2214 : {
2215 0 : CallArgs args = CallArgsFromVp(argc, vp);
2216 0 : RootedObject callee(cx, &args.callee());
2217 :
2218 0 : if (args.length() != 2) {
2219 0 : ReportUsageErrorASCII(cx, callee, "Wrong number of arguments.");
2220 0 : return false;
2221 : }
2222 :
2223 0 : if (!args[0].isString()) {
2224 0 : ReportUsageErrorASCII(cx, callee, "First argument must be a String.");
2225 0 : return false;
2226 : }
2227 :
2228 0 : if (!args[1].isInt32()) {
2229 0 : ReportUsageErrorASCII(cx, callee, "Second argument must be an Int32.");
2230 0 : return false;
2231 : }
2232 :
2233 0 : JSFlatString* strArg = JS_FlattenString(cx, args[0].toString());
2234 0 : if (!strArg)
2235 0 : return false;
2236 :
2237 : #define JIT_COMPILER_MATCH(key, string) \
2238 : else if (JS_FlatStringEqualsAscii(strArg, string)) \
2239 : opt = JSJITCOMPILER_ ## key;
2240 :
2241 0 : JSJitCompilerOption opt = JSJITCOMPILER_NOT_AN_OPTION;
2242 : if (false) {}
2243 0 : JIT_COMPILER_OPTIONS(JIT_COMPILER_MATCH);
2244 : #undef JIT_COMPILER_MATCH
2245 :
2246 0 : if (opt == JSJITCOMPILER_NOT_AN_OPTION) {
2247 0 : ReportUsageErrorASCII(cx, callee, "First argument does not name a valid option (see jsapi.h).");
2248 0 : return false;
2249 : }
2250 :
2251 0 : int32_t number = args[1].toInt32();
2252 0 : if (number < 0)
2253 0 : number = -1;
2254 :
2255 : // Throw if disabling the JITs and there's JIT code on the stack, to avoid
2256 : // assertion failures.
2257 0 : if ((opt == JSJITCOMPILER_BASELINE_ENABLE || opt == JSJITCOMPILER_ION_ENABLE) &&
2258 : number == 0)
2259 : {
2260 0 : js::jit::JitActivationIterator iter(cx);
2261 0 : if (!iter.done()) {
2262 0 : JS_ReportErrorASCII(cx, "Can't turn off JITs with JIT code on the stack.");
2263 0 : return false;
2264 : }
2265 : }
2266 :
2267 0 : JS_SetGlobalJitCompilerOption(cx, opt, uint32_t(number));
2268 :
2269 0 : args.rval().setUndefined();
2270 0 : return true;
2271 : }
2272 :
2273 : static bool
2274 0 : GetJitCompilerOptions(JSContext* cx, unsigned argc, Value* vp)
2275 : {
2276 0 : CallArgs args = CallArgsFromVp(argc, vp);
2277 0 : RootedObject info(cx, JS_NewPlainObject(cx));
2278 0 : if (!info)
2279 0 : return false;
2280 :
2281 0 : uint32_t intValue = 0;
2282 0 : RootedValue value(cx);
2283 :
2284 : #define JIT_COMPILER_MATCH(key, string) \
2285 : opt = JSJITCOMPILER_ ## key; \
2286 : if (JS_GetGlobalJitCompilerOption(cx, opt, &intValue)) { \
2287 : value.setInt32(intValue); \
2288 : if (!JS_SetProperty(cx, info, string, value)) \
2289 : return false; \
2290 : }
2291 :
2292 0 : JSJitCompilerOption opt = JSJITCOMPILER_NOT_AN_OPTION;
2293 0 : JIT_COMPILER_OPTIONS(JIT_COMPILER_MATCH);
2294 : #undef JIT_COMPILER_MATCH
2295 :
2296 0 : args.rval().setObject(*info);
2297 0 : return true;
2298 : }
2299 :
2300 : static bool
2301 0 : SetIonCheckGraphCoherency(JSContext* cx, unsigned argc, Value* vp)
2302 : {
2303 0 : CallArgs args = CallArgsFromVp(argc, vp);
2304 0 : jit::JitOptions.checkGraphConsistency = ToBoolean(args.get(0));
2305 0 : args.rval().setUndefined();
2306 0 : return true;
2307 : }
2308 :
2309 : class CloneBufferObject : public NativeObject {
2310 : static const JSPropertySpec props_[2];
2311 : static const size_t DATA_SLOT = 0;
2312 : static const size_t LENGTH_SLOT = 1;
2313 : static const size_t NUM_SLOTS = 2;
2314 :
2315 : public:
2316 : static const Class class_;
2317 :
2318 0 : static CloneBufferObject* Create(JSContext* cx) {
2319 0 : RootedObject obj(cx, JS_NewObject(cx, Jsvalify(&class_)));
2320 0 : if (!obj)
2321 0 : return nullptr;
2322 0 : obj->as<CloneBufferObject>().setReservedSlot(DATA_SLOT, PrivateValue(nullptr));
2323 0 : obj->as<CloneBufferObject>().setReservedSlot(LENGTH_SLOT, Int32Value(0));
2324 :
2325 0 : if (!JS_DefineProperties(cx, obj, props_))
2326 0 : return nullptr;
2327 :
2328 0 : return &obj->as<CloneBufferObject>();
2329 : }
2330 :
2331 0 : static CloneBufferObject* Create(JSContext* cx, JSAutoStructuredCloneBuffer* buffer) {
2332 0 : Rooted<CloneBufferObject*> obj(cx, Create(cx));
2333 0 : if (!obj)
2334 0 : return nullptr;
2335 0 : auto data = js::MakeUnique<JSStructuredCloneData>();
2336 0 : if (!data) {
2337 0 : ReportOutOfMemory(cx);
2338 0 : return nullptr;
2339 : }
2340 0 : buffer->steal(data.get());
2341 0 : obj->setData(data.release());
2342 0 : return obj;
2343 : }
2344 :
2345 0 : JSStructuredCloneData* data() const {
2346 0 : return static_cast<JSStructuredCloneData*>(getReservedSlot(DATA_SLOT).toPrivate());
2347 : }
2348 :
2349 0 : void setData(JSStructuredCloneData* aData) {
2350 0 : MOZ_ASSERT(!data());
2351 0 : setReservedSlot(DATA_SLOT, PrivateValue(aData));
2352 0 : }
2353 :
2354 : // Discard an owned clone buffer.
2355 0 : void discard() {
2356 0 : if (data()) {
2357 0 : JSAutoStructuredCloneBuffer clonebuf(JS::StructuredCloneScope::SameProcessSameThread, nullptr, nullptr);
2358 0 : clonebuf.adopt(Move(*data()));
2359 : }
2360 0 : setReservedSlot(DATA_SLOT, PrivateValue(nullptr));
2361 0 : }
2362 :
2363 : static bool
2364 0 : setCloneBuffer_impl(JSContext* cx, const CallArgs& args) {
2365 0 : if (args.length() != 1) {
2366 0 : JS_ReportErrorASCII(cx, "clonebuffer setter requires a single string argument");
2367 0 : return false;
2368 : }
2369 0 : if (!args[0].isString()) {
2370 0 : JS_ReportErrorASCII(cx, "clonebuffer value must be a string");
2371 0 : return false;
2372 : }
2373 :
2374 0 : if (fuzzingSafe) {
2375 : // A manually-created clonebuffer could easily trigger a crash
2376 0 : args.rval().setUndefined();
2377 0 : return true;
2378 : }
2379 :
2380 0 : Rooted<CloneBufferObject*> obj(cx, &args.thisv().toObject().as<CloneBufferObject>());
2381 0 : obj->discard();
2382 :
2383 0 : char* str = JS_EncodeString(cx, args[0].toString());
2384 0 : if (!str)
2385 0 : return false;
2386 0 : size_t nbytes = JS_GetStringLength(args[0].toString());
2387 0 : MOZ_ASSERT(nbytes % sizeof(uint64_t) == 0);
2388 0 : auto buf = js::MakeUnique<JSStructuredCloneData>(0, 0, nbytes);
2389 0 : if (!buf->Init(nbytes, nbytes)) {
2390 0 : JS_free(cx, str);
2391 0 : return false;
2392 : }
2393 0 : js_memcpy(buf->Start(), str, nbytes);
2394 0 : JS_free(cx, str);
2395 0 : obj->setData(buf.release());
2396 :
2397 0 : args.rval().setUndefined();
2398 0 : return true;
2399 : }
2400 :
2401 : static bool
2402 0 : is(HandleValue v) {
2403 0 : return v.isObject() && v.toObject().is<CloneBufferObject>();
2404 : }
2405 :
2406 : static bool
2407 0 : setCloneBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) {
2408 0 : CallArgs args = CallArgsFromVp(argc, vp);
2409 0 : return CallNonGenericMethod<is, setCloneBuffer_impl>(cx, args);
2410 : }
2411 :
2412 : static bool
2413 0 : getCloneBuffer_impl(JSContext* cx, const CallArgs& args) {
2414 0 : Rooted<CloneBufferObject*> obj(cx, &args.thisv().toObject().as<CloneBufferObject>());
2415 0 : MOZ_ASSERT(args.length() == 0);
2416 :
2417 0 : if (!obj->data()) {
2418 0 : args.rval().setUndefined();
2419 0 : return true;
2420 : }
2421 :
2422 : bool hasTransferable;
2423 0 : if (!JS_StructuredCloneHasTransferables(*obj->data(), &hasTransferable))
2424 0 : return false;
2425 :
2426 0 : if (hasTransferable) {
2427 0 : JS_ReportErrorASCII(cx, "cannot retrieve structured clone buffer with transferables");
2428 0 : return false;
2429 : }
2430 :
2431 0 : size_t size = obj->data()->Size();
2432 0 : UniqueChars buffer(static_cast<char*>(js_malloc(size)));
2433 0 : if (!buffer) {
2434 0 : ReportOutOfMemory(cx);
2435 0 : return false;
2436 : }
2437 0 : auto iter = obj->data()->Iter();
2438 0 : obj->data()->ReadBytes(iter, buffer.get(), size);
2439 0 : JSString* str = JS_NewStringCopyN(cx, buffer.get(), size);
2440 0 : if (!str)
2441 0 : return false;
2442 0 : args.rval().setString(str);
2443 0 : return true;
2444 : }
2445 :
2446 : static bool
2447 0 : getCloneBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) {
2448 0 : CallArgs args = CallArgsFromVp(argc, vp);
2449 0 : return CallNonGenericMethod<is, getCloneBuffer_impl>(cx, args);
2450 : }
2451 :
2452 0 : static void Finalize(FreeOp* fop, JSObject* obj) {
2453 0 : obj->as<CloneBufferObject>().discard();
2454 0 : }
2455 : };
2456 :
2457 : static const ClassOps CloneBufferObjectClassOps = {
2458 : nullptr, /* addProperty */
2459 : nullptr, /* delProperty */
2460 : nullptr, /* getProperty */
2461 : nullptr, /* setProperty */
2462 : nullptr, /* enumerate */
2463 : nullptr, /* newEnumerate */
2464 : nullptr, /* resolve */
2465 : nullptr, /* mayResolve */
2466 : CloneBufferObject::Finalize
2467 : };
2468 :
2469 : const Class CloneBufferObject::class_ = {
2470 : "CloneBuffer",
2471 : JSCLASS_HAS_RESERVED_SLOTS(CloneBufferObject::NUM_SLOTS) |
2472 : JSCLASS_FOREGROUND_FINALIZE,
2473 : &CloneBufferObjectClassOps
2474 : };
2475 :
2476 : const JSPropertySpec CloneBufferObject::props_[] = {
2477 : JS_PSGS("clonebuffer", getCloneBuffer, setCloneBuffer, 0),
2478 : JS_PS_END
2479 : };
2480 :
2481 : static mozilla::Maybe<JS::StructuredCloneScope>
2482 0 : ParseCloneScope(JSContext* cx, HandleString str)
2483 : {
2484 0 : mozilla::Maybe<JS::StructuredCloneScope> scope;
2485 :
2486 0 : JSAutoByteString scopeStr(cx, str);
2487 0 : if (!scopeStr)
2488 0 : return scope;
2489 :
2490 0 : if (strcmp(scopeStr.ptr(), "SameProcessSameThread") == 0)
2491 0 : scope.emplace(JS::StructuredCloneScope::SameProcessSameThread);
2492 0 : else if (strcmp(scopeStr.ptr(), "SameProcessDifferentThread") == 0)
2493 0 : scope.emplace(JS::StructuredCloneScope::SameProcessDifferentThread);
2494 0 : else if (strcmp(scopeStr.ptr(), "DifferentProcess") == 0)
2495 0 : scope.emplace(JS::StructuredCloneScope::DifferentProcess);
2496 :
2497 0 : return scope;
2498 : }
2499 :
2500 : static bool
2501 0 : Serialize(JSContext* cx, unsigned argc, Value* vp)
2502 : {
2503 0 : CallArgs args = CallArgsFromVp(argc, vp);
2504 :
2505 0 : mozilla::Maybe<JSAutoStructuredCloneBuffer> clonebuf;
2506 0 : JS::CloneDataPolicy policy;
2507 :
2508 0 : if (!args.get(2).isUndefined()) {
2509 0 : RootedObject opts(cx, ToObject(cx, args.get(2)));
2510 0 : if (!opts)
2511 0 : return false;
2512 :
2513 0 : RootedValue v(cx);
2514 0 : if (!JS_GetProperty(cx, opts, "SharedArrayBuffer", &v))
2515 0 : return false;
2516 :
2517 0 : if (!v.isUndefined()) {
2518 0 : JSString* str = JS::ToString(cx, v);
2519 0 : if (!str)
2520 0 : return false;
2521 0 : JSAutoByteString poli(cx, str);
2522 0 : if (!poli)
2523 0 : return false;
2524 :
2525 0 : if (strcmp(poli.ptr(), "allow") == 0) {
2526 : // default
2527 0 : } else if (strcmp(poli.ptr(), "deny") == 0) {
2528 0 : policy.denySharedArrayBuffer();
2529 : } else {
2530 0 : JS_ReportErrorASCII(cx, "Invalid policy value for 'SharedArrayBuffer'");
2531 0 : return false;
2532 : }
2533 : }
2534 :
2535 0 : if (!JS_GetProperty(cx, opts, "scope", &v))
2536 0 : return false;
2537 :
2538 0 : if (!v.isUndefined()) {
2539 0 : RootedString str(cx, JS::ToString(cx, v));
2540 0 : if (!str)
2541 0 : return false;
2542 0 : auto scope = ParseCloneScope(cx, str);
2543 0 : if (!scope) {
2544 0 : JS_ReportErrorASCII(cx, "Invalid structured clone scope");
2545 0 : return false;
2546 : }
2547 0 : clonebuf.emplace(*scope, nullptr, nullptr);
2548 : }
2549 : }
2550 :
2551 0 : if (!clonebuf)
2552 0 : clonebuf.emplace(JS::StructuredCloneScope::SameProcessSameThread, nullptr, nullptr);
2553 :
2554 0 : if (!clonebuf->write(cx, args.get(0), args.get(1), policy))
2555 0 : return false;
2556 :
2557 0 : RootedObject obj(cx, CloneBufferObject::Create(cx, clonebuf.ptr()));
2558 0 : if (!obj)
2559 0 : return false;
2560 :
2561 0 : args.rval().setObject(*obj);
2562 0 : return true;
2563 : }
2564 :
2565 : static bool
2566 0 : Deserialize(JSContext* cx, unsigned argc, Value* vp)
2567 : {
2568 0 : CallArgs args = CallArgsFromVp(argc, vp);
2569 :
2570 0 : if (!args.get(0).isObject() || !args[0].toObject().is<CloneBufferObject>()) {
2571 0 : JS_ReportErrorASCII(cx, "deserialize requires a clonebuffer argument");
2572 0 : return false;
2573 : }
2574 :
2575 0 : JS::StructuredCloneScope scope = JS::StructuredCloneScope::SameProcessSameThread;
2576 0 : if (args.get(1).isObject()) {
2577 0 : RootedObject opts(cx, &args[1].toObject());
2578 0 : if (!opts)
2579 0 : return false;
2580 :
2581 0 : RootedValue v(cx);
2582 0 : if (!JS_GetProperty(cx, opts, "scope", &v))
2583 0 : return false;
2584 :
2585 0 : if (!v.isUndefined()) {
2586 0 : RootedString str(cx, JS::ToString(cx, v));
2587 0 : if (!str)
2588 0 : return false;
2589 0 : auto maybeScope = ParseCloneScope(cx, str);
2590 0 : if (!maybeScope) {
2591 0 : JS_ReportErrorASCII(cx, "Invalid structured clone scope");
2592 0 : return false;
2593 : }
2594 :
2595 0 : scope = *maybeScope;
2596 : }
2597 : }
2598 :
2599 0 : Rooted<CloneBufferObject*> obj(cx, &args[0].toObject().as<CloneBufferObject>());
2600 :
2601 : // Clone buffer was already consumed?
2602 0 : if (!obj->data()) {
2603 : JS_ReportErrorASCII(cx, "deserialize given invalid clone buffer "
2604 0 : "(transferables already consumed?)");
2605 0 : return false;
2606 : }
2607 :
2608 : bool hasTransferable;
2609 0 : if (!JS_StructuredCloneHasTransferables(*obj->data(), &hasTransferable))
2610 0 : return false;
2611 :
2612 0 : RootedValue deserialized(cx);
2613 0 : if (!JS_ReadStructuredClone(cx, *obj->data(),
2614 : JS_STRUCTURED_CLONE_VERSION,
2615 : scope,
2616 : &deserialized, nullptr, nullptr))
2617 : {
2618 0 : return false;
2619 : }
2620 0 : args.rval().set(deserialized);
2621 :
2622 0 : if (hasTransferable)
2623 0 : obj->discard();
2624 :
2625 0 : return true;
2626 : }
2627 :
2628 : static bool
2629 0 : DetachArrayBuffer(JSContext* cx, unsigned argc, Value* vp)
2630 : {
2631 0 : CallArgs args = CallArgsFromVp(argc, vp);
2632 :
2633 0 : if (args.length() != 1) {
2634 0 : JS_ReportErrorASCII(cx, "detachArrayBuffer() requires a single argument");
2635 0 : return false;
2636 : }
2637 :
2638 0 : if (!args[0].isObject()) {
2639 0 : JS_ReportErrorASCII(cx, "detachArrayBuffer must be passed an object");
2640 0 : return false;
2641 : }
2642 :
2643 0 : RootedObject obj(cx, &args[0].toObject());
2644 0 : if (!JS_DetachArrayBuffer(cx, obj))
2645 0 : return false;
2646 :
2647 0 : args.rval().setUndefined();
2648 0 : return true;
2649 : }
2650 :
2651 : static bool
2652 0 : HelperThreadCount(JSContext* cx, unsigned argc, Value* vp)
2653 : {
2654 0 : CallArgs args = CallArgsFromVp(argc, vp);
2655 : #ifdef JS_MORE_DETERMINISTIC
2656 : // Always return 0 to get consistent output with and without --no-threads.
2657 : args.rval().setInt32(0);
2658 : #else
2659 0 : if (CanUseExtraThreads())
2660 0 : args.rval().setInt32(HelperThreadState().threadCount);
2661 : else
2662 0 : args.rval().setInt32(0);
2663 : #endif
2664 0 : return true;
2665 : }
2666 :
2667 : #ifdef JS_TRACE_LOGGING
2668 : static bool
2669 0 : EnableTraceLogger(JSContext* cx, unsigned argc, Value* vp)
2670 : {
2671 0 : CallArgs args = CallArgsFromVp(argc, vp);
2672 0 : TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
2673 0 : if (!TraceLoggerEnable(logger, cx))
2674 0 : return false;
2675 :
2676 0 : args.rval().setUndefined();
2677 0 : return true;
2678 : }
2679 :
2680 : static bool
2681 0 : DisableTraceLogger(JSContext* cx, unsigned argc, Value* vp)
2682 : {
2683 0 : CallArgs args = CallArgsFromVp(argc, vp);
2684 0 : TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
2685 0 : args.rval().setBoolean(TraceLoggerDisable(logger));
2686 :
2687 0 : return true;
2688 : }
2689 : #endif
2690 :
2691 : #ifdef DEBUG
2692 : static bool
2693 0 : DumpObject(JSContext* cx, unsigned argc, Value* vp)
2694 : {
2695 0 : CallArgs args = CallArgsFromVp(argc, vp);
2696 0 : RootedObject obj(cx, ToObject(cx, args.get(0)));
2697 0 : if (!obj)
2698 0 : return false;
2699 :
2700 0 : DumpObject(obj);
2701 :
2702 0 : args.rval().setUndefined();
2703 0 : return true;
2704 : }
2705 : #endif
2706 :
2707 : static bool
2708 0 : SharedMemoryEnabled(JSContext* cx, unsigned argc, Value* vp)
2709 : {
2710 0 : CallArgs args = CallArgsFromVp(argc, vp);
2711 0 : args.rval().setBoolean(cx->compartment()->creationOptions().getSharedMemoryAndAtomicsEnabled());
2712 0 : return true;
2713 : }
2714 :
2715 : static bool
2716 0 : SharedArrayRawBufferCount(JSContext* cx, unsigned argc, Value* vp)
2717 : {
2718 0 : CallArgs args = CallArgsFromVp(argc, vp);
2719 0 : args.rval().setInt32(SharedArrayRawBuffer::liveBuffers());
2720 0 : return true;
2721 : }
2722 :
2723 : static bool
2724 0 : SharedArrayRawBufferRefcount(JSContext* cx, unsigned argc, Value* vp)
2725 : {
2726 0 : CallArgs args = CallArgsFromVp(argc, vp);
2727 0 : if (args.length() != 1 || !args[0].isObject()) {
2728 0 : JS_ReportErrorASCII(cx, "Expected SharedArrayBuffer object");
2729 0 : return false;
2730 : }
2731 0 : RootedObject obj(cx, &args[0].toObject());
2732 0 : if (!obj->is<SharedArrayBufferObject>()) {
2733 0 : JS_ReportErrorASCII(cx, "Expected SharedArrayBuffer object");
2734 0 : return false;
2735 : }
2736 0 : args.rval().setInt32(obj->as<SharedArrayBufferObject>().rawBufferObject()->refcount());
2737 0 : return true;
2738 : }
2739 :
2740 : #ifdef NIGHTLY_BUILD
2741 : static bool
2742 0 : ObjectAddress(JSContext* cx, unsigned argc, Value* vp)
2743 : {
2744 0 : CallArgs args = CallArgsFromVp(argc, vp);
2745 0 : if (args.length() != 1) {
2746 0 : RootedObject callee(cx, &args.callee());
2747 0 : ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
2748 0 : return false;
2749 : }
2750 0 : if (!args[0].isObject()) {
2751 0 : RootedObject callee(cx, &args.callee());
2752 0 : ReportUsageErrorASCII(cx, callee, "Expected object");
2753 0 : return false;
2754 : }
2755 :
2756 : #ifdef JS_MORE_DETERMINISTIC
2757 : args.rval().setInt32(0);
2758 : #else
2759 0 : void* ptr = js::UncheckedUnwrap(&args[0].toObject(), true);
2760 : char buffer[64];
2761 0 : SprintfLiteral(buffer, "%p", ptr);
2762 :
2763 0 : JSString* str = JS_NewStringCopyZ(cx, buffer);
2764 0 : if (!str)
2765 0 : return false;
2766 :
2767 0 : args.rval().setString(str);
2768 : #endif
2769 :
2770 0 : return true;
2771 : }
2772 :
2773 : static bool
2774 0 : SharedAddress(JSContext* cx, unsigned argc, Value* vp)
2775 : {
2776 0 : CallArgs args = CallArgsFromVp(argc, vp);
2777 0 : if (args.length() != 1) {
2778 0 : RootedObject callee(cx, &args.callee());
2779 0 : ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
2780 0 : return false;
2781 : }
2782 0 : if (!args[0].isObject()) {
2783 0 : RootedObject callee(cx, &args.callee());
2784 0 : ReportUsageErrorASCII(cx, callee, "Expected object");
2785 0 : return false;
2786 : }
2787 :
2788 : #ifdef JS_MORE_DETERMINISTIC
2789 : args.rval().setString(cx->staticStrings().getUint(0));
2790 : #else
2791 0 : RootedObject obj(cx, CheckedUnwrap(&args[0].toObject()));
2792 0 : if (!obj) {
2793 0 : ReportAccessDenied(cx);
2794 0 : return false;
2795 : }
2796 0 : if (!obj->is<SharedArrayBufferObject>()) {
2797 0 : JS_ReportErrorASCII(cx, "Argument must be a SharedArrayBuffer");
2798 0 : return false;
2799 : }
2800 : char buffer[64];
2801 : uint32_t nchar =
2802 0 : SprintfLiteral(buffer, "%p",
2803 0 : obj->as<SharedArrayBufferObject>().dataPointerShared().unwrap(/*safeish*/));
2804 :
2805 0 : JSString* str = JS_NewStringCopyN(cx, buffer, nchar);
2806 0 : if (!str)
2807 0 : return false;
2808 :
2809 0 : args.rval().setString(str);
2810 : #endif
2811 :
2812 0 : return true;
2813 : }
2814 : #endif
2815 :
2816 : static bool
2817 0 : DumpBacktrace(JSContext* cx, unsigned argc, Value* vp)
2818 : {
2819 0 : CallArgs args = CallArgsFromVp(argc, vp);
2820 0 : DumpBacktrace(cx);
2821 0 : args.rval().setUndefined();
2822 0 : return true;
2823 : }
2824 :
2825 : static bool
2826 0 : GetBacktrace(JSContext* cx, unsigned argc, Value* vp)
2827 : {
2828 0 : CallArgs args = CallArgsFromVp(argc, vp);
2829 :
2830 0 : bool showArgs = false;
2831 0 : bool showLocals = false;
2832 0 : bool showThisProps = false;
2833 :
2834 0 : if (args.length() > 1) {
2835 0 : RootedObject callee(cx, &args.callee());
2836 0 : ReportUsageErrorASCII(cx, callee, "Too many arguments");
2837 0 : return false;
2838 : }
2839 :
2840 0 : if (args.length() == 1) {
2841 0 : RootedObject cfg(cx, ToObject(cx, args[0]));
2842 0 : if (!cfg)
2843 0 : return false;
2844 0 : RootedValue v(cx);
2845 :
2846 0 : if (!JS_GetProperty(cx, cfg, "args", &v))
2847 0 : return false;
2848 0 : showArgs = ToBoolean(v);
2849 :
2850 0 : if (!JS_GetProperty(cx, cfg, "locals", &v))
2851 0 : return false;
2852 0 : showLocals = ToBoolean(v);
2853 :
2854 0 : if (!JS_GetProperty(cx, cfg, "thisprops", &v))
2855 0 : return false;
2856 0 : showThisProps = ToBoolean(v);
2857 : }
2858 :
2859 0 : JS::UniqueChars buf = JS::FormatStackDump(cx, nullptr, showArgs, showLocals, showThisProps);
2860 0 : if (!buf)
2861 0 : return false;
2862 :
2863 0 : RootedString str(cx);
2864 0 : if (!(str = JS_NewStringCopyZ(cx, buf.get())))
2865 0 : return false;
2866 :
2867 0 : args.rval().setString(str);
2868 0 : return true;
2869 : }
2870 :
2871 : static bool
2872 0 : ReportOutOfMemory(JSContext* cx, unsigned argc, Value* vp)
2873 : {
2874 0 : CallArgs args = CallArgsFromVp(argc, vp);
2875 0 : JS_ReportOutOfMemory(cx);
2876 0 : cx->clearPendingException();
2877 0 : args.rval().setUndefined();
2878 0 : return true;
2879 : }
2880 :
2881 : static bool
2882 0 : ThrowOutOfMemory(JSContext* cx, unsigned argc, Value* vp)
2883 : {
2884 0 : JS_ReportOutOfMemory(cx);
2885 0 : return false;
2886 : }
2887 :
2888 : static bool
2889 0 : ReportLargeAllocationFailure(JSContext* cx, unsigned argc, Value* vp)
2890 : {
2891 0 : CallArgs args = CallArgsFromVp(argc, vp);
2892 0 : void* buf = cx->runtime()->onOutOfMemoryCanGC(AllocFunction::Malloc, JSRuntime::LARGE_ALLOCATION);
2893 0 : js_free(buf);
2894 0 : args.rval().setUndefined();
2895 0 : return true;
2896 : }
2897 :
2898 : namespace heaptools {
2899 :
2900 : typedef UniqueTwoByteChars EdgeName;
2901 :
2902 : // An edge to a node from its predecessor in a path through the graph.
2903 0 : class BackEdge {
2904 : // The node from which this edge starts.
2905 : JS::ubi::Node predecessor_;
2906 :
2907 : // The name of this edge.
2908 : EdgeName name_;
2909 :
2910 : public:
2911 0 : BackEdge() : name_(nullptr) { }
2912 : // Construct an initialized back edge, taking ownership of |name|.
2913 0 : BackEdge(JS::ubi::Node predecessor, EdgeName name)
2914 0 : : predecessor_(predecessor), name_(Move(name)) { }
2915 0 : BackEdge(BackEdge&& rhs) : predecessor_(rhs.predecessor_), name_(Move(rhs.name_)) { }
2916 0 : BackEdge& operator=(BackEdge&& rhs) {
2917 0 : MOZ_ASSERT(&rhs != this);
2918 0 : this->~BackEdge();
2919 0 : new(this) BackEdge(Move(rhs));
2920 0 : return *this;
2921 : }
2922 :
2923 0 : EdgeName forgetName() { return Move(name_); }
2924 0 : JS::ubi::Node predecessor() const { return predecessor_; }
2925 :
2926 : private:
2927 : // No copy constructor or copying assignment.
2928 : BackEdge(const BackEdge&) = delete;
2929 : BackEdge& operator=(const BackEdge&) = delete;
2930 : };
2931 :
2932 : // A path-finding handler class for use with JS::ubi::BreadthFirst.
2933 : struct FindPathHandler {
2934 : typedef BackEdge NodeData;
2935 : typedef JS::ubi::BreadthFirst<FindPathHandler> Traversal;
2936 :
2937 0 : FindPathHandler(JSContext*cx, JS::ubi::Node start, JS::ubi::Node target,
2938 : MutableHandle<GCVector<Value>> nodes, Vector<EdgeName>& edges)
2939 0 : : cx(cx), start(start), target(target), foundPath(false),
2940 0 : nodes(nodes), edges(edges) { }
2941 :
2942 : bool
2943 0 : operator()(Traversal& traversal, JS::ubi::Node origin, const JS::ubi::Edge& edge,
2944 : BackEdge* backEdge, bool first)
2945 : {
2946 : // We take care of each node the first time we visit it, so there's
2947 : // nothing to be done on subsequent visits.
2948 0 : if (!first)
2949 0 : return true;
2950 :
2951 : // Record how we reached this node. This is the last edge on a
2952 : // shortest path to this node.
2953 0 : EdgeName edgeName = DuplicateString(cx, edge.name.get());
2954 0 : if (!edgeName)
2955 0 : return false;
2956 0 : *backEdge = mozilla::Move(BackEdge(origin, Move(edgeName)));
2957 :
2958 : // Have we reached our final target node?
2959 0 : if (edge.referent == target) {
2960 : // Record the path that got us here, which must be a shortest path.
2961 0 : if (!recordPath(traversal))
2962 0 : return false;
2963 0 : foundPath = true;
2964 0 : traversal.stop();
2965 : }
2966 :
2967 0 : return true;
2968 : }
2969 :
2970 : // We've found a path to our target. Walk the backlinks to produce the
2971 : // (reversed) path, saving the path in |nodes| and |edges|. |nodes| is
2972 : // rooted, so it can hold the path's nodes as we leave the scope of
2973 : // the AutoCheckCannotGC.
2974 0 : bool recordPath(Traversal& traversal) {
2975 0 : JS::ubi::Node here = target;
2976 :
2977 0 : do {
2978 0 : Traversal::NodeMap::Ptr p = traversal.visited.lookup(here);
2979 0 : MOZ_ASSERT(p);
2980 0 : JS::ubi::Node predecessor = p->value().predecessor();
2981 0 : if (!nodes.append(predecessor.exposeToJS()) ||
2982 0 : !edges.append(p->value().forgetName()))
2983 0 : return false;
2984 0 : here = predecessor;
2985 0 : } while (here != start);
2986 :
2987 0 : return true;
2988 : }
2989 :
2990 : JSContext* cx;
2991 :
2992 : // The node we're starting from.
2993 : JS::ubi::Node start;
2994 :
2995 : // The node we're looking for.
2996 : JS::ubi::Node target;
2997 :
2998 : // True if we found a path to target, false if we didn't.
2999 : bool foundPath;
3000 :
3001 : // The nodes and edges of the path --- should we find one. The path is
3002 : // stored in reverse order, because that's how it's easiest for us to
3003 : // construct it:
3004 : // - edges[i] is the name of the edge from nodes[i] to nodes[i-1].
3005 : // - edges[0] is the name of the edge from nodes[0] to the target.
3006 : // - The last node, nodes[n-1], is the start node.
3007 : MutableHandle<GCVector<Value>> nodes;
3008 : Vector<EdgeName>& edges;
3009 : };
3010 :
3011 : } // namespace heaptools
3012 :
3013 : static bool
3014 0 : FindPath(JSContext* cx, unsigned argc, Value* vp)
3015 : {
3016 0 : CallArgs args = CallArgsFromVp(argc, vp);
3017 0 : if (argc < 2) {
3018 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
3019 0 : "findPath", "1", "");
3020 0 : return false;
3021 : }
3022 :
3023 : // We don't ToString non-objects given as 'start' or 'target', because this
3024 : // test is all about object identity, and ToString doesn't preserve that.
3025 : // Non-GCThing endpoints don't make much sense.
3026 0 : if (!args[0].isObject() && !args[0].isString() && !args[0].isSymbol()) {
3027 0 : ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
3028 : JSDVG_SEARCH_STACK, args[0], nullptr,
3029 0 : "not an object, string, or symbol", NULL);
3030 0 : return false;
3031 : }
3032 :
3033 0 : if (!args[1].isObject() && !args[1].isString() && !args[1].isSymbol()) {
3034 0 : ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
3035 : JSDVG_SEARCH_STACK, args[0], nullptr,
3036 0 : "not an object, string, or symbol", NULL);
3037 0 : return false;
3038 : }
3039 :
3040 0 : Rooted<GCVector<Value>> nodes(cx, GCVector<Value>(cx));
3041 0 : Vector<heaptools::EdgeName> edges(cx);
3042 :
3043 : {
3044 : // We can't tolerate the GC moving things around while we're searching
3045 : // the heap. Check that nothing we do causes a GC.
3046 0 : JS::AutoCheckCannotGC autoCannotGC;
3047 :
3048 0 : JS::ubi::Node start(args[0]), target(args[1]);
3049 :
3050 0 : heaptools::FindPathHandler handler(cx, start, target, &nodes, edges);
3051 0 : heaptools::FindPathHandler::Traversal traversal(cx, handler, autoCannotGC);
3052 0 : if (!traversal.init() || !traversal.addStart(start)) {
3053 0 : ReportOutOfMemory(cx);
3054 0 : return false;
3055 : }
3056 :
3057 0 : if (!traversal.traverse()) {
3058 0 : if (!cx->isExceptionPending())
3059 0 : ReportOutOfMemory(cx);
3060 0 : return false;
3061 : }
3062 :
3063 0 : if (!handler.foundPath) {
3064 : // We didn't find any paths from the start to the target.
3065 0 : args.rval().setUndefined();
3066 0 : return true;
3067 : }
3068 : }
3069 :
3070 : // |nodes| and |edges| contain the path from |start| to |target|, reversed.
3071 : // Construct a JavaScript array describing the path from the start to the
3072 : // target. Each element has the form:
3073 : //
3074 : // {
3075 : // node: <object or string or symbol>,
3076 : // edge: <string describing outgoing edge from node>
3077 : // }
3078 : //
3079 : // or, if the node is some internal thing that isn't a proper JavaScript
3080 : // value:
3081 : //
3082 : // { node: undefined, edge: <string> }
3083 0 : size_t length = nodes.length();
3084 0 : RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, length));
3085 0 : if (!result)
3086 0 : return false;
3087 0 : result->ensureDenseInitializedLength(cx, 0, length);
3088 :
3089 : // Walk |nodes| and |edges| in the stored order, and construct the result
3090 : // array in start-to-target order.
3091 0 : for (size_t i = 0; i < length; i++) {
3092 : // Build an object describing the node and edge.
3093 0 : RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
3094 0 : if (!obj)
3095 0 : return false;
3096 :
3097 0 : RootedValue wrapped(cx, nodes[i]);
3098 0 : if (!cx->compartment()->wrap(cx, &wrapped))
3099 0 : return false;
3100 :
3101 0 : if (!JS_DefineProperty(cx, obj, "node", wrapped,
3102 : JSPROP_ENUMERATE, nullptr, nullptr))
3103 0 : return false;
3104 :
3105 0 : heaptools::EdgeName edgeName = Move(edges[i]);
3106 :
3107 0 : RootedString edgeStr(cx, NewString<CanGC>(cx, edgeName.get(), js_strlen(edgeName.get())));
3108 0 : if (!edgeStr)
3109 0 : return false;
3110 0 : mozilla::Unused << edgeName.release(); // edgeStr acquired ownership
3111 :
3112 0 : if (!JS_DefineProperty(cx, obj, "edge", edgeStr, JSPROP_ENUMERATE, nullptr, nullptr))
3113 0 : return false;
3114 :
3115 0 : result->setDenseElement(length - i - 1, ObjectValue(*obj));
3116 : }
3117 :
3118 0 : args.rval().setObject(*result);
3119 0 : return true;
3120 : }
3121 :
3122 : static bool
3123 0 : ShortestPaths(JSContext* cx, unsigned argc, Value* vp)
3124 : {
3125 0 : CallArgs args = CallArgsFromVp(argc, vp);
3126 0 : if (!args.requireAtLeast(cx, "shortestPaths", 3))
3127 0 : return false;
3128 :
3129 : // We don't ToString non-objects given as 'start' or 'target', because this
3130 : // test is all about object identity, and ToString doesn't preserve that.
3131 : // Non-GCThing endpoints don't make much sense.
3132 0 : if (!args[0].isObject() && !args[0].isString() && !args[0].isSymbol()) {
3133 0 : ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
3134 : JSDVG_SEARCH_STACK, args[0], nullptr,
3135 0 : "not an object, string, or symbol", nullptr);
3136 0 : return false;
3137 : }
3138 :
3139 0 : if (!args[1].isObject() || !args[1].toObject().is<ArrayObject>()) {
3140 0 : ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
3141 : JSDVG_SEARCH_STACK, args[1], nullptr,
3142 0 : "not an array object", nullptr);
3143 0 : return false;
3144 : }
3145 :
3146 0 : RootedArrayObject objs(cx, &args[1].toObject().as<ArrayObject>());
3147 0 : size_t length = objs->getDenseInitializedLength();
3148 0 : if (length == 0) {
3149 0 : ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
3150 : JSDVG_SEARCH_STACK, args[1], nullptr,
3151 0 : "not a dense array object with one or more elements", nullptr);
3152 0 : return false;
3153 : }
3154 :
3155 0 : for (size_t i = 0; i < length; i++) {
3156 0 : RootedValue el(cx, objs->getDenseElement(i));
3157 0 : if (!el.isObject() && !el.isString() && !el.isSymbol()) {
3158 0 : JS_ReportErrorASCII(cx, "Each target must be an object, string, or symbol");
3159 0 : return false;
3160 : }
3161 : }
3162 :
3163 : int32_t maxNumPaths;
3164 0 : if (!JS::ToInt32(cx, args[2], &maxNumPaths))
3165 0 : return false;
3166 0 : if (maxNumPaths <= 0) {
3167 0 : ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
3168 : JSDVG_SEARCH_STACK, args[2], nullptr,
3169 0 : "not greater than 0", nullptr);
3170 0 : return false;
3171 : }
3172 :
3173 : // We accumulate the results into a GC-stable form, due to the fact that the
3174 : // JS::ubi::ShortestPaths lifetime (when operating on the live heap graph)
3175 : // is bounded within an AutoCheckCannotGC.
3176 0 : Rooted<GCVector<GCVector<GCVector<Value>>>> values(cx, GCVector<GCVector<GCVector<Value>>>(cx));
3177 0 : Vector<Vector<Vector<JS::ubi::EdgeName>>> names(cx);
3178 :
3179 : {
3180 0 : JS::AutoCheckCannotGC noGC(cx);
3181 :
3182 0 : JS::ubi::NodeSet targets;
3183 0 : if (!targets.init()) {
3184 0 : ReportOutOfMemory(cx);
3185 0 : return false;
3186 : }
3187 :
3188 0 : for (size_t i = 0; i < length; i++) {
3189 0 : RootedValue val(cx, objs->getDenseElement(i));
3190 0 : JS::ubi::Node node(val);
3191 0 : if (!targets.put(node)) {
3192 0 : ReportOutOfMemory(cx);
3193 0 : return false;
3194 : }
3195 : }
3196 :
3197 0 : JS::ubi::Node root(args[0]);
3198 : auto maybeShortestPaths = JS::ubi::ShortestPaths::Create(cx, noGC, maxNumPaths,
3199 0 : root, mozilla::Move(targets));
3200 0 : if (maybeShortestPaths.isNothing()) {
3201 0 : ReportOutOfMemory(cx);
3202 0 : return false;
3203 : }
3204 0 : auto& shortestPaths = *maybeShortestPaths;
3205 :
3206 0 : for (size_t i = 0; i < length; i++) {
3207 0 : if (!values.append(GCVector<GCVector<Value>>(cx)) ||
3208 0 : !names.append(Vector<Vector<JS::ubi::EdgeName>>(cx)))
3209 : {
3210 0 : return false;
3211 : }
3212 :
3213 0 : RootedValue val(cx, objs->getDenseElement(i));
3214 0 : JS::ubi::Node target(val);
3215 :
3216 0 : bool ok = shortestPaths.forEachPath(target, [&](JS::ubi::Path& path) {
3217 0 : Rooted<GCVector<Value>> pathVals(cx, GCVector<Value>(cx));
3218 0 : Vector<JS::ubi::EdgeName> pathNames(cx);
3219 :
3220 0 : for (auto& part : path) {
3221 0 : if (!pathVals.append(part->predecessor().exposeToJS()) ||
3222 0 : !pathNames.append(mozilla::Move(part->name())))
3223 : {
3224 0 : return false;
3225 : }
3226 : }
3227 :
3228 0 : return values.back().append(mozilla::Move(pathVals.get())) &&
3229 0 : names.back().append(mozilla::Move(pathNames));
3230 0 : });
3231 :
3232 0 : if (!ok)
3233 0 : return false;
3234 : }
3235 : }
3236 :
3237 0 : MOZ_ASSERT(values.length() == names.length());
3238 0 : MOZ_ASSERT(values.length() == length);
3239 :
3240 0 : RootedArrayObject results(cx, NewDenseFullyAllocatedArray(cx, length));
3241 0 : if (!results)
3242 0 : return false;
3243 0 : results->ensureDenseInitializedLength(cx, 0, length);
3244 :
3245 0 : for (size_t i = 0; i < length; i++) {
3246 0 : size_t numPaths = values[i].length();
3247 0 : MOZ_ASSERT(names[i].length() == numPaths);
3248 :
3249 0 : RootedArrayObject pathsArray(cx, NewDenseFullyAllocatedArray(cx, numPaths));
3250 0 : if (!pathsArray)
3251 0 : return false;
3252 0 : pathsArray->ensureDenseInitializedLength(cx, 0, numPaths);
3253 :
3254 0 : for (size_t j = 0; j < numPaths; j++) {
3255 0 : size_t pathLength = values[i][j].length();
3256 0 : MOZ_ASSERT(names[i][j].length() == pathLength);
3257 :
3258 0 : RootedArrayObject path(cx, NewDenseFullyAllocatedArray(cx, pathLength));
3259 0 : if (!path)
3260 0 : return false;
3261 0 : path->ensureDenseInitializedLength(cx, 0, pathLength);
3262 :
3263 0 : for (size_t k = 0; k < pathLength; k++) {
3264 0 : RootedPlainObject part(cx, NewBuiltinClassInstance<PlainObject>(cx));
3265 0 : if (!part)
3266 0 : return false;
3267 :
3268 0 : RootedValue predecessor(cx, values[i][j][k]);
3269 0 : if (!cx->compartment()->wrap(cx, &predecessor) ||
3270 0 : !JS_DefineProperty(cx, part, "predecessor", predecessor, JSPROP_ENUMERATE))
3271 : {
3272 0 : return false;
3273 : }
3274 :
3275 0 : if (names[i][j][k]) {
3276 0 : RootedString edge(cx, NewStringCopyZ<CanGC>(cx, names[i][j][k].get()));
3277 0 : if (!edge || !JS_DefineProperty(cx, part, "edge", edge, JSPROP_ENUMERATE))
3278 0 : return false;
3279 : }
3280 :
3281 0 : path->setDenseElement(k, ObjectValue(*part));
3282 : }
3283 :
3284 0 : pathsArray->setDenseElement(j, ObjectValue(*path));
3285 : }
3286 :
3287 0 : results->setDenseElement(i, ObjectValue(*pathsArray));
3288 : }
3289 :
3290 0 : args.rval().setObject(*results);
3291 0 : return true;
3292 : }
3293 :
3294 : static bool
3295 0 : EvalReturningScope(JSContext* cx, unsigned argc, Value* vp)
3296 : {
3297 0 : CallArgs args = CallArgsFromVp(argc, vp);
3298 0 : if (!args.requireAtLeast(cx, "evalReturningScope", 1))
3299 0 : return false;
3300 :
3301 0 : RootedString str(cx, ToString(cx, args[0]));
3302 0 : if (!str)
3303 0 : return false;
3304 :
3305 0 : RootedObject global(cx);
3306 0 : if (args.hasDefined(1)) {
3307 0 : global = ToObject(cx, args[1]);
3308 0 : if (!global)
3309 0 : return false;
3310 : }
3311 :
3312 0 : AutoStableStringChars strChars(cx);
3313 0 : if (!strChars.initTwoByte(cx, str))
3314 0 : return false;
3315 :
3316 0 : mozilla::Range<const char16_t> chars = strChars.twoByteRange();
3317 0 : size_t srclen = chars.length();
3318 0 : const char16_t* src = chars.begin().get();
3319 :
3320 0 : JS::AutoFilename filename;
3321 : unsigned lineno;
3322 :
3323 0 : JS::DescribeScriptedCaller(cx, &filename, &lineno);
3324 :
3325 0 : JS::CompileOptions options(cx);
3326 0 : options.setFileAndLine(filename.get(), lineno);
3327 0 : options.setNoScriptRval(true);
3328 :
3329 0 : JS::SourceBufferHolder srcBuf(src, srclen, JS::SourceBufferHolder::NoOwnership);
3330 0 : RootedScript script(cx);
3331 0 : if (!JS::CompileForNonSyntacticScope(cx, options, srcBuf, &script))
3332 0 : return false;
3333 :
3334 0 : if (global) {
3335 0 : global = CheckedUnwrap(global);
3336 0 : if (!global) {
3337 0 : JS_ReportErrorASCII(cx, "Permission denied to access global");
3338 0 : return false;
3339 : }
3340 0 : if (!global->is<GlobalObject>()) {
3341 0 : JS_ReportErrorASCII(cx, "Argument must be a global object");
3342 0 : return false;
3343 : }
3344 : } else {
3345 0 : global = JS::CurrentGlobalOrNull(cx);
3346 : }
3347 :
3348 0 : RootedObject varObj(cx);
3349 0 : RootedObject lexicalScope(cx);
3350 :
3351 : {
3352 : // If we're switching globals here, ExecuteInGlobalAndReturnScope will
3353 : // take care of cloning the script into that compartment before
3354 : // executing it.
3355 0 : AutoCompartment ac(cx, global);
3356 :
3357 0 : if (!js::ExecuteInGlobalAndReturnScope(cx, global, script, &lexicalScope))
3358 0 : return false;
3359 :
3360 0 : varObj = lexicalScope->enclosingEnvironment();
3361 : }
3362 :
3363 0 : RootedObject rv(cx, JS_NewPlainObject(cx));
3364 0 : if (!rv)
3365 0 : return false;
3366 :
3367 0 : RootedValue varObjVal(cx, ObjectValue(*varObj));
3368 0 : if (!cx->compartment()->wrap(cx, &varObjVal))
3369 0 : return false;
3370 0 : if (!JS_SetProperty(cx, rv, "vars", varObjVal))
3371 0 : return false;
3372 :
3373 0 : RootedValue lexicalScopeVal(cx, ObjectValue(*lexicalScope));
3374 0 : if (!cx->compartment()->wrap(cx, &lexicalScopeVal))
3375 0 : return false;
3376 0 : if (!JS_SetProperty(cx, rv, "lexicals", lexicalScopeVal))
3377 0 : return false;
3378 :
3379 0 : args.rval().setObject(*rv);
3380 0 : return true;
3381 : }
3382 :
3383 : static bool
3384 0 : ShellCloneAndExecuteScript(JSContext* cx, unsigned argc, Value* vp)
3385 : {
3386 0 : CallArgs args = CallArgsFromVp(argc, vp);
3387 0 : if (!args.requireAtLeast(cx, "cloneAndExecuteScript", 2))
3388 0 : return false;
3389 :
3390 0 : RootedString str(cx, ToString(cx, args[0]));
3391 0 : if (!str)
3392 0 : return false;
3393 :
3394 0 : RootedObject global(cx, ToObject(cx, args[1]));
3395 0 : if (!global)
3396 0 : return false;
3397 :
3398 0 : AutoStableStringChars strChars(cx);
3399 0 : if (!strChars.initTwoByte(cx, str))
3400 0 : return false;
3401 :
3402 0 : mozilla::Range<const char16_t> chars = strChars.twoByteRange();
3403 0 : size_t srclen = chars.length();
3404 0 : const char16_t* src = chars.begin().get();
3405 :
3406 0 : JS::AutoFilename filename;
3407 : unsigned lineno;
3408 :
3409 0 : JS::DescribeScriptedCaller(cx, &filename, &lineno);
3410 :
3411 0 : JS::CompileOptions options(cx);
3412 0 : options.setFileAndLine(filename.get(), lineno);
3413 0 : options.setNoScriptRval(true);
3414 :
3415 0 : JS::SourceBufferHolder srcBuf(src, srclen, JS::SourceBufferHolder::NoOwnership);
3416 0 : RootedScript script(cx);
3417 0 : if (!JS::Compile(cx, options, srcBuf, &script))
3418 0 : return false;
3419 :
3420 0 : global = CheckedUnwrap(global);
3421 0 : if (!global) {
3422 0 : JS_ReportErrorASCII(cx, "Permission denied to access global");
3423 0 : return false;
3424 : }
3425 0 : if (!global->is<GlobalObject>()) {
3426 0 : JS_ReportErrorASCII(cx, "Argument must be a global object");
3427 0 : return false;
3428 : }
3429 :
3430 0 : AutoCompartment ac(cx, global);
3431 :
3432 0 : JS::RootedValue rval(cx);
3433 0 : if (!JS::CloneAndExecuteScript(cx, script, &rval))
3434 0 : return false;
3435 :
3436 0 : args.rval().setUndefined();
3437 0 : return true;
3438 : }
3439 :
3440 : static bool
3441 0 : IsSimdAvailable(JSContext* cx, unsigned argc, Value* vp)
3442 : {
3443 0 : CallArgs args = CallArgsFromVp(argc, vp);
3444 : #if defined(JS_CODEGEN_NONE) || !defined(ENABLE_SIMD)
3445 : bool available = false;
3446 : #else
3447 0 : bool available = cx->jitSupportsSimd();
3448 : #endif
3449 0 : args.rval().set(BooleanValue(available));
3450 0 : return true;
3451 : }
3452 :
3453 : static bool
3454 0 : ByteSize(JSContext* cx, unsigned argc, Value* vp)
3455 : {
3456 0 : CallArgs args = CallArgsFromVp(argc, vp);
3457 0 : mozilla::MallocSizeOf mallocSizeOf = cx->runtime()->debuggerMallocSizeOf;
3458 :
3459 : {
3460 : // We can't tolerate the GC moving things around while we're using a
3461 : // ubi::Node. Check that nothing we do causes a GC.
3462 0 : JS::AutoCheckCannotGC autoCannotGC;
3463 :
3464 0 : JS::ubi::Node node = args.get(0);
3465 0 : if (node)
3466 0 : args.rval().setNumber(uint32_t(node.size(mallocSizeOf)));
3467 : else
3468 0 : args.rval().setUndefined();
3469 : }
3470 0 : return true;
3471 : }
3472 :
3473 : static bool
3474 0 : ByteSizeOfScript(JSContext*cx, unsigned argc, Value* vp)
3475 : {
3476 0 : CallArgs args = CallArgsFromVp(argc, vp);
3477 0 : if (!args.requireAtLeast(cx, "byteSizeOfScript", 1))
3478 0 : return false;
3479 0 : if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
3480 0 : JS_ReportErrorASCII(cx, "Argument must be a Function object");
3481 0 : return false;
3482 : }
3483 :
3484 0 : RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
3485 0 : if (fun->isNative()) {
3486 0 : JS_ReportErrorASCII(cx, "Argument must be a scripted function");
3487 0 : return false;
3488 : }
3489 :
3490 0 : RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
3491 0 : if (!script)
3492 0 : return false;
3493 :
3494 0 : mozilla::MallocSizeOf mallocSizeOf = cx->runtime()->debuggerMallocSizeOf;
3495 :
3496 : {
3497 : // We can't tolerate the GC moving things around while we're using a
3498 : // ubi::Node. Check that nothing we do causes a GC.
3499 0 : JS::AutoCheckCannotGC autoCannotGC;
3500 :
3501 0 : JS::ubi::Node node = script;
3502 0 : if (node)
3503 0 : args.rval().setNumber(uint32_t(node.size(mallocSizeOf)));
3504 : else
3505 0 : args.rval().setUndefined();
3506 : }
3507 0 : return true;
3508 : }
3509 :
3510 : static bool
3511 0 : SetImmutablePrototype(JSContext* cx, unsigned argc, Value* vp)
3512 : {
3513 0 : CallArgs args = CallArgsFromVp(argc, vp);
3514 0 : if (!args.get(0).isObject()) {
3515 0 : JS_ReportErrorASCII(cx, "setImmutablePrototype: object expected");
3516 0 : return false;
3517 : }
3518 :
3519 0 : RootedObject obj(cx, &args[0].toObject());
3520 :
3521 : bool succeeded;
3522 0 : if (!js::SetImmutablePrototype(cx, obj, &succeeded))
3523 0 : return false;
3524 :
3525 0 : args.rval().setBoolean(succeeded);
3526 0 : return true;
3527 : }
3528 :
3529 : #ifdef DEBUG
3530 : static bool
3531 0 : DumpStringRepresentation(JSContext* cx, unsigned argc, Value* vp)
3532 : {
3533 0 : CallArgs args = CallArgsFromVp(argc, vp);
3534 :
3535 0 : RootedString str(cx, ToString(cx, args.get(0)));
3536 0 : if (!str)
3537 0 : return false;
3538 :
3539 0 : str->dumpRepresentation(stderr, 0);
3540 :
3541 0 : args.rval().setUndefined();
3542 0 : return true;
3543 : }
3544 : #endif
3545 :
3546 : static bool
3547 0 : SetLazyParsingDisabled(JSContext* cx, unsigned argc, Value* vp)
3548 : {
3549 0 : CallArgs args = CallArgsFromVp(argc, vp);
3550 :
3551 0 : bool disable = !args.hasDefined(0) || ToBoolean(args[0]);
3552 0 : cx->compartment()->behaviors().setDisableLazyParsing(disable);
3553 :
3554 0 : args.rval().setUndefined();
3555 0 : return true;
3556 : }
3557 :
3558 : static bool
3559 0 : SetDiscardSource(JSContext* cx, unsigned argc, Value* vp)
3560 : {
3561 0 : CallArgs args = CallArgsFromVp(argc, vp);
3562 :
3563 0 : bool discard = !args.hasDefined(0) || ToBoolean(args[0]);
3564 0 : cx->compartment()->behaviors().setDiscardSource(discard);
3565 :
3566 0 : args.rval().setUndefined();
3567 0 : return true;
3568 : }
3569 :
3570 : static bool
3571 0 : GetConstructorName(JSContext* cx, unsigned argc, Value* vp)
3572 : {
3573 0 : CallArgs args = CallArgsFromVp(argc, vp);
3574 0 : if (!args.requireAtLeast(cx, "getConstructorName", 1))
3575 0 : return false;
3576 :
3577 0 : if (!args[0].isObject()) {
3578 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
3579 : "getConstructorName", "Object",
3580 0 : InformalValueTypeName(args[0]));
3581 0 : return false;
3582 : }
3583 :
3584 0 : RootedAtom name(cx);
3585 0 : RootedObject obj(cx, &args[0].toObject());
3586 0 : if (!JSObject::constructorDisplayAtom(cx, obj, &name))
3587 0 : return false;
3588 :
3589 0 : if (name) {
3590 0 : args.rval().setString(name);
3591 : } else {
3592 0 : args.rval().setNull();
3593 : }
3594 0 : return true;
3595 : }
3596 :
3597 : static bool
3598 0 : AllocationMarker(JSContext* cx, unsigned argc, Value* vp)
3599 : {
3600 0 : CallArgs args = CallArgsFromVp(argc, vp);
3601 :
3602 0 : bool allocateInsideNursery = true;
3603 0 : if (args.length() > 0 && args[0].isObject()) {
3604 0 : RootedObject options(cx, &args[0].toObject());
3605 :
3606 0 : RootedValue nurseryVal(cx);
3607 0 : if (!JS_GetProperty(cx, options, "nursery", &nurseryVal))
3608 0 : return false;
3609 0 : allocateInsideNursery = ToBoolean(nurseryVal);
3610 : }
3611 :
3612 : static const Class cls = { "AllocationMarker" };
3613 :
3614 0 : auto newKind = allocateInsideNursery ? GenericObject : TenuredObject;
3615 0 : RootedObject obj(cx, NewObjectWithGivenProto(cx, &cls, nullptr, newKind));
3616 0 : if (!obj)
3617 0 : return false;
3618 :
3619 0 : args.rval().setObject(*obj);
3620 0 : return true;
3621 : }
3622 :
3623 : namespace gcCallback {
3624 :
3625 : struct MajorGC {
3626 : int32_t depth;
3627 : int32_t phases;
3628 : };
3629 :
3630 : static void
3631 0 : majorGC(JSContext* cx, JSGCStatus status, void* data)
3632 : {
3633 0 : auto info = static_cast<MajorGC*>(data);
3634 0 : if (!(info->phases & (1 << status)))
3635 0 : return;
3636 :
3637 0 : if (info->depth > 0) {
3638 0 : info->depth--;
3639 0 : JS::PrepareForFullGC(cx);
3640 0 : JS::GCForReason(cx, GC_NORMAL, JS::gcreason::API);
3641 0 : info->depth++;
3642 : }
3643 : }
3644 :
3645 : struct MinorGC {
3646 : int32_t phases;
3647 : bool active;
3648 : };
3649 :
3650 : static void
3651 0 : minorGC(JSContext* cx, JSGCStatus status, void* data)
3652 : {
3653 0 : auto info = static_cast<MinorGC*>(data);
3654 0 : if (!(info->phases & (1 << status)))
3655 0 : return;
3656 :
3657 0 : if (info->active) {
3658 0 : info->active = false;
3659 0 : if (cx->zone() && !cx->zone()->isAtomsZone())
3660 0 : cx->runtime()->gc.evictNursery(JS::gcreason::DEBUG_GC);
3661 0 : info->active = true;
3662 : }
3663 : }
3664 :
3665 : // Process global, should really be runtime-local. Also, the final one of these
3666 : // is currently leaked, since they are only deleted when changing.
3667 : MajorGC* prevMajorGC = nullptr;
3668 : MinorGC* prevMinorGC = nullptr;
3669 :
3670 : } /* namespace gcCallback */
3671 :
3672 : static bool
3673 0 : SetGCCallback(JSContext* cx, unsigned argc, Value* vp)
3674 : {
3675 0 : CallArgs args = CallArgsFromVp(argc, vp);
3676 :
3677 0 : if (args.length() != 1) {
3678 0 : JS_ReportErrorASCII(cx, "Wrong number of arguments");
3679 0 : return false;
3680 : }
3681 :
3682 0 : RootedObject opts(cx, ToObject(cx, args[0]));
3683 0 : if (!opts)
3684 0 : return false;
3685 :
3686 0 : RootedValue v(cx);
3687 0 : if (!JS_GetProperty(cx, opts, "action", &v))
3688 0 : return false;
3689 :
3690 0 : JSString* str = JS::ToString(cx, v);
3691 0 : if (!str)
3692 0 : return false;
3693 0 : JSAutoByteString action(cx, str);
3694 0 : if (!action)
3695 0 : return false;
3696 :
3697 0 : int32_t phases = 0;
3698 0 : if ((strcmp(action.ptr(), "minorGC") == 0) || (strcmp(action.ptr(), "majorGC") == 0)) {
3699 0 : if (!JS_GetProperty(cx, opts, "phases", &v))
3700 0 : return false;
3701 0 : if (v.isUndefined()) {
3702 0 : phases = (1 << JSGC_END);
3703 : } else {
3704 0 : JSString* str = JS::ToString(cx, v);
3705 0 : if (!str)
3706 0 : return false;
3707 0 : JSAutoByteString phasesStr(cx, str);
3708 0 : if (!phasesStr)
3709 0 : return false;
3710 :
3711 0 : if (strcmp(phasesStr.ptr(), "begin") == 0)
3712 0 : phases = (1 << JSGC_BEGIN);
3713 0 : else if (strcmp(phasesStr.ptr(), "end") == 0)
3714 0 : phases = (1 << JSGC_END);
3715 0 : else if (strcmp(phasesStr.ptr(), "both") == 0)
3716 0 : phases = (1 << JSGC_BEGIN) | (1 << JSGC_END);
3717 : else {
3718 0 : JS_ReportErrorASCII(cx, "Invalid callback phase");
3719 0 : return false;
3720 : }
3721 : }
3722 : }
3723 :
3724 0 : if (gcCallback::prevMajorGC) {
3725 0 : JS_SetGCCallback(cx, nullptr, nullptr);
3726 0 : js_delete<gcCallback::MajorGC>(gcCallback::prevMajorGC);
3727 0 : gcCallback::prevMajorGC = nullptr;
3728 : }
3729 :
3730 0 : if (gcCallback::prevMinorGC) {
3731 0 : JS_SetGCCallback(cx, nullptr, nullptr);
3732 0 : js_delete<gcCallback::MinorGC>(gcCallback::prevMinorGC);
3733 0 : gcCallback::prevMinorGC = nullptr;
3734 : }
3735 :
3736 0 : if (strcmp(action.ptr(), "minorGC") == 0) {
3737 0 : auto info = js_new<gcCallback::MinorGC>();
3738 0 : if (!info) {
3739 0 : ReportOutOfMemory(cx);
3740 0 : return false;
3741 : }
3742 :
3743 0 : info->phases = phases;
3744 0 : info->active = true;
3745 0 : JS_SetGCCallback(cx, gcCallback::minorGC, info);
3746 0 : } else if (strcmp(action.ptr(), "majorGC") == 0) {
3747 0 : if (!JS_GetProperty(cx, opts, "depth", &v))
3748 0 : return false;
3749 0 : int32_t depth = 1;
3750 0 : if (!v.isUndefined()) {
3751 0 : if (!ToInt32(cx, v, &depth))
3752 0 : return false;
3753 : }
3754 0 : if (depth < 0) {
3755 0 : JS_ReportErrorASCII(cx, "Nesting depth cannot be negative");
3756 0 : return false;
3757 : }
3758 0 : if (depth + gcstats::MAX_PHASE_NESTING > gcstats::Statistics::MAX_SUSPENDED_PHASES) {
3759 0 : JS_ReportErrorASCII(cx, "Nesting depth too large, would overflow");
3760 0 : return false;
3761 : }
3762 :
3763 0 : auto info = js_new<gcCallback::MajorGC>();
3764 0 : if (!info) {
3765 0 : ReportOutOfMemory(cx);
3766 0 : return false;
3767 : }
3768 :
3769 0 : info->phases = phases;
3770 0 : info->depth = depth;
3771 0 : JS_SetGCCallback(cx, gcCallback::majorGC, info);
3772 : } else {
3773 0 : JS_ReportErrorASCII(cx, "Unknown GC callback action");
3774 0 : return false;
3775 : }
3776 :
3777 0 : args.rval().setUndefined();
3778 0 : return true;
3779 : }
3780 :
3781 : static bool
3782 0 : GetLcovInfo(JSContext* cx, unsigned argc, Value* vp)
3783 : {
3784 0 : CallArgs args = CallArgsFromVp(argc, vp);
3785 :
3786 0 : if (args.length() > 1) {
3787 0 : JS_ReportErrorASCII(cx, "Wrong number of arguments");
3788 0 : return false;
3789 : }
3790 :
3791 0 : RootedObject global(cx);
3792 0 : if (args.hasDefined(0)) {
3793 0 : global = ToObject(cx, args[0]);
3794 0 : if (!global) {
3795 0 : JS_ReportErrorASCII(cx, "Permission denied to access global");
3796 0 : return false;
3797 : }
3798 0 : global = CheckedUnwrap(global);
3799 0 : if (!global) {
3800 0 : ReportAccessDenied(cx);
3801 0 : return false;
3802 : }
3803 0 : if (!global->is<GlobalObject>()) {
3804 0 : JS_ReportErrorASCII(cx, "Argument must be a global object");
3805 0 : return false;
3806 : }
3807 : } else {
3808 0 : global = JS::CurrentGlobalOrNull(cx);
3809 : }
3810 :
3811 0 : size_t length = 0;
3812 0 : char* content = nullptr;
3813 : {
3814 0 : AutoCompartment ac(cx, global);
3815 0 : content = js::GetCodeCoverageSummary(cx, &length);
3816 : }
3817 :
3818 0 : if (!content)
3819 0 : return false;
3820 :
3821 0 : JSString* str = JS_NewStringCopyN(cx, content, length);
3822 0 : free(content);
3823 :
3824 0 : if (!str)
3825 0 : return false;
3826 :
3827 0 : args.rval().setString(str);
3828 0 : return true;
3829 : }
3830 :
3831 : #ifdef DEBUG
3832 : static bool
3833 0 : SetRNGState(JSContext* cx, unsigned argc, Value* vp)
3834 : {
3835 0 : CallArgs args = CallArgsFromVp(argc, vp);
3836 0 : if (!args.requireAtLeast(cx, "SetRNGState", 2))
3837 0 : return false;
3838 :
3839 : double d0;
3840 0 : if (!ToNumber(cx, args[0], &d0))
3841 0 : return false;
3842 :
3843 : double d1;
3844 0 : if (!ToNumber(cx, args[1], &d1))
3845 0 : return false;
3846 :
3847 0 : uint64_t seed0 = static_cast<uint64_t>(d0);
3848 0 : uint64_t seed1 = static_cast<uint64_t>(d1);
3849 :
3850 0 : if (seed0 == 0 && seed1 == 0) {
3851 0 : JS_ReportErrorASCII(cx, "RNG requires non-zero seed");
3852 0 : return false;
3853 : }
3854 :
3855 0 : cx->compartment()->ensureRandomNumberGenerator();
3856 0 : cx->compartment()->randomNumberGenerator.ref().setState(seed0, seed1);
3857 :
3858 0 : args.rval().setUndefined();
3859 0 : return true;
3860 : }
3861 : #endif
3862 :
3863 : static ModuleEnvironmentObject*
3864 0 : GetModuleEnvironment(JSContext* cx, HandleValue moduleValue)
3865 : {
3866 0 : RootedModuleObject module(cx, &moduleValue.toObject().as<ModuleObject>());
3867 :
3868 : // Use the initial environment so that tests can check bindings exists
3869 : // before they have been instantiated.
3870 0 : RootedModuleEnvironmentObject env(cx, &module->initialEnvironment());
3871 0 : MOZ_ASSERT(env);
3872 0 : MOZ_ASSERT_IF(module->environment(), module->environment() == env);
3873 :
3874 0 : return env;
3875 : }
3876 :
3877 : static bool
3878 0 : GetModuleEnvironmentNames(JSContext* cx, unsigned argc, Value* vp)
3879 : {
3880 0 : CallArgs args = CallArgsFromVp(argc, vp);
3881 0 : if (args.length() != 1) {
3882 0 : JS_ReportErrorASCII(cx, "Wrong number of arguments");
3883 0 : return false;
3884 : }
3885 :
3886 0 : if (!args[0].isObject() || !args[0].toObject().is<ModuleObject>()) {
3887 0 : JS_ReportErrorASCII(cx, "First argument should be a ModuleObject");
3888 0 : return false;
3889 : }
3890 :
3891 0 : RootedModuleEnvironmentObject env(cx, GetModuleEnvironment(cx, args[0]));
3892 0 : Rooted<IdVector> ids(cx, IdVector(cx));
3893 0 : if (!JS_Enumerate(cx, env, &ids))
3894 0 : return false;
3895 :
3896 0 : uint32_t length = ids.length();
3897 0 : RootedArrayObject array(cx, NewDenseFullyAllocatedArray(cx, length));
3898 0 : if (!array)
3899 0 : return false;
3900 :
3901 0 : array->setDenseInitializedLength(length);
3902 0 : for (uint32_t i = 0; i < length; i++)
3903 0 : array->initDenseElement(i, StringValue(JSID_TO_STRING(ids[i])));
3904 :
3905 0 : args.rval().setObject(*array);
3906 0 : return true;
3907 : }
3908 :
3909 : static bool
3910 0 : GetModuleEnvironmentValue(JSContext* cx, unsigned argc, Value* vp)
3911 : {
3912 0 : CallArgs args = CallArgsFromVp(argc, vp);
3913 0 : if (args.length() != 2) {
3914 0 : JS_ReportErrorASCII(cx, "Wrong number of arguments");
3915 0 : return false;
3916 : }
3917 :
3918 0 : if (!args[0].isObject() || !args[0].toObject().is<ModuleObject>()) {
3919 0 : JS_ReportErrorASCII(cx, "First argument should be a ModuleObject");
3920 0 : return false;
3921 : }
3922 :
3923 0 : if (!args[1].isString()) {
3924 0 : JS_ReportErrorASCII(cx, "Second argument should be a string");
3925 0 : return false;
3926 : }
3927 :
3928 0 : RootedModuleEnvironmentObject env(cx, GetModuleEnvironment(cx, args[0]));
3929 0 : RootedString name(cx, args[1].toString());
3930 0 : RootedId id(cx);
3931 0 : if (!JS_StringToId(cx, name, &id))
3932 0 : return false;
3933 :
3934 0 : return GetProperty(cx, env, env, id, args.rval());
3935 : }
3936 :
3937 : #ifdef DEBUG
3938 : static const char*
3939 0 : AssertionTypeToString(irregexp::RegExpAssertion::AssertionType type)
3940 : {
3941 0 : switch (type) {
3942 : case irregexp::RegExpAssertion::START_OF_LINE:
3943 0 : return "START_OF_LINE";
3944 : case irregexp::RegExpAssertion::START_OF_INPUT:
3945 0 : return "START_OF_INPUT";
3946 : case irregexp::RegExpAssertion::END_OF_LINE:
3947 0 : return "END_OF_LINE";
3948 : case irregexp::RegExpAssertion::END_OF_INPUT:
3949 0 : return "END_OF_INPUT";
3950 : case irregexp::RegExpAssertion::BOUNDARY:
3951 0 : return "BOUNDARY";
3952 : case irregexp::RegExpAssertion::NON_BOUNDARY:
3953 0 : return "NON_BOUNDARY";
3954 : case irregexp::RegExpAssertion::NOT_AFTER_LEAD_SURROGATE:
3955 0 : return "NOT_AFTER_LEAD_SURROGATE";
3956 : case irregexp::RegExpAssertion::NOT_IN_SURROGATE_PAIR:
3957 0 : return "NOT_IN_SURROGATE_PAIR";
3958 : }
3959 0 : MOZ_CRASH("unexpected AssertionType");
3960 : }
3961 :
3962 : static JSObject*
3963 0 : ConvertRegExpTreeToObject(JSContext* cx, irregexp::RegExpTree* tree)
3964 : {
3965 0 : RootedObject obj(cx, JS_NewPlainObject(cx));
3966 0 : if (!obj)
3967 0 : return nullptr;
3968 :
3969 : auto IntProp = [](JSContext* cx, HandleObject obj,
3970 0 : const char* name, int32_t value) {
3971 0 : RootedValue val(cx, Int32Value(value));
3972 0 : return JS_SetProperty(cx, obj, name, val);
3973 : };
3974 :
3975 : auto BooleanProp = [](JSContext* cx, HandleObject obj,
3976 0 : const char* name, bool value) {
3977 0 : RootedValue val(cx, BooleanValue(value));
3978 0 : return JS_SetProperty(cx, obj, name, val);
3979 : };
3980 :
3981 : auto StringProp = [](JSContext* cx, HandleObject obj,
3982 0 : const char* name, const char* value) {
3983 0 : RootedString valueStr(cx, JS_NewStringCopyZ(cx, value));
3984 0 : if (!valueStr)
3985 0 : return false;
3986 :
3987 0 : RootedValue val(cx, StringValue(valueStr));
3988 0 : return JS_SetProperty(cx, obj, name, val);
3989 : };
3990 :
3991 : auto ObjectProp = [](JSContext* cx, HandleObject obj,
3992 0 : const char* name, HandleObject value) {
3993 0 : RootedValue val(cx, ObjectValue(*value));
3994 0 : return JS_SetProperty(cx, obj, name, val);
3995 : };
3996 :
3997 : auto CharVectorProp = [](JSContext* cx, HandleObject obj,
3998 0 : const char* name, const irregexp::CharacterVector& data) {
3999 0 : RootedString valueStr(cx, JS_NewUCStringCopyN(cx, data.begin(), data.length()));
4000 0 : if (!valueStr)
4001 0 : return false;
4002 :
4003 0 : RootedValue val(cx, StringValue(valueStr));
4004 0 : return JS_SetProperty(cx, obj, name, val);
4005 : };
4006 :
4007 : auto TreeProp = [&ObjectProp](JSContext* cx, HandleObject obj,
4008 0 : const char* name, irregexp::RegExpTree* tree) {
4009 0 : RootedObject treeObj(cx, ConvertRegExpTreeToObject(cx, tree));
4010 0 : if (!treeObj)
4011 0 : return false;
4012 0 : return ObjectProp(cx, obj, name, treeObj);
4013 0 : };
4014 :
4015 : auto TreeVectorProp = [&ObjectProp](JSContext* cx, HandleObject obj,
4016 : const char* name,
4017 0 : const irregexp::RegExpTreeVector& nodes) {
4018 0 : size_t len = nodes.length();
4019 0 : RootedObject array(cx, JS_NewArrayObject(cx, len));
4020 0 : if (!array)
4021 0 : return false;
4022 :
4023 0 : for (size_t i = 0; i < len; i++) {
4024 0 : RootedObject child(cx, ConvertRegExpTreeToObject(cx, nodes[i]));
4025 0 : if (!child)
4026 0 : return false;
4027 :
4028 0 : RootedValue childVal(cx, ObjectValue(*child));
4029 0 : if (!JS_SetElement(cx, array, i, childVal))
4030 0 : return false;
4031 : }
4032 0 : return ObjectProp(cx, obj, name, array);
4033 0 : };
4034 :
4035 : auto CharRangesProp = [&ObjectProp](JSContext* cx, HandleObject obj,
4036 : const char* name,
4037 0 : const irregexp::CharacterRangeVector& ranges) {
4038 0 : size_t len = ranges.length();
4039 0 : RootedObject array(cx, JS_NewArrayObject(cx, len));
4040 0 : if (!array)
4041 0 : return false;
4042 :
4043 0 : for (size_t i = 0; i < len; i++) {
4044 0 : const irregexp::CharacterRange& range = ranges[i];
4045 0 : RootedObject rangeObj(cx, JS_NewPlainObject(cx));
4046 0 : if (!rangeObj)
4047 0 : return false;
4048 :
4049 : auto CharProp = [](JSContext* cx, HandleObject obj,
4050 0 : const char* name, char16_t c) {
4051 0 : RootedString valueStr(cx, JS_NewUCStringCopyN(cx, &c, 1));
4052 0 : if (!valueStr)
4053 0 : return false;
4054 0 : RootedValue val(cx, StringValue(valueStr));
4055 0 : return JS_SetProperty(cx, obj, name, val);
4056 : };
4057 :
4058 0 : if (!CharProp(cx, rangeObj, "from", range.from()))
4059 0 : return false;
4060 0 : if (!CharProp(cx, rangeObj, "to", range.to()))
4061 0 : return false;
4062 :
4063 0 : RootedValue rangeVal(cx, ObjectValue(*rangeObj));
4064 0 : if (!JS_SetElement(cx, array, i, rangeVal))
4065 0 : return false;
4066 : }
4067 0 : return ObjectProp(cx, obj, name, array);
4068 0 : };
4069 :
4070 : auto ElemProp = [&ObjectProp](JSContext* cx, HandleObject obj,
4071 0 : const char* name, const irregexp::TextElementVector& elements) {
4072 0 : size_t len = elements.length();
4073 0 : RootedObject array(cx, JS_NewArrayObject(cx, len));
4074 0 : if (!array)
4075 0 : return false;
4076 :
4077 0 : for (size_t i = 0; i < len; i++) {
4078 0 : const irregexp::TextElement& element = elements[i];
4079 0 : RootedObject elemTree(cx, ConvertRegExpTreeToObject(cx, element.tree()));
4080 0 : if (!elemTree)
4081 0 : return false;
4082 :
4083 0 : RootedValue elemTreeVal(cx, ObjectValue(*elemTree));
4084 0 : if (!JS_SetElement(cx, array, i, elemTreeVal))
4085 0 : return false;
4086 : }
4087 0 : return ObjectProp(cx, obj, name, array);
4088 0 : };
4089 :
4090 0 : if (tree->IsDisjunction()) {
4091 0 : if (!StringProp(cx, obj, "type", "Disjunction"))
4092 0 : return nullptr;
4093 0 : irregexp::RegExpDisjunction* t = tree->AsDisjunction();
4094 0 : if (!TreeVectorProp(cx, obj, "alternatives", t->alternatives()))
4095 0 : return nullptr;
4096 0 : return obj;
4097 : }
4098 0 : if (tree->IsAlternative()) {
4099 0 : if (!StringProp(cx, obj, "type", "Alternative"))
4100 0 : return nullptr;
4101 0 : irregexp::RegExpAlternative* t = tree->AsAlternative();
4102 0 : if (!TreeVectorProp(cx, obj, "nodes", t->nodes()))
4103 0 : return nullptr;
4104 0 : return obj;
4105 : }
4106 0 : if (tree->IsAssertion()) {
4107 0 : if (!StringProp(cx, obj, "type", "Assertion"))
4108 0 : return nullptr;
4109 0 : irregexp::RegExpAssertion* t = tree->AsAssertion();
4110 0 : if (!StringProp(cx, obj, "assertion_type", AssertionTypeToString(t->assertion_type())))
4111 0 : return nullptr;
4112 0 : return obj;
4113 : }
4114 0 : if (tree->IsCharacterClass()) {
4115 0 : if (!StringProp(cx, obj, "type", "CharacterClass"))
4116 0 : return nullptr;
4117 0 : irregexp::RegExpCharacterClass* t = tree->AsCharacterClass();
4118 0 : if (!BooleanProp(cx, obj, "is_negated", t->is_negated()))
4119 0 : return nullptr;
4120 0 : LifoAlloc* alloc = &cx->tempLifoAlloc();
4121 0 : if (!CharRangesProp(cx, obj, "ranges", t->ranges(alloc)))
4122 0 : return nullptr;
4123 0 : return obj;
4124 : }
4125 0 : if (tree->IsAtom()) {
4126 0 : if (!StringProp(cx, obj, "type", "Atom"))
4127 0 : return nullptr;
4128 0 : irregexp::RegExpAtom* t = tree->AsAtom();
4129 0 : if (!CharVectorProp(cx, obj, "data", t->data()))
4130 0 : return nullptr;
4131 0 : return obj;
4132 : }
4133 0 : if (tree->IsText()) {
4134 0 : if (!StringProp(cx, obj, "type", "Text"))
4135 0 : return nullptr;
4136 0 : irregexp::RegExpText* t = tree->AsText();
4137 0 : if (!ElemProp(cx, obj, "elements", t->elements()))
4138 0 : return nullptr;
4139 0 : return obj;
4140 : }
4141 0 : if (tree->IsQuantifier()) {
4142 0 : if (!StringProp(cx, obj, "type", "Quantifier"))
4143 0 : return nullptr;
4144 0 : irregexp::RegExpQuantifier* t = tree->AsQuantifier();
4145 0 : if (!IntProp(cx, obj, "min", t->min()))
4146 0 : return nullptr;
4147 0 : if (!IntProp(cx, obj, "max", t->max()))
4148 0 : return nullptr;
4149 0 : if (!StringProp(cx, obj, "quantifier_type",
4150 0 : t->is_possessive() ? "POSSESSIVE"
4151 0 : : t->is_non_greedy() ? "NON_GREEDY"
4152 : : "GREEDY"))
4153 0 : return nullptr;
4154 0 : if (!TreeProp(cx, obj, "body", t->body()))
4155 0 : return nullptr;
4156 0 : return obj;
4157 : }
4158 0 : if (tree->IsCapture()) {
4159 0 : if (!StringProp(cx, obj, "type", "Capture"))
4160 0 : return nullptr;
4161 0 : irregexp::RegExpCapture* t = tree->AsCapture();
4162 0 : if (!IntProp(cx, obj, "index", t->index()))
4163 0 : return nullptr;
4164 0 : if (!TreeProp(cx, obj, "body", t->body()))
4165 0 : return nullptr;
4166 0 : return obj;
4167 : }
4168 0 : if (tree->IsLookahead()) {
4169 0 : if (!StringProp(cx, obj, "type", "Lookahead"))
4170 0 : return nullptr;
4171 0 : irregexp::RegExpLookahead* t = tree->AsLookahead();
4172 0 : if (!BooleanProp(cx, obj, "is_positive", t->is_positive()))
4173 0 : return nullptr;
4174 0 : if (!TreeProp(cx, obj, "body", t->body()))
4175 0 : return nullptr;
4176 0 : return obj;
4177 : }
4178 0 : if (tree->IsBackReference()) {
4179 0 : if (!StringProp(cx, obj, "type", "BackReference"))
4180 0 : return nullptr;
4181 0 : irregexp::RegExpBackReference* t = tree->AsBackReference();
4182 0 : if (!IntProp(cx, obj, "index", t->index()))
4183 0 : return nullptr;
4184 0 : return obj;
4185 : }
4186 0 : if (tree->IsEmpty()) {
4187 0 : if (!StringProp(cx, obj, "type", "Empty"))
4188 0 : return nullptr;
4189 0 : return obj;
4190 : }
4191 :
4192 0 : MOZ_CRASH("unexpected RegExpTree type");
4193 : }
4194 :
4195 : static bool
4196 0 : ParseRegExp(JSContext* cx, unsigned argc, Value* vp)
4197 : {
4198 0 : CallArgs args = CallArgsFromVp(argc, vp);
4199 0 : RootedObject callee(cx, &args.callee());
4200 :
4201 0 : if (args.length() == 0) {
4202 0 : ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
4203 0 : return false;
4204 : }
4205 :
4206 0 : if (!args[0].isString()) {
4207 0 : ReportUsageErrorASCII(cx, callee, "First argument must be a String");
4208 0 : return false;
4209 : }
4210 :
4211 0 : RegExpFlag flags = RegExpFlag(0);
4212 0 : if (!args.get(1).isUndefined()) {
4213 0 : if (!args.get(1).isString()) {
4214 0 : ReportUsageErrorASCII(cx, callee, "Second argument, if present, must be a String");
4215 0 : return false;
4216 : }
4217 0 : RootedString flagStr(cx, args[1].toString());
4218 0 : if (!ParseRegExpFlags(cx, flagStr, &flags))
4219 0 : return false;
4220 : }
4221 :
4222 0 : bool match_only = false;
4223 0 : if (!args.get(2).isUndefined()) {
4224 0 : if (!args.get(2).isBoolean()) {
4225 0 : ReportUsageErrorASCII(cx, callee, "Third argument, if present, must be a Boolean");
4226 0 : return false;
4227 : }
4228 0 : match_only = args[2].toBoolean();
4229 : }
4230 :
4231 0 : RootedAtom pattern(cx, AtomizeString(cx, args[0].toString()));
4232 0 : if (!pattern)
4233 0 : return false;
4234 :
4235 0 : CompileOptions options(cx);
4236 0 : frontend::TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
4237 :
4238 0 : irregexp::RegExpCompileData data;
4239 0 : if (!irregexp::ParsePattern(dummyTokenStream, cx->tempLifoAlloc(), pattern,
4240 0 : flags & MultilineFlag, match_only,
4241 0 : flags & UnicodeFlag, flags & IgnoreCaseFlag,
4242 0 : flags & GlobalFlag, flags & StickyFlag,
4243 0 : &data))
4244 : {
4245 0 : return false;
4246 : }
4247 :
4248 0 : RootedObject obj(cx, ConvertRegExpTreeToObject(cx, data.tree));
4249 0 : if (!obj)
4250 0 : return false;
4251 :
4252 0 : args.rval().setObject(*obj);
4253 0 : return true;
4254 : }
4255 :
4256 : static bool
4257 0 : DisRegExp(JSContext* cx, unsigned argc, Value* vp)
4258 : {
4259 0 : CallArgs args = CallArgsFromVp(argc, vp);
4260 0 : RootedObject callee(cx, &args.callee());
4261 :
4262 0 : if (args.length() == 0) {
4263 0 : ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
4264 0 : return false;
4265 : }
4266 :
4267 0 : if (!args[0].isObject() || !args[0].toObject().is<RegExpObject>()) {
4268 0 : ReportUsageErrorASCII(cx, callee, "First argument must be a RegExp");
4269 0 : return false;
4270 : }
4271 :
4272 0 : Rooted<RegExpObject*> reobj(cx, &args[0].toObject().as<RegExpObject>());
4273 :
4274 0 : bool match_only = false;
4275 0 : if (!args.get(1).isUndefined()) {
4276 0 : if (!args.get(1).isBoolean()) {
4277 0 : ReportUsageErrorASCII(cx, callee, "Second argument, if present, must be a Boolean");
4278 0 : return false;
4279 : }
4280 0 : match_only = args[1].toBoolean();
4281 : }
4282 :
4283 0 : RootedLinearString input(cx, cx->runtime()->emptyString);
4284 0 : if (!args.get(2).isUndefined()) {
4285 0 : if (!args.get(2).isString()) {
4286 0 : ReportUsageErrorASCII(cx, callee, "Third argument, if present, must be a String");
4287 0 : return false;
4288 : }
4289 0 : RootedString inputStr(cx, args[2].toString());
4290 0 : input = inputStr->ensureLinear(cx);
4291 0 : if (!input)
4292 0 : return false;
4293 : }
4294 :
4295 0 : if (!RegExpObject::dumpBytecode(cx, reobj, match_only, input))
4296 0 : return false;
4297 :
4298 0 : args.rval().setUndefined();
4299 0 : return true;
4300 : }
4301 : #endif // DEBUG
4302 :
4303 : static bool
4304 0 : EnableForEach(JSContext* cx, unsigned argc, Value* vp)
4305 : {
4306 0 : CallArgs args = CallArgsFromVp(argc, vp);
4307 0 : JS::ContextOptionsRef(cx).setForEachStatement(true);
4308 0 : args.rval().setUndefined();
4309 0 : return true;
4310 : }
4311 :
4312 : static bool
4313 0 : DisableForEach(JSContext* cx, unsigned argc, Value* vp)
4314 : {
4315 0 : CallArgs args = CallArgsFromVp(argc, vp);
4316 0 : JS::ContextOptionsRef(cx).setForEachStatement(false);
4317 0 : args.rval().setUndefined();
4318 0 : return true;
4319 : }
4320 :
4321 : #if defined(FUZZING) && defined(__AFL_COMPILER)
4322 : static bool
4323 : AflLoop(JSContext* cx, unsigned argc, Value* vp)
4324 : {
4325 : CallArgs args = CallArgsFromVp(argc, vp);
4326 :
4327 : uint32_t max_cnt;
4328 : if (!ToUint32(cx, args.get(0), &max_cnt))
4329 : return false;
4330 :
4331 : args.rval().setBoolean(!!__AFL_LOOP(max_cnt));
4332 : return true;
4333 : }
4334 : #endif
4335 :
4336 : static bool
4337 0 : TimeSinceCreation(JSContext* cx, unsigned argc, Value* vp)
4338 : {
4339 0 : CallArgs args = CallArgsFromVp(argc, vp);
4340 0 : double when = (mozilla::TimeStamp::Now() -
4341 0 : mozilla::TimeStamp::ProcessCreation()).ToMilliseconds();
4342 0 : args.rval().setNumber(when);
4343 0 : return true;
4344 : }
4345 :
4346 : static bool
4347 0 : GetErrorNotes(JSContext* cx, unsigned argc, Value* vp)
4348 : {
4349 0 : CallArgs args = CallArgsFromVp(argc, vp);
4350 0 : if (!args.requireAtLeast(cx, "getErrorNotes", 1))
4351 0 : return false;
4352 :
4353 0 : if (!args[0].isObject() || !args[0].toObject().is<ErrorObject>()) {
4354 0 : args.rval().setNull();
4355 0 : return true;
4356 : }
4357 :
4358 0 : JSErrorReport* report = args[0].toObject().as<ErrorObject>().getErrorReport();
4359 0 : if (!report) {
4360 0 : args.rval().setNull();
4361 0 : return true;
4362 : }
4363 :
4364 0 : RootedObject notesArray(cx, CreateErrorNotesArray(cx, report));
4365 0 : if (!notesArray)
4366 0 : return false;
4367 :
4368 0 : args.rval().setObject(*notesArray);
4369 0 : return true;
4370 : }
4371 :
4372 : static bool
4373 0 : IsConstructor(JSContext* cx, unsigned argc, Value* vp)
4374 : {
4375 0 : CallArgs args = CallArgsFromVp(argc, vp);
4376 0 : if (args.length() < 1)
4377 0 : args.rval().setBoolean(false);
4378 : else
4379 0 : args.rval().setBoolean(IsConstructor(args[0]));
4380 0 : return true;
4381 : }
4382 :
4383 : static const JSFunctionSpecWithHelp TestingFunctions[] = {
4384 : JS_FN_HELP("gc", ::GC, 0, 0,
4385 : "gc([obj] | 'zone' [, 'shrinking'])",
4386 : " Run the garbage collector. When obj is given, GC only its zone.\n"
4387 : " If 'zone' is given, GC any zones that were scheduled for\n"
4388 : " GC via schedulegc.\n"
4389 : " If 'shrinking' is passed as the optional second argument, perform a\n"
4390 : " shrinking GC rather than a normal GC."),
4391 :
4392 : JS_FN_HELP("minorgc", ::MinorGC, 0, 0,
4393 : "minorgc([aboutToOverflow])",
4394 : " Run a minor collector on the Nursery. When aboutToOverflow is true, marks\n"
4395 : " the store buffer as about-to-overflow before collecting."),
4396 :
4397 : JS_FN_HELP("gcparam", GCParameter, 2, 0,
4398 : "gcparam(name [, value])",
4399 : " Wrapper for JS_[GS]etGCParameter. The name is one of:" GC_PARAMETER_ARGS_LIST),
4400 :
4401 : JS_FN_HELP("relazifyFunctions", RelazifyFunctions, 0, 0,
4402 : "relazifyFunctions(...)",
4403 : " Perform a GC and allow relazification of functions. Accepts the same\n"
4404 : " arguments as gc()."),
4405 :
4406 : JS_FN_HELP("getBuildConfiguration", GetBuildConfiguration, 0, 0,
4407 : "getBuildConfiguration()",
4408 : " Return an object describing some of the configuration options SpiderMonkey\n"
4409 : " was built with."),
4410 :
4411 : JS_FN_HELP("hasChild", HasChild, 0, 0,
4412 : "hasChild(parent, child)",
4413 : " Return true if |child| is a child of |parent|, as determined by a call to\n"
4414 : " TraceChildren"),
4415 :
4416 : JS_FN_HELP("setSavedStacksRNGState", SetSavedStacksRNGState, 1, 0,
4417 : "setSavedStacksRNGState(seed)",
4418 : " Set this compartment's SavedStacks' RNG state.\n"),
4419 :
4420 : JS_FN_HELP("getSavedFrameCount", GetSavedFrameCount, 0, 0,
4421 : "getSavedFrameCount()",
4422 : " Return the number of SavedFrame instances stored in this compartment's\n"
4423 : " SavedStacks cache."),
4424 :
4425 : JS_FN_HELP("saveStack", SaveStack, 0, 0,
4426 : "saveStack([maxDepth [, compartment]])",
4427 : " Capture a stack. If 'maxDepth' is given, capture at most 'maxDepth' number\n"
4428 : " of frames. If 'compartment' is given, allocate the js::SavedFrame instances\n"
4429 : " with the given object's compartment."),
4430 :
4431 : JS_FN_HELP("captureFirstSubsumedFrame", CaptureFirstSubsumedFrame, 1, 0,
4432 : "saveStack(object [, shouldIgnoreSelfHosted = true]])",
4433 : " Capture a stack back to the first frame whose principals are subsumed by the\n"
4434 : " object's compartment's principals. If 'shouldIgnoreSelfHosted' is given,\n"
4435 : " control whether self-hosted frames are considered when checking principals."),
4436 :
4437 : JS_FN_HELP("callFunctionFromNativeFrame", CallFunctionFromNativeFrame, 1, 0,
4438 : "callFunctionFromNativeFrame(function)",
4439 : " Call 'function' with a (C++-)native frame on stack.\n"
4440 : " Required for testing that SaveStack properly handles native frames."),
4441 :
4442 : JS_FN_HELP("callFunctionWithAsyncStack", CallFunctionWithAsyncStack, 0, 0,
4443 : "callFunctionWithAsyncStack(function, stack, asyncCause)",
4444 : " Call 'function', using the provided stack as the async stack responsible\n"
4445 : " for the call, and propagate its return value or the exception it throws.\n"
4446 : " The function is called with no arguments, and 'this' is 'undefined'. The\n"
4447 : " specified |asyncCause| is attached to the provided stack frame."),
4448 :
4449 : JS_FN_HELP("enableTrackAllocations", EnableTrackAllocations, 0, 0,
4450 : "enableTrackAllocations()",
4451 : " Start capturing the JS stack at every allocation. Note that this sets an\n"
4452 : " object metadata callback that will override any other object metadata\n"
4453 : " callback that may be set."),
4454 :
4455 : JS_FN_HELP("disableTrackAllocations", DisableTrackAllocations, 0, 0,
4456 : "disableTrackAllocations()",
4457 : " Stop capturing the JS stack at every allocation."),
4458 :
4459 : JS_FN_HELP("newExternalString", NewExternalString, 1, 0,
4460 : "newExternalString(str)",
4461 : " Copies str's chars and returns a new external string."),
4462 :
4463 : JS_FN_HELP("newMaybeExternalString", NewMaybeExternalString, 1, 0,
4464 : "newMaybeExternalString(str)",
4465 : " Like newExternalString but uses the JS_NewMaybeExternalString API."),
4466 :
4467 : JS_FN_HELP("ensureFlatString", EnsureFlatString, 1, 0,
4468 : "ensureFlatString(str)",
4469 : " Ensures str is a flat (null-terminated) string and returns it."),
4470 :
4471 : JS_FN_HELP("representativeStringArray", RepresentativeStringArray, 0, 0,
4472 : "representativeStringArray()",
4473 : " Returns an array of strings that represent the various internal string\n"
4474 : " types and character encodings."),
4475 :
4476 : #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
4477 : JS_FN_HELP("oomThreadTypes", OOMThreadTypes, 0, 0,
4478 : "oomThreadTypes()",
4479 : " Get the number of thread types that can be used as an argument for\n"
4480 : "oomAfterAllocations() and oomAtAllocation()."),
4481 :
4482 : JS_FN_HELP("oomAfterAllocations", OOMAfterAllocations, 2, 0,
4483 : "oomAfterAllocations(count [,threadType])",
4484 : " After 'count' js_malloc memory allocations, fail every following allocation\n"
4485 : " (return nullptr). The optional thread type limits the effect to the\n"
4486 : " specified type of helper thread."),
4487 :
4488 : JS_FN_HELP("oomAtAllocation", OOMAtAllocation, 2, 0,
4489 : "oomAtAllocation(count [,threadType])",
4490 : " After 'count' js_malloc memory allocations, fail the next allocation\n"
4491 : " (return nullptr). The optional thread type limits the effect to the\n"
4492 : " specified type of helper thread."),
4493 :
4494 : JS_FN_HELP("resetOOMFailure", ResetOOMFailure, 0, 0,
4495 : "resetOOMFailure()",
4496 : " Remove the allocation failure scheduled by either oomAfterAllocations() or\n"
4497 : " oomAtAllocation() and return whether any allocation had been caused to fail."),
4498 :
4499 : JS_FN_HELP("oomTest", OOMTest, 0, 0,
4500 : "oomTest(function, [expectExceptionOnFailure = true])",
4501 : " Test that the passed function behaves correctly under OOM conditions by\n"
4502 : " repeatedly executing it and simulating allocation failure at successive\n"
4503 : " allocations until the function completes without seeing a failure.\n"
4504 : " By default this tests that an exception is raised if execution fails, but\n"
4505 : " this can be disabled by passing false as the optional second parameter.\n"
4506 : " This is also disabled when --fuzzing-safe is specified."),
4507 : #endif
4508 :
4509 : JS_FN_HELP("settlePromiseNow", SettlePromiseNow, 1, 0,
4510 : "settlePromiseNow(promise)",
4511 : " 'Settle' a 'promise' immediately. This just marks the promise as resolved\n"
4512 : " with a value of `undefined` and causes the firing of any onPromiseSettled\n"
4513 : " hooks set on Debugger instances that are observing the given promise's\n"
4514 : " global as a debuggee."),
4515 : JS_FN_HELP("getWaitForAllPromise", GetWaitForAllPromise, 1, 0,
4516 : "getWaitForAllPromise(densePromisesArray)",
4517 : " Calls the 'GetWaitForAllPromise' JSAPI function and returns the result\n"
4518 : " Promise."),
4519 : JS_FN_HELP("resolvePromise", ResolvePromise, 2, 0,
4520 : "resolvePromise(promise, resolution)",
4521 : " Resolve a Promise by calling the JSAPI function JS::ResolvePromise."),
4522 : JS_FN_HELP("rejectPromise", RejectPromise, 2, 0,
4523 : "rejectPromise(promise, reason)",
4524 : " Reject a Promise by calling the JSAPI function JS::RejectPromise."),
4525 :
4526 : JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver, 0, 0,
4527 : "makeFinalizeObserver()",
4528 : " Get a special object whose finalization increases the counter returned\n"
4529 : " by the finalizeCount function."),
4530 :
4531 : JS_FN_HELP("finalizeCount", FinalizeCount, 0, 0,
4532 : "finalizeCount()",
4533 : " Return the current value of the finalization counter that is incremented\n"
4534 : " each time an object returned by the makeFinalizeObserver is finalized."),
4535 :
4536 : JS_FN_HELP("resetFinalizeCount", ResetFinalizeCount, 0, 0,
4537 : "resetFinalizeCount()",
4538 : " Reset the value returned by finalizeCount()."),
4539 :
4540 : JS_FN_HELP("gcPreserveCode", GCPreserveCode, 0, 0,
4541 : "gcPreserveCode()",
4542 : " Preserve JIT code during garbage collections."),
4543 :
4544 : #ifdef JS_GC_ZEAL
4545 : JS_FN_HELP("gczeal", GCZeal, 2, 0,
4546 : "gczeal(level, [N])",
4547 : gc::ZealModeHelpText),
4548 :
4549 : JS_FN_HELP("schedulegc", ScheduleGC, 1, 0,
4550 : "schedulegc([num | obj | string])",
4551 : " If num is given, schedule a GC after num allocations.\n"
4552 : " If obj is given, schedule a GC of obj's zone.\n"
4553 : " If string is given, schedule a GC of the string's zone if possible.\n"
4554 : " Returns the number of allocations before the next trigger."),
4555 :
4556 : JS_FN_HELP("selectforgc", SelectForGC, 0, 0,
4557 : "selectforgc(obj1, obj2, ...)",
4558 : " Schedule the given objects to be marked in the next GC slice."),
4559 :
4560 : JS_FN_HELP("verifyprebarriers", VerifyPreBarriers, 0, 0,
4561 : "verifyprebarriers()",
4562 : " Start or end a run of the pre-write barrier verifier."),
4563 :
4564 : JS_FN_HELP("verifypostbarriers", VerifyPostBarriers, 0, 0,
4565 : "verifypostbarriers()",
4566 : " Does nothing (the post-write barrier verifier has been remove)."),
4567 :
4568 : JS_FN_HELP("gcstate", GCState, 0, 0,
4569 : "gcstate()",
4570 : " Report the global GC state."),
4571 :
4572 : JS_FN_HELP("deterministicgc", DeterministicGC, 1, 0,
4573 : "deterministicgc(true|false)",
4574 : " If true, only allow determinstic GCs to run."),
4575 :
4576 : JS_FN_HELP("dumpGCArenaInfo", DumpGCArenaInfo, 0, 0,
4577 : "dumpGCArenaInfo()",
4578 : " Prints information about the different GC things and how they are arranged\n"
4579 : " in arenas.\n"),
4580 : #endif
4581 :
4582 : JS_FN_HELP("startgc", StartGC, 1, 0,
4583 : "startgc([n [, 'shrinking']])",
4584 : " Start an incremental GC and run a slice that processes about n objects.\n"
4585 : " If 'shrinking' is passesd as the optional second argument, perform a\n"
4586 : " shrinking GC rather than a normal GC."),
4587 :
4588 : JS_FN_HELP("gcslice", GCSlice, 1, 0,
4589 : "gcslice([n])",
4590 : " Start or continue an an incremental GC, running a slice that processes about n objects."),
4591 :
4592 : JS_FN_HELP("abortgc", AbortGC, 1, 0,
4593 : "abortgc()",
4594 : " Abort the current incremental GC."),
4595 :
4596 : JS_FN_HELP("fullcompartmentchecks", FullCompartmentChecks, 1, 0,
4597 : "fullcompartmentchecks(true|false)",
4598 : " If true, check for compartment mismatches before every GC."),
4599 :
4600 : JS_FN_HELP("nondeterministicGetWeakMapKeys", NondeterministicGetWeakMapKeys, 1, 0,
4601 : "nondeterministicGetWeakMapKeys(weakmap)",
4602 : " Return an array of the keys in the given WeakMap."),
4603 :
4604 : JS_FN_HELP("internalConst", InternalConst, 1, 0,
4605 : "internalConst(name)",
4606 : " Query an internal constant for the engine. See InternalConst source for\n"
4607 : " the list of constant names."),
4608 :
4609 : JS_FN_HELP("isProxy", IsProxy, 1, 0,
4610 : "isProxy(obj)",
4611 : " If true, obj is a proxy of some sort"),
4612 :
4613 : JS_FN_HELP("dumpHeap", DumpHeap, 1, 0,
4614 : "dumpHeap(['collectNurseryBeforeDump'], [filename])",
4615 : " Dump reachable and unreachable objects to the named file, or to stdout. If\n"
4616 : " 'collectNurseryBeforeDump' is specified, a minor GC is performed first,\n"
4617 : " otherwise objects in the nursery are ignored."),
4618 :
4619 : JS_FN_HELP("terminate", Terminate, 0, 0,
4620 : "terminate()",
4621 : " Terminate JavaScript execution, as if we had run out of\n"
4622 : " memory or been terminated by the slow script dialog."),
4623 :
4624 : JS_FN_HELP("readGeckoProfilingStack", ReadGeckoProfilingStack, 0, 0,
4625 : "readGeckoProfilingStack()",
4626 : " Reads the jit stack using ProfilingFrameIterator."),
4627 :
4628 : JS_FN_HELP("enableOsiPointRegisterChecks", EnableOsiPointRegisterChecks, 0, 0,
4629 : "enableOsiPointRegisterChecks()",
4630 : "Emit extra code to verify live regs at the start of a VM call are not\n"
4631 : "modified before its OsiPoint."),
4632 :
4633 : JS_FN_HELP("displayName", DisplayName, 1, 0,
4634 : "displayName(fn)",
4635 : " Gets the display name for a function, which can possibly be a guessed or\n"
4636 : " inferred name based on where the function was defined. This can be\n"
4637 : " different from the 'name' property on the function."),
4638 :
4639 : JS_FN_HELP("isAsmJSCompilationAvailable", IsAsmJSCompilationAvailable, 0, 0,
4640 : "isAsmJSCompilationAvailable",
4641 : " Returns whether asm.js compilation is currently available or whether it is disabled\n"
4642 : " (e.g., by the debugger)."),
4643 :
4644 : JS_FN_HELP("isSimdAvailable", IsSimdAvailable, 0, 0,
4645 : "isSimdAvailable",
4646 : " Returns true if SIMD extensions are supported on this platform."),
4647 :
4648 : JS_FN_HELP("getJitCompilerOptions", GetJitCompilerOptions, 0, 0,
4649 : "getCompilerOptions()",
4650 : "Return an object describing some of the JIT compiler options.\n"),
4651 :
4652 : JS_FN_HELP("isAsmJSModule", IsAsmJSModule, 1, 0,
4653 : "isAsmJSModule(fn)",
4654 : " Returns whether the given value is a function containing \"use asm\" that has been\n"
4655 : " validated according to the asm.js spec."),
4656 :
4657 : JS_FN_HELP("isAsmJSModuleLoadedFromCache", IsAsmJSModuleLoadedFromCache, 1, 0,
4658 : "isAsmJSModuleLoadedFromCache(fn)",
4659 : " Return whether the given asm.js module function has been loaded directly\n"
4660 : " from the cache. This function throws an error if fn is not a validated asm.js\n"
4661 : " module."),
4662 :
4663 : JS_FN_HELP("isAsmJSFunction", IsAsmJSFunction, 1, 0,
4664 : "isAsmJSFunction(fn)",
4665 : " Returns whether the given value is a nested function in an asm.js module that has been\n"
4666 : " both compile- and link-time validated."),
4667 :
4668 : JS_FN_HELP("wasmIsSupported", WasmIsSupported, 0, 0,
4669 : "wasmIsSupported()",
4670 : " Returns a boolean indicating whether WebAssembly is supported on the current device."),
4671 :
4672 : JS_FN_HELP("wasmTextToBinary", WasmTextToBinary, 1, 0,
4673 : "wasmTextToBinary(str)",
4674 : " Translates the given text wasm module into its binary encoding."),
4675 :
4676 : JS_FN_HELP("wasmBinaryToText", WasmBinaryToText, 1, 0,
4677 : "wasmBinaryToText(bin)",
4678 : " Translates binary encoding to text format"),
4679 :
4680 : JS_FN_HELP("wasmExtractCode", WasmExtractCode, 1, 0,
4681 : "wasmExtractCode(module)",
4682 : " Extracts generated machine code from WebAssembly.Module."),
4683 :
4684 : JS_FN_HELP("isLazyFunction", IsLazyFunction, 1, 0,
4685 : "isLazyFunction(fun)",
4686 : " True if fun is a lazy JSFunction."),
4687 :
4688 : JS_FN_HELP("isRelazifiableFunction", IsRelazifiableFunction, 1, 0,
4689 : "isRelazifiableFunction(fun)",
4690 : " Ture if fun is a JSFunction with a relazifiable JSScript."),
4691 :
4692 : JS_FN_HELP("enableShellAllocationMetadataBuilder", EnableShellAllocationMetadataBuilder, 0, 0,
4693 : "enableShellAllocationMetadataBuilder()",
4694 : " Use ShellAllocationMetadataBuilder to supply metadata for all newly created objects."),
4695 :
4696 : JS_FN_HELP("getAllocationMetadata", GetAllocationMetadata, 1, 0,
4697 : "getAllocationMetadata(obj)",
4698 : " Get the metadata for an object."),
4699 :
4700 : JS_INLINABLE_FN_HELP("bailout", testingFunc_bailout, 0, 0, TestBailout,
4701 : "bailout()",
4702 : " Force a bailout out of ionmonkey (if running in ionmonkey)."),
4703 :
4704 : JS_FN_HELP("bailAfter", testingFunc_bailAfter, 1, 0,
4705 : "bailAfter(number)",
4706 : " Start a counter to bail once after passing the given amount of possible bailout positions in\n"
4707 : " ionmonkey.\n"),
4708 :
4709 :
4710 : JS_FN_HELP("inJit", testingFunc_inJit, 0, 0,
4711 : "inJit()",
4712 : " Returns true when called within (jit-)compiled code. When jit compilation is disabled this\n"
4713 : " function returns an error string. This function returns false in all other cases.\n"
4714 : " Depending on truthiness, you should continue to wait for compilation to happen or stop execution.\n"),
4715 :
4716 : JS_FN_HELP("inIon", testingFunc_inIon, 0, 0,
4717 : "inIon()",
4718 : " Returns true when called within ion. When ion is disabled or when compilation is abnormally\n"
4719 : " slow to start, this function returns an error string. Otherwise, this function returns false.\n"
4720 : " This behaviour ensures that a falsy value means that we are not in ion, but expect a\n"
4721 : " compilation to occur in the future. Conversely, a truthy value means that we are either in\n"
4722 : " ion or that there is litle or no chance of ion ever compiling the current script."),
4723 :
4724 : JS_FN_HELP("assertJitStackInvariants", TestingFunc_assertJitStackInvariants, 0, 0,
4725 : "assertJitStackInvariants()",
4726 : " Iterates the Jit stack and check that stack invariants hold."),
4727 :
4728 : JS_FN_HELP("setJitCompilerOption", SetJitCompilerOption, 2, 0,
4729 : "setCompilerOption(<option>, <number>)",
4730 : " Set a compiler option indexed in JSCompileOption enum to a number.\n"),
4731 :
4732 : JS_FN_HELP("setIonCheckGraphCoherency", SetIonCheckGraphCoherency, 1, 0,
4733 : "setIonCheckGraphCoherency(bool)",
4734 : " Set whether Ion should perform graph consistency (DEBUG-only) assertions. These assertions\n"
4735 : " are valuable and should be generally enabled, however they can be very expensive for large\n"
4736 : " (wasm) programs."),
4737 :
4738 : JS_FN_HELP("serialize", Serialize, 1, 0,
4739 : "serialize(data, [transferables, [policy]])",
4740 : " Serialize 'data' using JS_WriteStructuredClone. Returns a structured\n"
4741 : " clone buffer object. 'policy' may be an options hash. Valid keys:\n"
4742 : " 'SharedArrayBuffer' - either 'allow' (the default) or 'deny'\n"
4743 : " to specify whether SharedArrayBuffers may be serialized.\n"
4744 : "\n"
4745 : " 'scope' - SameProcessSameThread, SameProcessDifferentThread, or\n"
4746 : " DifferentProcess. Determines how some values will be serialized.\n"
4747 : " Clone buffers may only be deserialized with a compatible scope."),
4748 :
4749 : JS_FN_HELP("deserialize", Deserialize, 1, 0,
4750 : "deserialize(clonebuffer[, opts])",
4751 : " Deserialize data generated by serialize. 'opts' is an options hash with one\n"
4752 : " recognized key 'scope', which limits the clone buffers that are considered\n"
4753 : " valid. Allowed values: 'SameProcessSameThread', 'SameProcessDifferentThread',\n"
4754 : " and 'DifferentProcess'. So for example, a DifferentProcess clone buffer\n"
4755 : " may be deserialized in any scope, but a SameProcessSameThread clone buffer\n"
4756 : " cannot be deserialized in a DifferentProcess scope."),
4757 :
4758 : JS_FN_HELP("detachArrayBuffer", DetachArrayBuffer, 1, 0,
4759 : "detachArrayBuffer(buffer)",
4760 : " Detach the given ArrayBuffer object from its memory, i.e. as if it\n"
4761 : " had been transferred to a WebWorker."),
4762 :
4763 : JS_FN_HELP("helperThreadCount", HelperThreadCount, 0, 0,
4764 : "helperThreadCount()",
4765 : " Returns the number of helper threads available for off-thread tasks."),
4766 :
4767 : #ifdef JS_TRACE_LOGGING
4768 : JS_FN_HELP("startTraceLogger", EnableTraceLogger, 0, 0,
4769 : "startTraceLogger()",
4770 : " Start logging this thread.\n"),
4771 :
4772 : JS_FN_HELP("stopTraceLogger", DisableTraceLogger, 0, 0,
4773 : "stopTraceLogger()",
4774 : " Stop logging this thread."),
4775 : #endif
4776 :
4777 : JS_FN_HELP("reportOutOfMemory", ReportOutOfMemory, 0, 0,
4778 : "reportOutOfMemory()",
4779 : " Report OOM, then clear the exception and return undefined. For crash testing."),
4780 :
4781 : JS_FN_HELP("throwOutOfMemory", ThrowOutOfMemory, 0, 0,
4782 : "throwOutOfMemory()",
4783 : " Throw out of memory exception, for OOM handling testing."),
4784 :
4785 : JS_FN_HELP("reportLargeAllocationFailure", ReportLargeAllocationFailure, 0, 0,
4786 : "reportLargeAllocationFailure()",
4787 : " Call the large allocation failure callback, as though a large malloc call failed,\n"
4788 : " then return undefined. In Gecko, this sends a memory pressure notification, which\n"
4789 : " can free up some memory."),
4790 :
4791 : JS_FN_HELP("findPath", FindPath, 2, 0,
4792 : "findPath(start, target)",
4793 : " Return an array describing one of the shortest paths of GC heap edges from\n"
4794 : " |start| to |target|, or |undefined| if |target| is unreachable from |start|.\n"
4795 : " Each element of the array is either of the form:\n"
4796 : " { node: <object or string>, edge: <string describing edge from node> }\n"
4797 : " if the node is a JavaScript object or value; or of the form:\n"
4798 : " { type: <string describing node>, edge: <string describing edge> }\n"
4799 : " if the node is some internal thing that is not a proper JavaScript value\n"
4800 : " (like a shape or a scope chain element). The destination of the i'th array\n"
4801 : " element's edge is the node of the i+1'th array element; the destination of\n"
4802 : " the last array element is implicitly |target|.\n"),
4803 :
4804 : JS_FN_HELP("shortestPaths", ShortestPaths, 3, 0,
4805 : "shortestPaths(start, targets, maxNumPaths)",
4806 : " Return an array of arrays of shortest retaining paths. There is an array of\n"
4807 : " shortest retaining paths for each object in |targets|. The maximum number of\n"
4808 : " paths in each of those arrays is bounded by |maxNumPaths|. Each element in a\n"
4809 : " path is of the form |{ predecessor, edge }|."),
4810 :
4811 : #ifdef DEBUG
4812 : JS_FN_HELP("dumpObject", DumpObject, 1, 0,
4813 : "dumpObject()",
4814 : " Dump an internal representation of an object."),
4815 : #endif
4816 :
4817 : JS_FN_HELP("sharedMemoryEnabled", SharedMemoryEnabled, 0, 0,
4818 : "sharedMemoryEnabled()",
4819 : " Return true if SharedArrayBuffer and Atomics are enabled"),
4820 :
4821 : JS_FN_HELP("sharedArrayRawBufferCount", SharedArrayRawBufferCount, 0, 0,
4822 : "sharedArrayRawBufferCount()",
4823 : " Return the number of live SharedArrayRawBuffer objects"),
4824 :
4825 : JS_FN_HELP("sharedArrayRawBufferRefcount", SharedArrayRawBufferRefcount, 0, 0,
4826 : "sharedArrayRawBufferRefcount(sab)",
4827 : " Return the reference count of the SharedArrayRawBuffer object held by sab"),
4828 :
4829 : #ifdef NIGHTLY_BUILD
4830 : JS_FN_HELP("objectAddress", ObjectAddress, 1, 0,
4831 : "objectAddress(obj)",
4832 : " Return the current address of the object. For debugging only--this\n"
4833 : " address may change during a moving GC."),
4834 :
4835 : JS_FN_HELP("sharedAddress", SharedAddress, 1, 0,
4836 : "sharedAddress(obj)",
4837 : " Return the address of the shared storage of a SharedArrayBuffer."),
4838 : #endif
4839 :
4840 : JS_FN_HELP("evalReturningScope", EvalReturningScope, 1, 0,
4841 : "evalReturningScope(scriptStr, [global])",
4842 : " Evaluate the script in a new scope and return the scope.\n"
4843 : " If |global| is present, clone the script to |global| before executing."),
4844 :
4845 : JS_FN_HELP("cloneAndExecuteScript", ShellCloneAndExecuteScript, 2, 0,
4846 : "cloneAndExecuteScript(source, global)",
4847 : " Compile |source| in the current compartment, clone it into |global|'s\n"
4848 : " compartment, and run it there."),
4849 :
4850 : JS_FN_HELP("backtrace", DumpBacktrace, 1, 0,
4851 : "backtrace()",
4852 : " Dump out a brief backtrace."),
4853 :
4854 : JS_FN_HELP("getBacktrace", GetBacktrace, 1, 0,
4855 : "getBacktrace([options])",
4856 : " Return the current stack as a string. Takes an optional options object,\n"
4857 : " which may contain any or all of the boolean properties\n"
4858 : " options.args - show arguments to each function\n"
4859 : " options.locals - show local variables in each frame\n"
4860 : " options.thisprops - show the properties of the 'this' object of each frame\n"),
4861 :
4862 : JS_FN_HELP("byteSize", ByteSize, 1, 0,
4863 : "byteSize(value)",
4864 : " Return the size in bytes occupied by |value|, or |undefined| if value\n"
4865 : " is not allocated in memory.\n"),
4866 :
4867 : JS_FN_HELP("byteSizeOfScript", ByteSizeOfScript, 1, 0,
4868 : "byteSizeOfScript(f)",
4869 : " Return the size in bytes occupied by the function |f|'s JSScript.\n"),
4870 :
4871 : JS_FN_HELP("setImmutablePrototype", SetImmutablePrototype, 1, 0,
4872 : "setImmutablePrototype(obj)",
4873 : " Try to make obj's [[Prototype]] immutable, such that subsequent attempts to\n"
4874 : " change it will fail. Return true if obj's [[Prototype]] was successfully made\n"
4875 : " immutable (or if it already was immutable), false otherwise. Throws in case\n"
4876 : " of internal error, or if the operation doesn't even make sense (for example,\n"
4877 : " because the object is a revoked proxy)."),
4878 :
4879 : #ifdef DEBUG
4880 : JS_FN_HELP("dumpStringRepresentation", DumpStringRepresentation, 1, 0,
4881 : "dumpStringRepresentation(str)",
4882 : " Print a human-readable description of how the string |str| is represented.\n"),
4883 : #endif
4884 :
4885 : JS_FN_HELP("setLazyParsingDisabled", SetLazyParsingDisabled, 1, 0,
4886 : "setLazyParsingDisabled(bool)",
4887 : " Explicitly disable lazy parsing in the current compartment. The default is that lazy "
4888 : " parsing is not explicitly disabled."),
4889 :
4890 : JS_FN_HELP("setDiscardSource", SetDiscardSource, 1, 0,
4891 : "setDiscardSource(bool)",
4892 : " Explicitly enable source discarding in the current compartment. The default is that "
4893 : " source discarding is not explicitly enabled."),
4894 :
4895 : JS_FN_HELP("getConstructorName", GetConstructorName, 1, 0,
4896 : "getConstructorName(object)",
4897 : " If the given object was created with `new Ctor`, return the constructor's display name. "
4898 : " Otherwise, return null."),
4899 :
4900 : JS_FN_HELP("allocationMarker", AllocationMarker, 0, 0,
4901 : "allocationMarker([options])",
4902 : " Return a freshly allocated object whose [[Class]] name is\n"
4903 : " \"AllocationMarker\". Such objects are allocated only by calls\n"
4904 : " to this function, never implicitly by the system, making them\n"
4905 : " suitable for use in allocation tooling tests. Takes an optional\n"
4906 : " options object which may contain the following properties:\n"
4907 : " * nursery: bool, whether to allocate the object in the nursery\n"),
4908 :
4909 : JS_FN_HELP("setGCCallback", SetGCCallback, 1, 0,
4910 : "setGCCallback({action:\"...\", options...})",
4911 : " Set the GC callback. action may be:\n"
4912 : " 'minorGC' - run a nursery collection\n"
4913 : " 'majorGC' - run a major collection, nesting up to a given 'depth'\n"),
4914 :
4915 : JS_FN_HELP("getLcovInfo", GetLcovInfo, 1, 0,
4916 : "getLcovInfo(global)",
4917 : " Generate LCOV tracefile for the given compartment. If no global are provided then\n"
4918 : " the current global is used as the default one.\n"),
4919 :
4920 : #ifdef DEBUG
4921 : JS_FN_HELP("setRNGState", SetRNGState, 2, 0,
4922 : "setRNGState(seed0, seed1)",
4923 : " Set this compartment's RNG state.\n"),
4924 : #endif
4925 :
4926 : JS_FN_HELP("getModuleEnvironmentNames", GetModuleEnvironmentNames, 1, 0,
4927 : "getModuleEnvironmentNames(module)",
4928 : " Get the list of a module environment's bound names for a specified module.\n"),
4929 :
4930 : JS_FN_HELP("getModuleEnvironmentValue", GetModuleEnvironmentValue, 2, 0,
4931 : "getModuleEnvironmentValue(module, name)",
4932 : " Get the value of a bound name in a module environment.\n"),
4933 :
4934 : JS_FN_HELP("enableForEach", EnableForEach, 0, 0,
4935 : "enableForEach()",
4936 : " Enables the deprecated, non-standard for-each.\n"),
4937 :
4938 : JS_FN_HELP("disableForEach", DisableForEach, 0, 0,
4939 : "disableForEach()",
4940 : " Disables the deprecated, non-standard for-each.\n"),
4941 :
4942 : #if defined(FUZZING) && defined(__AFL_COMPILER)
4943 : JS_FN_HELP("aflloop", AflLoop, 1, 0,
4944 : "aflloop(max_cnt)",
4945 : " Call the __AFL_LOOP() runtime function (see AFL docs)\n"),
4946 : #endif
4947 :
4948 : JS_FN_HELP("timeSinceCreation", TimeSinceCreation, 0, 0,
4949 : "TimeSinceCreation()",
4950 : " Returns the time in milliseconds since process creation.\n"
4951 : " This uses a clock compatible with the profiler.\n"),
4952 :
4953 : JS_FN_HELP("isConstructor", IsConstructor, 1, 0,
4954 : "isConstructor(value)",
4955 : " Returns whether the value is considered IsConstructor.\n"),
4956 :
4957 : JS_FS_HELP_END
4958 3 : };
4959 :
4960 : static const JSFunctionSpecWithHelp FuzzingUnsafeTestingFunctions[] = {
4961 : #ifdef DEBUG
4962 : JS_FN_HELP("parseRegExp", ParseRegExp, 3, 0,
4963 : "parseRegExp(pattern[, flags[, match_only])",
4964 : " Parses a RegExp pattern and returns a tree, potentially throwing."),
4965 :
4966 : JS_FN_HELP("disRegExp", DisRegExp, 3, 0,
4967 : "disRegExp(regexp[, match_only[, input]])",
4968 : " Dumps RegExp bytecode."),
4969 : #endif
4970 :
4971 : JS_FN_HELP("getErrorNotes", GetErrorNotes, 1, 0,
4972 : "getErrorNotes(error)",
4973 : " Returns an array of error notes."),
4974 :
4975 : JS_FS_HELP_END
4976 : };
4977 :
4978 : bool
4979 0 : js::DefineTestingFunctions(JSContext* cx, HandleObject obj, bool fuzzingSafe_,
4980 : bool disableOOMFunctions_)
4981 : {
4982 0 : fuzzingSafe = fuzzingSafe_;
4983 0 : if (EnvVarIsDefined("MOZ_FUZZING_SAFE"))
4984 0 : fuzzingSafe = true;
4985 :
4986 0 : disableOOMFunctions = disableOOMFunctions_;
4987 :
4988 0 : if (!fuzzingSafe) {
4989 0 : if (!JS_DefineFunctionsWithHelp(cx, obj, FuzzingUnsafeTestingFunctions))
4990 0 : return false;
4991 : }
4992 :
4993 0 : return JS_DefineFunctionsWithHelp(cx, obj, TestingFunctions);
4994 : }
|