TestingFunctions.cpp (350106B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "builtin/TestingFunctions.h" 8 9 #include "mozilla/Atomics.h" 10 #include "mozilla/Casting.h" 11 #ifdef JS_HAS_INTL_API 12 # include "mozilla/intl/ICU4CLibrary.h" 13 # include "mozilla/intl/Locale.h" 14 # include "mozilla/intl/String.h" 15 # include "mozilla/intl/TimeZone.h" 16 #endif 17 #include "mozilla/Maybe.h" 18 #include "mozilla/RefPtr.h" 19 #include "mozilla/ScopeExit.h" 20 #include "mozilla/Span.h" 21 #include "mozilla/Sprintf.h" 22 #include "mozilla/StringBuffer.h" 23 #include "mozilla/ThreadLocal.h" 24 25 #include <algorithm> 26 #include <cfloat> 27 #include <cinttypes> 28 #include <cmath> 29 #include <cstdlib> 30 #include <ctime> 31 #include <functional> 32 #include <utility> 33 34 #if defined(XP_UNIX) && !defined(XP_DARWIN) 35 # include <time.h> 36 #else 37 # include <chrono> 38 #endif 39 40 #include "fdlibm.h" 41 #include "jsapi.h" 42 #include "jsfriendapi.h" 43 44 #ifdef JS_HAS_INTL_API 45 # include "builtin/intl/CommonFunctions.h" 46 # include "builtin/intl/FormatBuffer.h" 47 # include "builtin/intl/SharedIntlData.h" 48 #endif 49 #include "builtin/BigInt.h" 50 #include "builtin/JSON.h" 51 #include "builtin/MapObject.h" 52 #include "builtin/Promise.h" 53 #include "builtin/TestingUtility.h" // js::ParseCompileOptions, js::ParseDebugMetadata 54 #include "builtin/WeakMapObject.h" 55 #include "ds/IdValuePair.h" // js::IdValuePair 56 #include "frontend/CompilationStencil.h" // frontend::CompilationStencil 57 #include "frontend/FrontendContext.h" // AutoReportFrontendContext 58 #include "gc/GC.h" 59 #include "gc/GCEnum.h" 60 #include "gc/GCLock.h" 61 #include "gc/Zone.h" 62 #include "jit/BaselineJIT.h" 63 #include "jit/CacheIRSpewer.h" 64 #include "jit/Disassemble.h" 65 #include "jit/FlushICache.h" 66 #include "jit/InlinableNatives.h" 67 #include "jit/Invalidation.h" 68 #include "jit/Ion.h" 69 #include "jit/JitOptions.h" 70 #include "jit/JitRuntime.h" 71 #include "jit/JitScript.h" 72 #include "jit/TrialInlining.h" 73 #include "js/Array.h" // JS::NewArrayObject 74 #include "js/ArrayBuffer.h" // JS::{DetachArrayBuffer,GetArrayBufferLengthAndData,NewArrayBufferWithContents} 75 #include "js/CallAndConstruct.h" // JS::Call, JS::IsCallable, JS::IsConstructor, JS_CallFunction 76 #include "js/CharacterEncoding.h" 77 #include "js/CompilationAndEvaluation.h" 78 #include "js/CompileOptions.h" // JS::CompileOptions, JS::DecodeOptions, JS::InstantiateOptions 79 #include "js/Conversions.h" 80 #include "js/Date.h" 81 #include "js/experimental/CodeCoverage.h" // js::GetCodeCoverageSummary 82 #include "js/experimental/CompileScript.h" // JS::CompileGlobalScriptToStencil, JS::CompileModuleScriptToStencil, JS::PrepareForInstantiate 83 #include "js/experimental/JSStencil.h" // JS::Stencil, JS::EncodeStencil, JS::DecodeStencil, JS::InstantiateGlobalStencil 84 #include "js/experimental/PCCountProfiling.h" // JS::{Start,Stop}PCCountProfiling, JS::PurgePCCounts, JS::GetPCCountScript{Count,Summary,Contents} 85 #include "js/experimental/TypedData.h" // JS_GetObjectAsUint8Array 86 #include "js/friend/DumpFunctions.h" // js::Dump{Backtrace,Heap,Object}, JS::FormatStackDump, js::IgnoreNurseryObjects 87 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* 88 #include "js/friend/WindowProxy.h" // js::ToWindowProxyIfWindow 89 #include "js/GlobalObject.h" 90 #include "js/HashTable.h" 91 #include "js/Interrupt.h" 92 #include "js/LocaleSensitive.h" 93 #include "js/Prefs.h" 94 #include "js/Printf.h" 95 #include "js/PropertyAndElement.h" // JS_DefineProperties, JS_DefineProperty, JS_DefinePropertyById, JS_Enumerate, JS_GetProperty, JS_GetPropertyById, JS_HasProperty, JS_SetElement, JS_SetProperty 96 #include "js/PropertySpec.h" 97 #include "js/SourceText.h" 98 #include "js/StableStringChars.h" 99 #include "js/Stack.h" 100 #include "js/String.h" // JS::GetLinearStringLength, JS::StringToLinearString 101 #include "js/StructuredClone.h" 102 #include "js/Transcoding.h" // JS::TranscodeResult, JS::TranscodeRange, JS::TranscodeBuffer, JS::IsTranscodeFailureResult 103 #include "js/UbiNode.h" 104 #include "js/UbiNodeBreadthFirst.h" 105 #include "js/UbiNodeShortestPaths.h" 106 #include "js/UniquePtr.h" 107 #include "js/Vector.h" 108 #include "js/Wrapper.h" 109 #include "threading/CpuCount.h" 110 #include "util/DifferentialTesting.h" 111 #include "util/StringBuilder.h" 112 #include "util/Text.h" 113 #include "vm/BooleanObject.h" 114 #include "vm/DateObject.h" 115 #include "vm/DateTime.h" 116 #include "vm/ErrorObject.h" 117 #include "vm/GlobalObject.h" 118 #include "vm/HelperThreads.h" 119 #include "vm/HelperThreadState.h" 120 #include "vm/Interpreter.h" 121 #include "vm/JSContext.h" 122 #include "vm/JSObject.h" 123 #include "vm/NumberObject.h" 124 #include "vm/PlainObject.h" // js::PlainObject 125 #include "vm/PromiseObject.h" // js::PromiseObject, js::PromiseSlot_* 126 #include "vm/ProxyObject.h" 127 #include "vm/RealmFuses.h" 128 #include "vm/RuntimeFuses.h" 129 #include "vm/SavedStacks.h" 130 #include "vm/ScopeKind.h" 131 #include "vm/Stack.h" 132 #include "vm/StencilObject.h" // StencilObject, StencilXDRBufferObject 133 #include "vm/StringObject.h" 134 #include "vm/StringType.h" 135 #include "wasm/AsmJS.h" 136 #include "wasm/WasmBaselineCompile.h" 137 #include "wasm/WasmBuiltinModule.h" 138 #include "wasm/WasmDump.h" 139 #include "wasm/WasmFeatures.h" 140 #include "wasm/WasmGcObject.h" 141 #include "wasm/WasmInstance.h" 142 #include "wasm/WasmIonCompile.h" 143 #include "wasm/WasmJS.h" 144 #include "wasm/WasmModule.h" 145 #include "wasm/WasmValType.h" 146 #include "wasm/WasmValue.h" 147 148 #include "debugger/DebugAPI-inl.h" 149 #include "gc/AtomMarking-inl.h" 150 #include "jit/JitHints-inl.h" 151 #include "vm/Compartment-inl.h" 152 #include "vm/EnvironmentObject-inl.h" 153 #include "vm/JSContext-inl.h" 154 #include "vm/JSObject-inl.h" 155 #include "vm/NativeObject-inl.h" 156 #include "vm/ObjectFlags-inl.h" 157 #include "vm/StringType-inl.h" 158 #include "wasm/WasmInstance-inl.h" 159 160 using namespace js; 161 162 using mozilla::AssertedCast; 163 using mozilla::AsWritableChars; 164 using mozilla::Maybe; 165 using mozilla::Some; 166 using mozilla::Span; 167 168 using JS::AutoStableStringChars; 169 using JS::CompileOptions; 170 using JS::SliceBudget; 171 using JS::SourceText; 172 using JS::WorkBudget; 173 174 // If fuzzingSafe is set, remove functionality that could cause problems with 175 // fuzzers. Set this via the environment variable MOZ_FUZZING_SAFE. 176 mozilla::Atomic<bool> js::fuzzingSafe(false); 177 178 // If disableOOMFunctions is set, disable functionality that causes artificial 179 // OOM conditions. 180 static mozilla::Atomic<bool> disableOOMFunctions(false); 181 182 static bool EnvVarIsDefined(const char* name) { 183 const char* value = getenv(name); 184 return value && *value; 185 } 186 187 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT) 188 static bool EnvVarAsInt(const char* name, int* valueOut) { 189 if (!EnvVarIsDefined(name)) { 190 return false; 191 } 192 193 *valueOut = atoi(getenv(name)); 194 return true; 195 } 196 #endif 197 198 static bool GetRealmConfiguration(JSContext* cx, unsigned argc, Value* vp) { 199 CallArgs args = CallArgsFromVp(argc, vp); 200 RootedObject callee(cx, &args.callee()); 201 RootedObject info(cx, JS_NewPlainObject(cx)); 202 if (!info) { 203 return false; 204 } 205 if (args.length() > 1) { 206 ReportUsageErrorASCII(cx, callee, "Must have zero or one arguments"); 207 return false; 208 } 209 if (args.length() == 1 && !args[0].isString()) { 210 ReportUsageErrorASCII(cx, callee, "Argument must be a string"); 211 return false; 212 } 213 214 if (args.length() == 1) { 215 RootedString str(cx, ToString(cx, args[0])); 216 if (!str) { 217 return false; 218 } 219 RootedId id(cx); 220 if (!JS_StringToId(cx, str, &id)) { 221 return false; 222 } 223 224 bool hasProperty; 225 if (JS_HasPropertyById(cx, info, id, &hasProperty) && hasProperty) { 226 // Returning a true/false from GetProperty 227 return GetProperty(cx, info, info, id, args.rval()); 228 } 229 230 ReportUsageErrorASCII(cx, callee, "Invalid option name"); 231 return false; 232 } 233 234 args.rval().setObject(*info); 235 return true; 236 } 237 238 static bool GetBuildConfiguration(JSContext* cx, unsigned argc, Value* vp) { 239 CallArgs args = CallArgsFromVp(argc, vp); 240 RootedObject callee(cx, &args.callee()); 241 RootedObject info(cx, JS_NewPlainObject(cx)); 242 if (!info) { 243 return false; 244 } 245 if (args.length() > 1) { 246 ReportUsageErrorASCII(cx, callee, "Must have zero or one arguments"); 247 return false; 248 } 249 if (args.length() == 1 && !args[0].isString()) { 250 ReportUsageErrorASCII(cx, callee, "Argument must be a string"); 251 return false; 252 } 253 254 if (!JS_SetProperty(cx, info, "rooting-analysis", FalseHandleValue)) { 255 return false; 256 } 257 258 if (!JS_SetProperty(cx, info, "exact-rooting", TrueHandleValue)) { 259 return false; 260 } 261 262 if (!JS_SetProperty(cx, info, "trace-jscalls-api", FalseHandleValue)) { 263 return false; 264 } 265 266 if (!JS_SetProperty(cx, info, "incremental-gc", TrueHandleValue)) { 267 return false; 268 } 269 270 if (!JS_SetProperty(cx, info, "generational-gc", TrueHandleValue)) { 271 return false; 272 } 273 274 if (!JS_SetProperty(cx, info, "oom-backtraces", FalseHandleValue)) { 275 return false; 276 } 277 278 RootedValue value(cx); 279 #ifdef DEBUG 280 value = BooleanValue(true); 281 #else 282 value = BooleanValue(false); 283 #endif 284 if (!JS_SetProperty(cx, info, "debug", value)) { 285 return false; 286 } 287 288 #ifdef RELEASE_OR_BETA 289 value = BooleanValue(true); 290 #else 291 value = BooleanValue(false); 292 #endif 293 if (!JS_SetProperty(cx, info, "release_or_beta", value)) { 294 return false; 295 } 296 297 #ifdef EARLY_BETA_OR_EARLIER 298 value = BooleanValue(true); 299 #else 300 value = BooleanValue(false); 301 #endif 302 if (!JS_SetProperty(cx, info, "early_beta_or_earlier", value)) { 303 return false; 304 } 305 306 #ifdef MOZ_CODE_COVERAGE 307 value = BooleanValue(true); 308 #else 309 value = BooleanValue(false); 310 #endif 311 if (!JS_SetProperty(cx, info, "coverage", value)) { 312 return false; 313 } 314 315 #ifdef JS_HAS_CTYPES 316 value = BooleanValue(true); 317 #else 318 value = BooleanValue(false); 319 #endif 320 if (!JS_SetProperty(cx, info, "has-ctypes", value)) { 321 return false; 322 } 323 324 #if defined(JS_CODEGEN_X86) 325 value = BooleanValue(true); 326 #else 327 value = BooleanValue(false); 328 #endif 329 if (!JS_SetProperty(cx, info, "x86", value)) { 330 return false; 331 } 332 333 #if defined(JS_CODEGEN_X64) 334 value = BooleanValue(true); 335 #else 336 value = BooleanValue(false); 337 #endif 338 if (!JS_SetProperty(cx, info, "x64", value)) { 339 return false; 340 } 341 342 #ifdef JS_CODEGEN_ARM 343 value = BooleanValue(true); 344 #else 345 value = BooleanValue(false); 346 #endif 347 if (!JS_SetProperty(cx, info, "arm", value)) { 348 return false; 349 } 350 351 #ifdef JS_SIMULATOR_ARM 352 value = BooleanValue(true); 353 #else 354 value = BooleanValue(false); 355 #endif 356 if (!JS_SetProperty(cx, info, "arm-simulator", value)) { 357 return false; 358 } 359 360 #ifdef ANDROID 361 value = BooleanValue(true); 362 #else 363 value = BooleanValue(false); 364 #endif 365 if (!JS_SetProperty(cx, info, "android", value)) { 366 return false; 367 } 368 369 #ifdef XP_WIN 370 value = BooleanValue(true); 371 #else 372 value = BooleanValue(false); 373 #endif 374 if (!JS_SetProperty(cx, info, "windows", value)) { 375 return false; 376 } 377 378 #ifdef XP_MACOSX 379 value = BooleanValue(true); 380 #else 381 value = BooleanValue(false); 382 #endif 383 if (!JS_SetProperty(cx, info, "osx", value)) { 384 return false; 385 } 386 387 #ifdef JS_CODEGEN_ARM64 388 value = BooleanValue(true); 389 #else 390 value = BooleanValue(false); 391 #endif 392 if (!JS_SetProperty(cx, info, "arm64", value)) { 393 return false; 394 } 395 396 #ifdef JS_SIMULATOR_ARM64 397 value = BooleanValue(true); 398 #else 399 value = BooleanValue(false); 400 #endif 401 if (!JS_SetProperty(cx, info, "arm64-simulator", value)) { 402 return false; 403 } 404 405 #ifdef JS_CODEGEN_MIPS64 406 value = BooleanValue(true); 407 #else 408 value = BooleanValue(false); 409 #endif 410 if (!JS_SetProperty(cx, info, "mips64", value)) { 411 return false; 412 } 413 414 #ifdef JS_SIMULATOR_MIPS64 415 value = BooleanValue(true); 416 #else 417 value = BooleanValue(false); 418 #endif 419 if (!JS_SetProperty(cx, info, "mips64-simulator", value)) { 420 return false; 421 } 422 423 #ifdef JS_SIMULATOR 424 value = BooleanValue(true); 425 #else 426 value = BooleanValue(false); 427 #endif 428 if (!JS_SetProperty(cx, info, "simulator", value)) { 429 return false; 430 } 431 432 #ifdef __wasi__ 433 value = BooleanValue(true); 434 #else 435 value = BooleanValue(false); 436 #endif 437 if (!JS_SetProperty(cx, info, "wasi", value)) { 438 return false; 439 } 440 441 #ifdef ENABLE_PORTABLE_BASELINE_INTERP 442 value = BooleanValue(true); 443 #else 444 value = BooleanValue(false); 445 #endif 446 if (!JS_SetProperty(cx, info, "pbl", value)) { 447 return false; 448 } 449 450 #ifdef JS_CODEGEN_LOONG64 451 value = BooleanValue(true); 452 #else 453 value = BooleanValue(false); 454 #endif 455 if (!JS_SetProperty(cx, info, "loong64", value)) { 456 return false; 457 } 458 459 #ifdef JS_SIMULATOR_LOONG64 460 value = BooleanValue(true); 461 #else 462 value = BooleanValue(false); 463 #endif 464 if (!JS_SetProperty(cx, info, "loong64-simulator", value)) { 465 return false; 466 } 467 468 #ifdef JS_CODEGEN_RISCV64 469 value = BooleanValue(true); 470 #else 471 value = BooleanValue(false); 472 #endif 473 if (!JS_SetProperty(cx, info, "riscv64", value)) { 474 return false; 475 } 476 477 #ifdef JS_SIMULATOR_RISCV64 478 value = BooleanValue(true); 479 #else 480 value = BooleanValue(false); 481 #endif 482 if (!JS_SetProperty(cx, info, "riscv64-simulator", value)) { 483 return false; 484 } 485 486 #ifdef MOZ_ASAN 487 value = BooleanValue(true); 488 #else 489 value = BooleanValue(false); 490 #endif 491 if (!JS_SetProperty(cx, info, "asan", value)) { 492 return false; 493 } 494 495 #ifdef MOZ_TSAN 496 value = BooleanValue(true); 497 #else 498 value = BooleanValue(false); 499 #endif 500 if (!JS_SetProperty(cx, info, "tsan", value)) { 501 return false; 502 } 503 504 #ifdef MOZ_UBSAN 505 value = BooleanValue(true); 506 #else 507 value = BooleanValue(false); 508 #endif 509 if (!JS_SetProperty(cx, info, "ubsan", value)) { 510 return false; 511 } 512 513 #ifdef JS_GC_ZEAL 514 value = BooleanValue(true); 515 #else 516 value = BooleanValue(false); 517 #endif 518 if (!JS_SetProperty(cx, info, "has-gczeal", value)) { 519 return false; 520 } 521 522 #ifdef MOZ_PROFILING 523 value = BooleanValue(true); 524 #else 525 value = BooleanValue(false); 526 #endif 527 if (!JS_SetProperty(cx, info, "profiling", value)) { 528 return false; 529 } 530 531 #ifdef MOZ_VALGRIND 532 value = BooleanValue(true); 533 #else 534 value = BooleanValue(false); 535 #endif 536 if (!JS_SetProperty(cx, info, "valgrind", value)) { 537 return false; 538 } 539 540 #ifdef JS_HAS_INTL_API 541 value = BooleanValue(true); 542 #else 543 value = BooleanValue(false); 544 #endif 545 if (!JS_SetProperty(cx, info, "intl-api", value)) { 546 return false; 547 } 548 549 #if defined(SOLARIS) 550 value = BooleanValue(false); 551 #else 552 value = BooleanValue(true); 553 #endif 554 if (!JS_SetProperty(cx, info, "mapped-array-buffer", value)) { 555 return false; 556 } 557 558 #ifdef MOZ_MEMORY 559 value = BooleanValue(true); 560 #else 561 value = BooleanValue(false); 562 #endif 563 if (!JS_SetProperty(cx, info, "moz-memory", value)) { 564 return false; 565 } 566 567 value.setInt32(sizeof(void*)); 568 if (!JS_SetProperty(cx, info, "pointer-byte-size", value)) { 569 return false; 570 } 571 572 #ifdef ENABLE_DECORATORS 573 value = BooleanValue(true); 574 #else 575 value = BooleanValue(false); 576 #endif 577 if (!JS_SetProperty(cx, info, "decorators", value)) { 578 return false; 579 } 580 581 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT 582 value = BooleanValue(true); 583 #else 584 value = BooleanValue(false); 585 #endif 586 if (!JS_SetProperty(cx, info, "explicit-resource-management", value)) { 587 return false; 588 } 589 590 #ifdef FUZZING 591 value = BooleanValue(true); 592 #else 593 value = BooleanValue(false); 594 #endif 595 if (!JS_SetProperty(cx, info, "fuzzing-defined", value)) { 596 return false; 597 } 598 599 #if (defined(__GNUC__) && defined(__SSE__) && defined(__x86_64__)) || \ 600 defined(__arm__) || defined(__aarch64__) 601 // See js.cpp "disable-main-thread-denormals" command line option. 602 value = BooleanValue(true); 603 #else 604 value = BooleanValue(false); 605 #endif 606 if (!JS_SetProperty(cx, info, "can-disable-main-thread-denormals", value)) { 607 return false; 608 } 609 610 value = Int32Value(JSFatInlineString::MAX_LENGTH_LATIN1); 611 if (!JS_SetProperty(cx, info, "inline-latin1-chars", value)) { 612 return false; 613 } 614 615 value = Int32Value(JSFatInlineString::MAX_LENGTH_TWO_BYTE); 616 if (!JS_SetProperty(cx, info, "inline-two-byte-chars", value)) { 617 return false; 618 } 619 620 value = Int32Value(JSThinInlineString::MAX_LENGTH_LATIN1); 621 if (!JS_SetProperty(cx, info, "thin-inline-latin1-chars", value)) { 622 return false; 623 } 624 625 value = Int32Value(JSThinInlineString::MAX_LENGTH_TWO_BYTE); 626 if (!JS_SetProperty(cx, info, "thin-inline-two-byte-chars", value)) { 627 return false; 628 } 629 630 if (js::ThinInlineAtom::EverInstantiated) { 631 value = Int32Value(js::ThinInlineAtom::MAX_LENGTH_LATIN1); 632 if (!JS_SetProperty(cx, info, "thin-inline-atom-latin1-chars", value)) { 633 return false; 634 } 635 636 value = Int32Value(js::ThinInlineAtom::MAX_LENGTH_TWO_BYTE); 637 if (!JS_SetProperty(cx, info, "thin-inline-atom-two-byte-chars", value)) { 638 return false; 639 } 640 } 641 642 value = Int32Value(js::FatInlineAtom::MAX_LENGTH_LATIN1); 643 if (!JS_SetProperty(cx, info, "fat-inline-atom-latin1-chars", value)) { 644 return false; 645 } 646 647 value = Int32Value(js::FatInlineAtom::MAX_LENGTH_TWO_BYTE); 648 if (!JS_SetProperty(cx, info, "fat-inline-atom-two-byte-chars", value)) { 649 return false; 650 } 651 652 if (args.length() == 1) { 653 RootedString str(cx, ToString(cx, args[0])); 654 if (!str) { 655 return false; 656 } 657 RootedId id(cx); 658 if (!JS_StringToId(cx, str, &id)) { 659 return false; 660 } 661 662 bool hasProperty; 663 if (JS_HasPropertyById(cx, info, id, &hasProperty) && hasProperty) { 664 // Returning a true/false from GetProperty 665 return GetProperty(cx, info, info, id, args.rval()); 666 } 667 668 ReportUsageErrorASCII(cx, callee, "Invalid option name"); 669 return false; 670 } 671 672 args.rval().setObject(*info); 673 return true; 674 } 675 676 static bool IsLCovEnabled(JSContext* cx, unsigned argc, Value* vp) { 677 CallArgs args = CallArgsFromVp(argc, vp); 678 args.rval().setBoolean(coverage::IsLCovEnabled()); 679 return true; 680 } 681 682 static bool TrialInline(JSContext* cx, unsigned argc, Value* vp) { 683 CallArgs args = CallArgsFromVp(argc, vp); 684 args.rval().setUndefined(); 685 686 FrameIter iter(cx); 687 if (iter.done() || !iter.isBaseline() || iter.realm() != cx->realm()) { 688 return true; 689 } 690 691 jit::BaselineFrame* frame = iter.abstractFramePtr().asBaselineFrame(); 692 if (!jit::CanIonCompileScript(cx, frame->script())) { 693 return true; 694 } 695 696 return jit::DoTrialInlining(cx, frame); 697 } 698 699 static bool ReturnStringCopy(JSContext* cx, CallArgs& args, 700 const char* message) { 701 JSString* str = JS_NewStringCopyZ(cx, message); 702 if (!str) { 703 return false; 704 } 705 706 args.rval().setString(str); 707 return true; 708 } 709 710 static bool MaybeGC(JSContext* cx, unsigned argc, Value* vp) { 711 CallArgs args = CallArgsFromVp(argc, vp); 712 JS_MaybeGC(cx); 713 args.rval().setUndefined(); 714 return true; 715 } 716 717 static bool GC(JSContext* cx, unsigned argc, Value* vp) { 718 CallArgs args = CallArgsFromVp(argc, vp); 719 720 /* 721 * If the first argument is 'zone', we collect any zones previously 722 * scheduled for GC via schedulegc. If the first argument is an object, we 723 * collect the object's zone (and any other zones scheduled for 724 * GC). Otherwise, we collect all zones. 725 */ 726 bool zone = false; 727 if (args.length() >= 1) { 728 Value arg = args[0]; 729 if (arg.isString()) { 730 if (!JS_StringEqualsLiteral(cx, arg.toString(), "zone", &zone)) { 731 return false; 732 } 733 } else if (arg.isObject()) { 734 PrepareZoneForGC(cx, UncheckedUnwrap(&arg.toObject())->zone()); 735 zone = true; 736 } 737 } 738 739 JS::GCOptions options = JS::GCOptions::Normal; 740 JS::GCReason reason = JS::GCReason::API; 741 if (args.length() >= 2) { 742 Value arg = args[1]; 743 if (arg.isString()) { 744 bool shrinking = false; 745 bool last_ditch = false; 746 if (!JS_StringEqualsLiteral(cx, arg.toString(), "shrinking", 747 &shrinking)) { 748 return false; 749 } 750 if (!JS_StringEqualsLiteral(cx, arg.toString(), "last-ditch", 751 &last_ditch)) { 752 return false; 753 } 754 if (shrinking) { 755 options = JS::GCOptions::Shrink; 756 } else if (last_ditch) { 757 options = JS::GCOptions::Shrink; 758 reason = JS::GCReason::LAST_DITCH; 759 } 760 } 761 } 762 763 size_t preBytes = cx->runtime()->gc.heapSize.bytes(); 764 765 if (zone) { 766 PrepareForDebugGC(cx->runtime()); 767 } else { 768 JS::PrepareForFullGC(cx); 769 } 770 771 JS::NonIncrementalGC(cx, options, reason); 772 773 char buf[256] = {'\0'}; 774 if (!js::SupportDifferentialTesting()) { 775 SprintfLiteral(buf, "before %zu, after %zu\n", preBytes, 776 cx->runtime()->gc.heapSize.bytes()); 777 } 778 return ReturnStringCopy(cx, args, buf); 779 } 780 781 static bool MinorGC(JSContext* cx, unsigned argc, Value* vp) { 782 CallArgs args = CallArgsFromVp(argc, vp); 783 if (args.get(0) == BooleanValue(true)) { 784 gc::GCRuntime& gc = cx->runtime()->gc; 785 if (gc.nursery().isEnabled()) { 786 gc.storeBuffer().setAboutToOverflow(JS::GCReason::FULL_GENERIC_BUFFER); 787 } 788 } 789 790 cx->minorGC(JS::GCReason::API); 791 args.rval().setUndefined(); 792 return true; 793 } 794 795 #define PARAM_NAME_LIST_ENTRY(name, key, writable) " " name 796 #define GC_PARAMETER_ARGS_LIST FOR_EACH_GC_PARAM(PARAM_NAME_LIST_ENTRY) 797 798 static bool GCParameter(JSContext* cx, unsigned argc, Value* vp) { 799 CallArgs args = CallArgsFromVp(argc, vp); 800 801 JSString* str = ToString(cx, args.get(0)); 802 if (!str) { 803 return false; 804 } 805 806 UniqueChars name = EncodeLatin1(cx, str); 807 if (!name) { 808 return false; 809 } 810 811 JSGCParamKey param; 812 bool writable; 813 if (!GetGCParameterInfo(name.get(), ¶m, &writable)) { 814 JS_ReportErrorASCII( 815 cx, "the first argument must be one of:" GC_PARAMETER_ARGS_LIST); 816 return false; 817 } 818 819 // Request mode. 820 if (args.length() == 1) { 821 uint32_t value = JS_GetGCParameter(cx, param); 822 args.rval().setNumber(value); 823 return true; 824 } 825 826 if (!writable) { 827 JS_ReportErrorASCII(cx, "Attempt to change read-only parameter %s", 828 name.get()); 829 return false; 830 } 831 832 if (fuzzingSafe) { 833 // Some Params are not yet fuzzing safe and so we silently skip 834 // changing said parameters. 835 switch (param) { 836 case JSGC_SEMISPACE_NURSERY_ENABLED: 837 args.rval().setUndefined(); 838 return true; 839 default: 840 break; 841 } 842 } 843 844 if (disableOOMFunctions) { 845 switch (param) { 846 case JSGC_MAX_BYTES: 847 case JSGC_MAX_NURSERY_BYTES: 848 args.rval().setUndefined(); 849 return true; 850 default: 851 break; 852 } 853 } 854 855 double d; 856 if (!ToNumber(cx, args[1], &d)) { 857 return false; 858 } 859 860 if (d < 0 || d > UINT32_MAX) { 861 JS_ReportErrorASCII(cx, "Parameter value out of range"); 862 return false; 863 } 864 865 uint32_t value = floor(d); 866 bool ok = cx->runtime()->gc.setParameter(cx, param, value); 867 if (!ok) { 868 JS_ReportErrorASCII(cx, "Parameter value out of range"); 869 return false; 870 } 871 872 args.rval().setUndefined(); 873 return true; 874 } 875 876 static bool FinishBackgroundFree(JSContext* cx, unsigned argc, Value* vp) { 877 CallArgs args = CallArgsFromVp(argc, vp); 878 cx->runtime()->gc.waitBackgroundFreeEnd(); 879 args.rval().setUndefined(); 880 return true; 881 } 882 883 static bool RelazifyFunctions(JSContext* cx, unsigned argc, Value* vp) { 884 // Relazifying functions on GC is usually only done for compartments that are 885 // not active. To aid fuzzing, this testing function allows us to relazify 886 // even if the compartment is active. 887 888 CallArgs args = CallArgsFromVp(argc, vp); 889 890 // Disable relazification of all scripts on stack. It is a pervasive 891 // assumption in the engine that running scripts still have bytecode. 892 for (AllScriptFramesIter i(cx); !i.done(); ++i) { 893 i.script()->clearAllowRelazify(); 894 } 895 896 cx->runtime()->allowRelazificationForTesting = true; 897 898 JS::PrepareForFullGC(cx); 899 JS::NonIncrementalGC(cx, JS::GCOptions::Shrink, JS::GCReason::API); 900 901 cx->runtime()->allowRelazificationForTesting = false; 902 903 args.rval().setUndefined(); 904 return true; 905 } 906 907 static bool IsProxy(JSContext* cx, unsigned argc, Value* vp) { 908 CallArgs args = CallArgsFromVp(argc, vp); 909 if (args.length() != 1) { 910 JS_ReportErrorASCII(cx, "the function takes exactly one argument"); 911 return false; 912 } 913 if (!args[0].isObject()) { 914 args.rval().setBoolean(false); 915 return true; 916 } 917 args.rval().setBoolean(args[0].toObject().is<ProxyObject>()); 918 return true; 919 } 920 921 static bool WasmIsSupported(JSContext* cx, unsigned argc, Value* vp) { 922 CallArgs args = CallArgsFromVp(argc, vp); 923 args.rval().setBoolean(wasm::HasSupport(cx) && 924 wasm::AnyCompilerAvailable(cx)); 925 return true; 926 } 927 928 static bool WasmIsSupportedByHardware(JSContext* cx, unsigned argc, Value* vp) { 929 CallArgs args = CallArgsFromVp(argc, vp); 930 args.rval().setBoolean(wasm::HasPlatformSupport()); 931 return true; 932 } 933 934 static bool WasmDebuggingEnabled(JSContext* cx, unsigned argc, Value* vp) { 935 CallArgs args = CallArgsFromVp(argc, vp); 936 args.rval().setBoolean(wasm::HasSupport(cx) && wasm::BaselineAvailable(cx)); 937 return true; 938 } 939 940 static bool WasmStreamingEnabled(JSContext* cx, unsigned argc, Value* vp) { 941 CallArgs args = CallArgsFromVp(argc, vp); 942 args.rval().setBoolean(wasm::StreamingCompilationAvailable(cx)); 943 return true; 944 } 945 946 static bool WasmCachingEnabled(JSContext* cx, unsigned argc, Value* vp) { 947 CallArgs args = CallArgsFromVp(argc, vp); 948 args.rval().setBoolean(wasm::CodeCachingAvailable(cx)); 949 return true; 950 } 951 952 static bool WasmHugeMemorySupported(JSContext* cx, unsigned argc, Value* vp) { 953 CallArgs args = CallArgsFromVp(argc, vp); 954 #ifdef WASM_SUPPORTS_HUGE_MEMORY 955 args.rval().setBoolean(true); 956 #else 957 args.rval().setBoolean(false); 958 #endif 959 return true; 960 } 961 962 static bool WasmMaxMemoryPages(JSContext* cx, unsigned argc, Value* vp) { 963 CallArgs args = CallArgsFromVp(argc, vp); 964 if (args.length() < 1) { 965 JS_ReportErrorASCII(cx, "not enough arguments"); 966 return false; 967 } 968 if (!args.get(0).isString()) { 969 JS_ReportErrorASCII(cx, "address type must be a string"); 970 return false; 971 } 972 RootedString s(cx, args.get(0).toString()); 973 Rooted<JSLinearString*> ls(cx, s->ensureLinear(cx)); 974 if (!ls) { 975 return false; 976 } 977 wasm::PageSize pageSize = wasm::PageSize::Standard; 978 if (argc > 1 && args.get(1).isInt32()) { 979 uint32_t pageSizeBytes = args.get(1).toInt32(); 980 if (pageSizeBytes != PageSizeInBytes(wasm::PageSize::Standard)) { 981 JS_ReportErrorASCII(cx, "bad page size"); 982 return false; 983 } 984 } 985 if (StringEqualsLiteral(ls, "i32")) { 986 wasm::Pages pages = wasm::MaxMemoryPages(wasm::AddressType::I32, pageSize); 987 args.rval().setInt32(pages.pageCount()); 988 return true; 989 } 990 if (StringEqualsLiteral(ls, "i64")) { 991 wasm::Pages pages = wasm::MaxMemoryPages(wasm::AddressType::I64, pageSize); 992 args.rval().setNumber(pages.pageCount()); 993 return true; 994 } 995 JS_ReportErrorASCII(cx, "bad address type"); 996 return false; 997 } 998 999 static bool WasmThreadsEnabled(JSContext* cx, unsigned argc, Value* vp) { 1000 CallArgs args = CallArgsFromVp(argc, vp); 1001 args.rval().setBoolean(wasm::ThreadsAvailable(cx)); 1002 return true; 1003 } 1004 1005 #define WASM_FEATURE(NAME, ...) \ 1006 static bool Wasm##NAME##Enabled(JSContext* cx, unsigned argc, Value* vp) { \ 1007 CallArgs args = CallArgsFromVp(argc, vp); \ 1008 args.rval().setBoolean(wasm::NAME##Available(cx)); \ 1009 return true; \ 1010 } 1011 JS_FOR_WASM_FEATURES(WASM_FEATURE); 1012 #undef WASM_FEATURE 1013 1014 static bool WasmSimdEnabled(JSContext* cx, unsigned argc, Value* vp) { 1015 CallArgs args = CallArgsFromVp(argc, vp); 1016 args.rval().setBoolean(wasm::SimdAvailable(cx)); 1017 return true; 1018 } 1019 1020 static bool WasmCompilersPresent(JSContext* cx, unsigned argc, Value* vp) { 1021 CallArgs args = CallArgsFromVp(argc, vp); 1022 1023 char buf[256]; 1024 *buf = 0; 1025 if (wasm::BaselinePlatformSupport()) { 1026 strcat(buf, "baseline"); 1027 } 1028 if (wasm::IonPlatformSupport()) { 1029 if (*buf) { 1030 strcat(buf, ","); 1031 } 1032 strcat(buf, "ion"); 1033 } 1034 1035 return ReturnStringCopy(cx, args, buf); 1036 } 1037 1038 static bool WasmCompileMode(JSContext* cx, unsigned argc, Value* vp) { 1039 CallArgs args = CallArgsFromVp(argc, vp); 1040 1041 // This triplet of predicates will select zero or one baseline compiler and 1042 // zero or one optimizing compiler. 1043 bool baseline = wasm::BaselineAvailable(cx); 1044 bool ion = wasm::IonAvailable(cx); 1045 bool none = !baseline && !ion; 1046 bool tiered = baseline && ion; 1047 1048 JSStringBuilder result(cx); 1049 if (none && !result.append("none")) { 1050 return false; 1051 } 1052 if (baseline && !result.append("baseline")) { 1053 return false; 1054 } 1055 if (tiered && !result.append("+")) { 1056 return false; 1057 } 1058 if (ion && !result.append("ion")) { 1059 return false; 1060 } 1061 if (JSString* str = result.finishString()) { 1062 args.rval().setString(str); 1063 return true; 1064 } 1065 return false; 1066 } 1067 1068 static bool WasmLazyTieringEnabled(JSContext* cx, unsigned argc, Value* vp) { 1069 // Note: ensure this function stays in sync with `PlatformCanTier()`. 1070 CallArgs args = CallArgsFromVp(argc, vp); 1071 bool baseline = wasm::BaselineAvailable(cx); 1072 bool ion = wasm::IonAvailable(cx); 1073 1074 bool enabled = 1075 baseline && ion && JS::Prefs::wasm_lazy_tiering() && 1076 (JS::Prefs::wasm_lazy_tiering_synchronous() || 1077 (CanUseExtraThreads() && jit::CanFlushExecutionContextForAllThreads())); 1078 1079 args.rval().setBoolean(enabled); 1080 return true; 1081 } 1082 1083 static bool WasmBaselineDisabledByFeatures(JSContext* cx, unsigned argc, 1084 Value* vp) { 1085 CallArgs args = CallArgsFromVp(argc, vp); 1086 bool isDisabled = false; 1087 JSStringBuilder reason(cx); 1088 if (!wasm::BaselineDisabledByFeatures(cx, &isDisabled, &reason)) { 1089 return false; 1090 } 1091 if (isDisabled) { 1092 JSString* result = reason.finishString(); 1093 if (!result) { 1094 return false; 1095 } 1096 args.rval().setString(result); 1097 } else { 1098 args.rval().setBoolean(false); 1099 } 1100 return true; 1101 } 1102 1103 static bool WasmIonDisabledByFeatures(JSContext* cx, unsigned argc, Value* vp) { 1104 CallArgs args = CallArgsFromVp(argc, vp); 1105 bool isDisabled = false; 1106 JSStringBuilder reason(cx); 1107 if (!wasm::IonDisabledByFeatures(cx, &isDisabled, &reason)) { 1108 return false; 1109 } 1110 if (isDisabled) { 1111 JSString* result = reason.finishString(); 1112 if (!result) { 1113 return false; 1114 } 1115 args.rval().setString(result); 1116 } else { 1117 args.rval().setBoolean(false); 1118 } 1119 return true; 1120 } 1121 1122 #ifdef ENABLE_WASM_SIMD 1123 # ifdef DEBUG 1124 static char lastAnalysisResult[1024]; 1125 1126 namespace js { 1127 namespace wasm { 1128 void ReportSimdAnalysis(const char* data) { 1129 strncpy(lastAnalysisResult, data, sizeof(lastAnalysisResult)); 1130 lastAnalysisResult[sizeof(lastAnalysisResult) - 1] = 0; 1131 } 1132 } // namespace wasm 1133 } // namespace js 1134 1135 // Unstable API for white-box testing of SIMD optimizations. 1136 // 1137 // Current API: takes no arguments, returns a string describing the last Simd 1138 // simplification applied. 1139 1140 static bool WasmSimdAnalysis(JSContext* cx, unsigned argc, Value* vp) { 1141 CallArgs args = CallArgsFromVp(argc, vp); 1142 JSString* result = 1143 JS_NewStringCopyZ(cx, *lastAnalysisResult ? lastAnalysisResult : "none"); 1144 if (!result) { 1145 return false; 1146 } 1147 args.rval().setString(result); 1148 *lastAnalysisResult = (char)0; 1149 return true; 1150 } 1151 # endif 1152 #endif 1153 1154 static bool WasmGlobalFromArrayBuffer(JSContext* cx, unsigned argc, Value* vp) { 1155 if (!wasm::HasSupport(cx)) { 1156 JS_ReportErrorASCII(cx, "wasm support unavailable"); 1157 return false; 1158 } 1159 CallArgs args = CallArgsFromVp(argc, vp); 1160 1161 if (args.length() < 2) { 1162 JS_ReportErrorASCII(cx, "not enough arguments"); 1163 return false; 1164 } 1165 1166 // Get the type of the value 1167 wasm::ValType valType; 1168 if (!wasm::ToValType(cx, args.get(0), &valType)) { 1169 return false; 1170 } 1171 1172 // Get the array buffer for the value 1173 if (!args.get(1).isObject() || 1174 !args.get(1).toObject().is<ArrayBufferObject>()) { 1175 JS_ReportErrorASCII(cx, "argument is not an array buffer"); 1176 return false; 1177 } 1178 Rooted<ArrayBufferObject*> buffer( 1179 cx, &args.get(1).toObject().as<ArrayBufferObject>()); 1180 1181 // Only allow POD to be created from bytes 1182 switch (valType.kind()) { 1183 case wasm::ValType::I32: 1184 case wasm::ValType::I64: 1185 case wasm::ValType::F32: 1186 case wasm::ValType::F64: 1187 case wasm::ValType::V128: 1188 break; 1189 default: 1190 JS_ReportErrorASCII( 1191 cx, "invalid valtype for creating WebAssembly.Global from bytes"); 1192 return false; 1193 } 1194 1195 // Check we have all the bytes we need 1196 if (valType.size() != buffer->byteLength()) { 1197 JS_ReportErrorASCII(cx, "array buffer has incorrect size"); 1198 return false; 1199 } 1200 1201 // Copy the bytes from buffer into a tagged val 1202 wasm::RootedVal val(cx); 1203 val.get().initFromRootedLocation(valType, buffer->dataPointer()); 1204 1205 // Create the global object 1206 RootedObject proto( 1207 cx, GlobalObject::getOrCreatePrototype(cx, JSProto_WasmGlobal)); 1208 if (!proto) { 1209 return false; 1210 } 1211 Rooted<WasmGlobalObject*> result( 1212 cx, WasmGlobalObject::create(cx, val, false, proto)); 1213 if (!result) { 1214 return false; 1215 } 1216 1217 args.rval().setObject(*result.get()); 1218 return true; 1219 } 1220 1221 enum class LaneInterp { 1222 I32x4, 1223 I64x2, 1224 F32x4, 1225 F64x2, 1226 }; 1227 1228 size_t LaneInterpLanes(LaneInterp interp) { 1229 switch (interp) { 1230 case LaneInterp::I32x4: 1231 return 4; 1232 case LaneInterp::I64x2: 1233 return 2; 1234 case LaneInterp::F32x4: 1235 return 4; 1236 case LaneInterp::F64x2: 1237 return 2; 1238 default: 1239 MOZ_ASSERT_UNREACHABLE(); 1240 return 0; 1241 } 1242 } 1243 1244 static bool ToLaneInterp(JSContext* cx, HandleValue v, LaneInterp* out) { 1245 RootedString interpStr(cx, ToString(cx, v)); 1246 if (!interpStr) { 1247 return false; 1248 } 1249 Rooted<JSLinearString*> interpLinearStr(cx, interpStr->ensureLinear(cx)); 1250 if (!interpLinearStr) { 1251 return false; 1252 } 1253 1254 if (StringEqualsLiteral(interpLinearStr, "i32x4")) { 1255 *out = LaneInterp::I32x4; 1256 return true; 1257 } else if (StringEqualsLiteral(interpLinearStr, "i64x2")) { 1258 *out = LaneInterp::I64x2; 1259 return true; 1260 } else if (StringEqualsLiteral(interpLinearStr, "f32x4")) { 1261 *out = LaneInterp::F32x4; 1262 return true; 1263 } else if (StringEqualsLiteral(interpLinearStr, "f64x2")) { 1264 *out = LaneInterp::F64x2; 1265 return true; 1266 } 1267 1268 JS_ReportErrorASCII(cx, "invalid lane interpretation"); 1269 return false; 1270 } 1271 1272 static bool WasmGlobalExtractLane(JSContext* cx, unsigned argc, Value* vp) { 1273 if (!wasm::HasSupport(cx)) { 1274 JS_ReportErrorASCII(cx, "wasm support unavailable"); 1275 return false; 1276 } 1277 CallArgs args = CallArgsFromVp(argc, vp); 1278 1279 if (args.length() < 3) { 1280 JS_ReportErrorASCII(cx, "not enough arguments"); 1281 return false; 1282 } 1283 1284 // Get the global value 1285 if (!args.get(0).isObject() || 1286 !args.get(0).toObject().is<WasmGlobalObject>()) { 1287 JS_ReportErrorASCII(cx, "argument is not wasm value"); 1288 return false; 1289 } 1290 Rooted<WasmGlobalObject*> global( 1291 cx, &args.get(0).toObject().as<WasmGlobalObject>()); 1292 1293 // Check that we have a v128 value 1294 if (global->type().kind() != wasm::ValType::V128) { 1295 JS_ReportErrorASCII(cx, "global is not a v128 value"); 1296 return false; 1297 } 1298 wasm::V128 v128 = global->val().get().v128(); 1299 1300 // Get the passed interpretation of lanes 1301 LaneInterp interp; 1302 if (!ToLaneInterp(cx, args.get(1), &interp)) { 1303 return false; 1304 } 1305 1306 // Get the lane to extract 1307 int32_t lane; 1308 if (!ToInt32(cx, args.get(2), &lane)) { 1309 return false; 1310 } 1311 1312 // Check that the lane interp is valid 1313 if (lane < 0 || size_t(lane) >= LaneInterpLanes(interp)) { 1314 JS_ReportErrorASCII(cx, "invalid lane for interp"); 1315 return false; 1316 } 1317 1318 wasm::RootedVal val(cx); 1319 switch (interp) { 1320 case LaneInterp::I32x4: { 1321 uint32_t i; 1322 v128.extractLane<uint32_t>(lane, &i); 1323 val.set(wasm::Val(i)); 1324 break; 1325 } 1326 case LaneInterp::I64x2: { 1327 uint64_t i; 1328 v128.extractLane<uint64_t>(lane, &i); 1329 val.set(wasm::Val(i)); 1330 break; 1331 } 1332 case LaneInterp::F32x4: { 1333 float f; 1334 v128.extractLane<float>(lane, &f); 1335 val.set(wasm::Val(f)); 1336 break; 1337 } 1338 case LaneInterp::F64x2: { 1339 double d; 1340 v128.extractLane<double>(lane, &d); 1341 val.set(wasm::Val(d)); 1342 break; 1343 } 1344 default: 1345 MOZ_ASSERT_UNREACHABLE(); 1346 } 1347 1348 RootedObject proto( 1349 cx, GlobalObject::getOrCreatePrototype(cx, JSProto_WasmGlobal)); 1350 Rooted<WasmGlobalObject*> result( 1351 cx, WasmGlobalObject::create(cx, val, false, proto)); 1352 args.rval().setObject(*result.get()); 1353 return true; 1354 } 1355 1356 static bool WasmGlobalsEqual(JSContext* cx, unsigned argc, Value* vp) { 1357 if (!wasm::HasSupport(cx)) { 1358 JS_ReportErrorASCII(cx, "wasm support unavailable"); 1359 return false; 1360 } 1361 CallArgs args = CallArgsFromVp(argc, vp); 1362 1363 if (args.length() < 2) { 1364 JS_ReportErrorASCII(cx, "not enough arguments"); 1365 return false; 1366 } 1367 1368 if (!args.get(0).isObject() || 1369 !args.get(0).toObject().is<WasmGlobalObject>() || 1370 !args.get(1).isObject() || 1371 !args.get(1).toObject().is<WasmGlobalObject>()) { 1372 JS_ReportErrorASCII(cx, "argument is not wasm value"); 1373 return false; 1374 } 1375 1376 Rooted<WasmGlobalObject*> a(cx, 1377 &args.get(0).toObject().as<WasmGlobalObject>()); 1378 Rooted<WasmGlobalObject*> b(cx, 1379 &args.get(1).toObject().as<WasmGlobalObject>()); 1380 1381 if (a->type().kind() != b->type().kind()) { 1382 JS_ReportErrorASCII(cx, "globals are of different kind"); 1383 return false; 1384 } 1385 1386 bool result; 1387 const wasm::Val& aVal = a->val().get(); 1388 const wasm::Val& bVal = b->val().get(); 1389 switch (a->type().kind()) { 1390 case wasm::ValType::I32: { 1391 result = aVal.i32() == bVal.i32(); 1392 break; 1393 } 1394 case wasm::ValType::I64: { 1395 result = aVal.i64() == bVal.i64(); 1396 break; 1397 } 1398 case wasm::ValType::F32: { 1399 result = mozilla::BitwiseCast<uint32_t>(aVal.f32()) == 1400 mozilla::BitwiseCast<uint32_t>(bVal.f32()); 1401 break; 1402 } 1403 case wasm::ValType::F64: { 1404 result = mozilla::BitwiseCast<uint64_t>(aVal.f64()) == 1405 mozilla::BitwiseCast<uint64_t>(bVal.f64()); 1406 break; 1407 } 1408 case wasm::ValType::V128: { 1409 // Don't know the interpretation of the v128, so we only can do an exact 1410 // bitwise equality. Testing code can use wasmGlobalExtractLane to 1411 // workaround this if needed. 1412 result = aVal.v128() == bVal.v128(); 1413 break; 1414 } 1415 case wasm::ValType::Ref: { 1416 result = aVal.ref() == bVal.ref(); 1417 break; 1418 } 1419 default: 1420 JS_ReportErrorASCII(cx, "unsupported type"); 1421 return false; 1422 } 1423 args.rval().setBoolean(result); 1424 return true; 1425 } 1426 1427 // Flavors of NaN values for WebAssembly. 1428 // See 1429 // https://webassembly.github.io/spec/core/syntax/values.html#floating-point. 1430 enum class NaNFlavor { 1431 // A canonical NaN value. 1432 // - the sign bit is unspecified, 1433 // - the 8-bit exponent is set to all 1s 1434 // - the MSB of the payload is set to 1 (a quieted NaN) and all others to 0. 1435 Canonical, 1436 // An arithmetic NaN. This is the same as a canonical NaN including that the 1437 // payload MSB is set to 1, but one or more of the remaining payload bits MAY 1438 // BE set to 1 (a canonical NaN specifies all 0s). 1439 Arithmetic, 1440 }; 1441 1442 static bool IsNaNFlavor(uint32_t bits, NaNFlavor flavor) { 1443 switch (flavor) { 1444 case NaNFlavor::Canonical: { 1445 return (bits & 0x7fffffff) == 0x7fc00000; 1446 } 1447 case NaNFlavor::Arithmetic: { 1448 const uint32_t ArithmeticNaN = 0x7f800000; 1449 const uint32_t ArithmeticPayloadMSB = 0x00400000; 1450 bool isNaN = (bits & ArithmeticNaN) == ArithmeticNaN; 1451 bool isMSBSet = (bits & ArithmeticPayloadMSB) == ArithmeticPayloadMSB; 1452 return isNaN && isMSBSet; 1453 } 1454 default: 1455 MOZ_CRASH(); 1456 } 1457 } 1458 1459 static bool IsNaNFlavor(uint64_t bits, NaNFlavor flavor) { 1460 switch (flavor) { 1461 case NaNFlavor::Canonical: { 1462 return (bits & 0x7fffffffffffffff) == 0x7ff8000000000000; 1463 } 1464 case NaNFlavor::Arithmetic: { 1465 uint64_t ArithmeticNaN = 0x7ff0000000000000; 1466 uint64_t ArithmeticPayloadMSB = 0x0008000000000000; 1467 bool isNaN = (bits & ArithmeticNaN) == ArithmeticNaN; 1468 bool isMsbSet = (bits & ArithmeticPayloadMSB) == ArithmeticPayloadMSB; 1469 return isNaN && isMsbSet; 1470 } 1471 default: 1472 MOZ_CRASH(); 1473 } 1474 } 1475 1476 static bool ToNaNFlavor(JSContext* cx, HandleValue v, NaNFlavor* out) { 1477 RootedString flavorStr(cx, ToString(cx, v)); 1478 if (!flavorStr) { 1479 return false; 1480 } 1481 Rooted<JSLinearString*> flavorLinearStr(cx, flavorStr->ensureLinear(cx)); 1482 if (!flavorLinearStr) { 1483 return false; 1484 } 1485 1486 if (StringEqualsLiteral(flavorLinearStr, "canonical_nan")) { 1487 *out = NaNFlavor::Canonical; 1488 return true; 1489 } else if (StringEqualsLiteral(flavorLinearStr, "arithmetic_nan")) { 1490 *out = NaNFlavor::Arithmetic; 1491 return true; 1492 } 1493 1494 JS_ReportErrorASCII(cx, "invalid nan flavor"); 1495 return false; 1496 } 1497 1498 static bool WasmGlobalIsNaN(JSContext* cx, unsigned argc, Value* vp) { 1499 if (!wasm::HasSupport(cx)) { 1500 JS_ReportErrorASCII(cx, "wasm support unavailable"); 1501 return false; 1502 } 1503 CallArgs args = CallArgsFromVp(argc, vp); 1504 1505 if (args.length() < 2) { 1506 JS_ReportErrorASCII(cx, "not enough arguments"); 1507 return false; 1508 } 1509 1510 if (!args.get(0).isObject() || 1511 !args.get(0).toObject().is<WasmGlobalObject>()) { 1512 JS_ReportErrorASCII(cx, "argument is not wasm value"); 1513 return false; 1514 } 1515 Rooted<WasmGlobalObject*> global( 1516 cx, &args.get(0).toObject().as<WasmGlobalObject>()); 1517 1518 NaNFlavor flavor; 1519 if (!ToNaNFlavor(cx, args.get(1), &flavor)) { 1520 return false; 1521 } 1522 1523 bool result; 1524 const wasm::Val& val = global->val().get(); 1525 switch (global->type().kind()) { 1526 case wasm::ValType::F32: { 1527 result = IsNaNFlavor(mozilla::BitwiseCast<uint32_t>(val.f32()), flavor); 1528 break; 1529 } 1530 case wasm::ValType::F64: { 1531 result = IsNaNFlavor(mozilla::BitwiseCast<uint64_t>(val.f64()), flavor); 1532 break; 1533 } 1534 default: 1535 JS_ReportErrorASCII(cx, "global is not a floating point value"); 1536 return false; 1537 } 1538 args.rval().setBoolean(result); 1539 return true; 1540 } 1541 1542 static bool WasmGlobalToString(JSContext* cx, unsigned argc, Value* vp) { 1543 if (!wasm::HasSupport(cx)) { 1544 JS_ReportErrorASCII(cx, "wasm support unavailable"); 1545 return false; 1546 } 1547 CallArgs args = CallArgsFromVp(argc, vp); 1548 1549 if (args.length() < 1) { 1550 JS_ReportErrorASCII(cx, "not enough arguments"); 1551 return false; 1552 } 1553 if (!args.get(0).isObject() || 1554 !args.get(0).toObject().is<WasmGlobalObject>()) { 1555 JS_ReportErrorASCII(cx, "argument is not wasm value"); 1556 return false; 1557 } 1558 Rooted<WasmGlobalObject*> global( 1559 cx, &args.get(0).toObject().as<WasmGlobalObject>()); 1560 const wasm::Val& globalVal = global->val().get(); 1561 1562 UniqueChars result; 1563 switch (globalVal.type().kind()) { 1564 case wasm::ValType::I32: { 1565 result = JS_smprintf("i32:%" PRIx32, globalVal.i32()); 1566 break; 1567 } 1568 case wasm::ValType::I64: { 1569 result = JS_smprintf("i64:%" PRIx64, globalVal.i64()); 1570 break; 1571 } 1572 case wasm::ValType::F32: { 1573 result = JS_smprintf("f32:%f", globalVal.f32()); 1574 break; 1575 } 1576 case wasm::ValType::F64: { 1577 result = JS_smprintf("f64:%lf", globalVal.f64()); 1578 break; 1579 } 1580 case wasm::ValType::V128: { 1581 wasm::V128 v128 = globalVal.v128(); 1582 result = JS_smprintf( 1583 "v128:%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x", v128.bytes[0], 1584 v128.bytes[1], v128.bytes[2], v128.bytes[3], v128.bytes[4], 1585 v128.bytes[5], v128.bytes[6], v128.bytes[7], v128.bytes[8], 1586 v128.bytes[9], v128.bytes[10], v128.bytes[11], v128.bytes[12], 1587 v128.bytes[13], v128.bytes[14], v128.bytes[15]); 1588 break; 1589 } 1590 case wasm::ValType::Ref: { 1591 result = JS_smprintf("ref:%" PRIxPTR, globalVal.ref().rawValue()); 1592 break; 1593 } 1594 default: 1595 MOZ_ASSERT_UNREACHABLE(); 1596 } 1597 1598 return ReturnStringCopy(cx, args, result.get()); 1599 } 1600 1601 static bool WasmLosslessInvoke(JSContext* cx, unsigned argc, Value* vp) { 1602 if (!wasm::HasSupport(cx)) { 1603 JS_ReportErrorASCII(cx, "wasm support unavailable"); 1604 return false; 1605 } 1606 CallArgs args = CallArgsFromVp(argc, vp); 1607 1608 if (args.length() < 1) { 1609 JS_ReportErrorASCII(cx, "not enough arguments"); 1610 return false; 1611 } 1612 if (!args.get(0).isObject() || !args.get(0).toObject().is<JSFunction>()) { 1613 JS_ReportErrorASCII(cx, "argument is not an object"); 1614 return false; 1615 } 1616 1617 RootedFunction func(cx, &args[0].toObject().as<JSFunction>()); 1618 if (!func || !func->isWasm()) { 1619 JS_ReportErrorASCII(cx, "argument is not an exported wasm function"); 1620 return false; 1621 } 1622 1623 // Switch to the function's realm 1624 AutoRealm ar(cx, func); 1625 1626 // Get the instance and funcIndex for calling the function 1627 wasm::Instance& instance = func->wasmInstance(); 1628 uint32_t funcIndex = func->wasmFuncIndex(); 1629 1630 // Set up a modified call frame following the standard JS 1631 // [callee, this, arguments...] convention. 1632 RootedValueVector wasmCallFrame(cx); 1633 size_t len = 2 + args.length(); 1634 if (!wasmCallFrame.resize(len)) { 1635 return false; 1636 } 1637 wasmCallFrame[0].set(ObjectValue(*func)); 1638 wasmCallFrame[1].set(args.thisv()); 1639 // Copy over the arguments needed to invoke the provided wasm function, 1640 // skipping the wasm function we're calling that is at `args.get(0)`. 1641 for (size_t i = 1; i < args.length(); i++) { 1642 size_t wasmArg = i - 1; 1643 wasmCallFrame[2 + wasmArg].set(args.get(i)); 1644 } 1645 size_t wasmArgc = argc - 1; 1646 CallArgs wasmCallArgs(CallArgsFromVp(wasmArgc, wasmCallFrame.begin())); 1647 1648 // Invoke the function with the new call frame 1649 bool result = instance.callExport(cx, funcIndex, wasmCallArgs, 1650 wasm::CoercionLevel::Lossless); 1651 // Assign the wasm rval to our rval 1652 args.rval().set(wasmCallArgs.rval()); 1653 return result; 1654 } 1655 1656 static bool ConvertToTier(JSContext* cx, HandleValue value, 1657 const wasm::Code& code, wasm::Tier* tier) { 1658 RootedString option(cx, JS::ToString(cx, value)); 1659 1660 if (!option) { 1661 return false; 1662 } 1663 1664 bool stableTier = false; 1665 bool bestTier = false; 1666 bool baselineTier = false; 1667 bool ionTier = false; 1668 1669 if (!JS_StringEqualsLiteral(cx, option, "stable", &stableTier) || 1670 !JS_StringEqualsLiteral(cx, option, "best", &bestTier) || 1671 !JS_StringEqualsLiteral(cx, option, "baseline", &baselineTier) || 1672 !JS_StringEqualsLiteral(cx, option, "ion", &ionTier)) { 1673 return false; 1674 } 1675 1676 if (stableTier) { 1677 *tier = code.stableCompleteTier(); 1678 } else if (bestTier) { 1679 *tier = code.bestCompleteTier(); 1680 } else if (baselineTier) { 1681 *tier = wasm::Tier::Baseline; 1682 } else if (ionTier) { 1683 *tier = wasm::Tier::Optimized; 1684 } else { 1685 // You can omit the argument but you can't pass just anything you like 1686 return false; 1687 } 1688 1689 return true; 1690 } 1691 1692 static bool WasmExtractCode(JSContext* cx, unsigned argc, Value* vp) { 1693 if (!wasm::HasSupport(cx)) { 1694 JS_ReportErrorASCII(cx, "wasm support unavailable"); 1695 return false; 1696 } 1697 1698 CallArgs args = CallArgsFromVp(argc, vp); 1699 1700 if (!args.get(0).isObject()) { 1701 JS_ReportErrorASCII(cx, "argument is not an object"); 1702 return false; 1703 } 1704 1705 Rooted<WasmModuleObject*> module( 1706 cx, args[0].toObject().maybeUnwrapIf<WasmModuleObject>()); 1707 if (!module) { 1708 JS_ReportErrorASCII(cx, "argument is not a WebAssembly.Module"); 1709 return false; 1710 } 1711 1712 wasm::Tier tier = module->module().code().stableCompleteTier(); 1713 if (args.length() > 1 && 1714 !ConvertToTier(cx, args[1], module->module().code(), &tier)) { 1715 args.rval().setNull(); 1716 return false; 1717 } 1718 1719 RootedValue result(cx); 1720 if (!module->module().extractCode(cx, tier, &result)) { 1721 return false; 1722 } 1723 1724 args.rval().set(result); 1725 return true; 1726 } 1727 1728 static bool HasDisassembler(JSContext* cx, unsigned argc, Value* vp) { 1729 CallArgs args = CallArgsFromVp(argc, vp); 1730 args.rval().setBoolean(jit::HasDisassembler()); 1731 return true; 1732 } 1733 1734 MOZ_THREAD_LOCAL(JSSprinter*) disasmPrinter; 1735 1736 static void captureDisasmText(const char* text) { 1737 JSSprinter* printer = disasmPrinter.get(); 1738 printer->put(text); 1739 printer->printf("\n"); 1740 } 1741 1742 static bool DisassembleNative(JSContext* cx, unsigned argc, Value* vp) { 1743 CallArgs args = CallArgsFromVp(argc, vp); 1744 1745 if (args.length() < 1) { 1746 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1747 JSMSG_MORE_ARGS_NEEDED, "disnative", "1", "", 1748 "0"); 1749 return false; 1750 } 1751 1752 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) { 1753 JS_ReportErrorASCII(cx, "The first argument must be a function."); 1754 return false; 1755 } 1756 1757 JSSprinter sprinter(cx); 1758 if (!sprinter.init()) { 1759 return false; 1760 } 1761 1762 RootedFunction fun(cx, &args[0].toObject().as<JSFunction>()); 1763 1764 uint8_t* jit_begin = nullptr; 1765 uint8_t* jit_end = nullptr; 1766 1767 if (fun->isAsmJSNative() || fun->isWasmWithJitEntry()) { 1768 if (IsAsmJSModule(fun)) { 1769 JS_ReportErrorASCII(cx, "Can't disassemble asm.js module function."); 1770 return false; 1771 } 1772 if (fun->isAsmJSNative()) { 1773 sprinter.printf("; backend=asmjs\n"); 1774 } 1775 sprinter.printf("; backend=wasm\n"); 1776 1777 js::wasm::Instance& inst = fun->wasmInstance(); 1778 const uint32_t funcIndex = fun->wasmFuncIndex(); 1779 const js::wasm::CodeBlock& codeBlock = inst.code().funcCodeBlock(funcIndex); 1780 const js::wasm::FuncExport& func = codeBlock.lookupFuncExport(funcIndex); 1781 const js::wasm::CodeRange& codeRange = codeBlock.codeRange(func); 1782 1783 jit_begin = codeBlock.base() + codeRange.begin(); 1784 jit_end = codeBlock.base() + codeRange.end(); 1785 } else if (fun->hasJitScript() && fun->nonLazyScript()->hasIonScript()) { 1786 sprinter.printf("; backend=ion\n"); 1787 jit_begin = fun->nonLazyScript()->ionScript()->method()->raw(); 1788 jit_end = fun->nonLazyScript()->ionScript()->method()->rawEnd(); 1789 } else if (fun->hasJitScript() && fun->nonLazyScript()->hasBaselineScript()) { 1790 sprinter.printf("; backend=baseline\n"); 1791 jit_begin = fun->nonLazyScript()->baselineScript()->method()->raw(); 1792 jit_end = fun->nonLazyScript()->baselineScript()->method()->rawEnd(); 1793 } else { 1794 JS_ReportErrorASCII(cx, 1795 "The function hasn't been warmed up, hence no JIT code " 1796 "to disassemble."); 1797 return false; 1798 } 1799 1800 MOZ_ASSERT(jit_begin); 1801 MOZ_ASSERT(jit_end); 1802 1803 #ifdef JS_CODEGEN_ARM 1804 // The ARM32 disassembler is currently not fuzzing-safe because it doesn't 1805 // handle constant pools correctly (bug 1875363). 1806 if (fuzzingSafe) { 1807 JS_ReportErrorASCII(cx, "disnative is not fuzzing-safe on ARM32"); 1808 return false; 1809 } 1810 #elif defined(JS_CODEGEN_RISCV64) 1811 // The riscv64 disassembler is currently not fuzzing-safe because it doesn't 1812 // handle constant pools correctly (bug 1987559). 1813 if (fuzzingSafe) { 1814 JS_ReportErrorASCII(cx, "disnative is not fuzzing-safe on riscv64"); 1815 return false; 1816 } 1817 #endif 1818 1819 // Dump the raw code to a file before disassembling in case 1820 // finishString triggers a GC and discards the jitcode. 1821 if (!fuzzingSafe && args.length() > 1 && args[1].isString()) { 1822 RootedString str(cx, args[1].toString()); 1823 JS::UniqueChars fileNameBytes = JS_EncodeStringToUTF8(cx, str); 1824 1825 const char* fileName = fileNameBytes.get(); 1826 if (!fileName) { 1827 ReportOutOfMemory(cx); 1828 return false; 1829 } 1830 1831 FILE* f = fopen(fileName, "w"); 1832 if (!f) { 1833 JS_ReportErrorASCII(cx, "Could not open file for writing."); 1834 return false; 1835 } 1836 1837 uintptr_t expected_length = reinterpret_cast<uintptr_t>(jit_end) - 1838 reinterpret_cast<uintptr_t>(jit_begin); 1839 if (expected_length != fwrite(jit_begin, jit_end - jit_begin, 1, f)) { 1840 JS_ReportErrorASCII(cx, "Did not write all function bytes to the file."); 1841 fclose(f); 1842 return false; 1843 } 1844 fclose(f); 1845 } 1846 1847 disasmPrinter.set(&sprinter); 1848 auto onFinish = mozilla::MakeScopeExit([&] { disasmPrinter.set(nullptr); }); 1849 1850 jit::Disassemble(jit_begin, jit_end - jit_begin, &captureDisasmText); 1851 1852 JSString* str = sprinter.release(cx); 1853 if (!str) { 1854 return false; 1855 } 1856 1857 args.rval().setString(str); 1858 return true; 1859 } 1860 1861 static bool DisassembleBaselineICs(JSContext* cx, unsigned argc, Value* vp) { 1862 CallArgs args = CallArgsFromVp(argc, vp); 1863 args.rval().setUndefined(); 1864 1865 if (!args.requireAtLeast(cx, "disblic", 1)) { 1866 return false; 1867 } 1868 1869 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) { 1870 JS_ReportErrorASCII(cx, "The first argument must be a function."); 1871 return false; 1872 } 1873 1874 JSSprinter sprinter(cx); 1875 if (!sprinter.init()) { 1876 return false; 1877 } 1878 1879 disasmPrinter.set(&sprinter); 1880 auto onFinish = mozilla::MakeScopeExit([&] { disasmPrinter.set(nullptr); }); 1881 1882 RootedFunction fun(cx, &args[0].toObject().as<JSFunction>()); 1883 1884 if (!fun->hasJitScript()) { 1885 args.rval().setUndefined(); 1886 return true; 1887 } 1888 1889 #ifdef JS_CODEGEN_ARM 1890 // The ARM32 disassembler is currently not fuzzing-safe because it doesn't 1891 // handle constant pools correctly (bug 1875363). 1892 if (fuzzingSafe) { 1893 JS_ReportErrorASCII(cx, "disblic is not fuzzing-safe on ARM32"); 1894 return false; 1895 } 1896 #elif defined(JS_CODEGEN_RISCV64) 1897 // The riscv64 disassembler is currently not fuzzing-safe because it doesn't 1898 // handle constant pools correctly (bug 1987559). 1899 if (fuzzingSafe) { 1900 JS_ReportErrorASCII(cx, "disblic is not fuzzing-safe on riscv64"); 1901 return false; 1902 } 1903 #endif 1904 1905 RootedScript script(cx, fun->nonLazyScript()); 1906 jit::ICScript* icScript = script->jitScript()->icScript(); 1907 for (uint32_t i = 0; i < icScript->numICEntries(); i++) { 1908 jit::ICEntry& entry = icScript->icEntry(i); 1909 jit::ICStub* stub = entry.firstStub(); 1910 1911 jit::ICStub* fallbackStub = stub; 1912 while (!fallbackStub->isFallback()) { 1913 fallbackStub = fallbackStub->toCacheIRStub()->next(); 1914 } 1915 uint32_t pcOffset = fallbackStub->toFallbackStub()->pcOffset(); 1916 sprinter.printf("; %s (pcOffset %05u)\n", 1917 CodeName(JSOp(*script->offsetToPC(pcOffset))), pcOffset); 1918 1919 uint32_t stubNum = 1; 1920 while (!stub->isFallback()) { 1921 sprinter.printf("; Stub #%d (entry count: %d)\n", stubNum, 1922 stub->enteredCount()); 1923 jit::ICCacheIRStub* cacheIRStub = stub->toCacheIRStub(); 1924 uint8_t* jit_begin = stub->jitCode()->raw(); 1925 uint8_t* jit_end = stub->jitCode()->rawEnd(); 1926 #ifdef JS_CACHEIR_SPEW 1927 sprinter.printf("; IR:\n"); 1928 SpewCacheIROps(sprinter, "; ", cacheIRStub->stubInfo()); 1929 #endif 1930 1931 jit::Disassemble(jit_begin, jit_end - jit_begin, &captureDisasmText); 1932 stub = cacheIRStub->next(); 1933 stubNum++; 1934 } 1935 } 1936 1937 JSString* str = sprinter.release(cx); 1938 if (!str) { 1939 return false; 1940 } 1941 1942 args.rval().setString(str); 1943 return true; 1944 } 1945 1946 static bool ComputeTier(JSContext* cx, const wasm::Code& code, 1947 HandleValue tierSelection, wasm::Tier* tier) { 1948 *tier = code.stableCompleteTier(); 1949 if (!tierSelection.isUndefined() && 1950 !ConvertToTier(cx, tierSelection, code, tier)) { 1951 JS_ReportErrorASCII(cx, "invalid tier"); 1952 return false; 1953 } 1954 1955 return true; 1956 } 1957 1958 template <typename DisasmFunction> 1959 static bool DisassembleIt(JSContext* cx, bool asString, MutableHandleValue rval, 1960 DisasmFunction&& disassembleIt) { 1961 if (asString) { 1962 JSSprinter sprinter(cx); 1963 if (!sprinter.init()) { 1964 return false; 1965 } 1966 disasmPrinter.set(&sprinter); 1967 auto onFinish = mozilla::MakeScopeExit([&] { disasmPrinter.set(nullptr); }); 1968 disassembleIt(captureDisasmText); 1969 1970 JSString* sresult = sprinter.release(cx); 1971 if (!sresult) { 1972 return false; 1973 } 1974 rval.setString(sresult); 1975 return true; 1976 } 1977 1978 disassembleIt([](const char* text) { fprintf(stderr, "%s\n", text); }); 1979 return true; 1980 } 1981 1982 static bool WasmDisassembleFunction(JSContext* cx, const HandleFunction& func, 1983 HandleValue tierSelection, bool asString, 1984 MutableHandleValue rval) { 1985 wasm::Instance& instance = func->wasmInstance(); 1986 uint32_t funcIndex = func->wasmFuncIndex(); 1987 wasm::Tier tier; 1988 1989 if (!ComputeTier(cx, instance.code(), tierSelection, &tier)) { 1990 return false; 1991 } 1992 1993 if (!instance.code().funcHasTier(funcIndex, tier)) { 1994 JS_ReportErrorASCII(cx, "function missing selected tier"); 1995 return false; 1996 } 1997 1998 return DisassembleIt( 1999 cx, asString, rval, [&](void (*captureText)(const char*)) { 2000 instance.disassembleExport(cx, funcIndex, tier, captureText); 2001 }); 2002 } 2003 2004 static bool WasmDisassembleCode(JSContext* cx, const wasm::Code& code, 2005 HandleValue tierSelection, int kindSelection, 2006 bool asString, MutableHandleValue rval) { 2007 wasm::Tier tier; 2008 if (!ComputeTier(cx, code, tierSelection, &tier)) { 2009 return false; 2010 } 2011 2012 return DisassembleIt(cx, asString, rval, 2013 [&](void (*captureText)(const char*)) { 2014 code.disassemble(cx, tier, kindSelection, captureText); 2015 }); 2016 } 2017 2018 static bool WasmDisassemble(JSContext* cx, unsigned argc, Value* vp) { 2019 if (!wasm::HasSupport(cx)) { 2020 JS_ReportErrorASCII(cx, "wasm support unavailable"); 2021 return false; 2022 } 2023 2024 CallArgs args = CallArgsFromVp(argc, vp); 2025 2026 args.rval().set(UndefinedValue()); 2027 2028 if (!args.get(0).isObject()) { 2029 JS_ReportErrorASCII(cx, "argument is not an object"); 2030 return false; 2031 } 2032 2033 bool asString = false; 2034 RootedValue tierSelection(cx); 2035 int kindSelection = (1 << wasm::CodeRange::Function); 2036 if (args.length() > 1 && args[1].isObject()) { 2037 RootedObject options(cx, &args[1].toObject()); 2038 RootedValue val(cx); 2039 2040 if (!JS_GetProperty(cx, options, "asString", &val)) { 2041 return false; 2042 } 2043 asString = val.isBoolean() && val.toBoolean(); 2044 2045 if (!JS_GetProperty(cx, options, "tier", &tierSelection)) { 2046 return false; 2047 } 2048 2049 if (!JS_GetProperty(cx, options, "kinds", &val)) { 2050 return false; 2051 } 2052 if (val.isString() && val.toString()->hasLatin1Chars()) { 2053 AutoStableStringChars stable(cx); 2054 if (!stable.init(cx, val.toString())) { 2055 return false; 2056 } 2057 const char* p = (const char*)(stable.latin1Chars()); 2058 const char* end = p + val.toString()->length(); 2059 int selection = 0; 2060 for (;;) { 2061 if (strncmp(p, "Function", 8) == 0) { 2062 selection |= (1 << wasm::CodeRange::Function); 2063 p += 8; 2064 } else if (strncmp(p, "InterpEntry", 11) == 0) { 2065 selection |= (1 << wasm::CodeRange::InterpEntry); 2066 p += 11; 2067 } else if (strncmp(p, "JitEntry", 8) == 0) { 2068 selection |= (1 << wasm::CodeRange::JitEntry); 2069 p += 8; 2070 } else if (strncmp(p, "ImportInterpExit", 16) == 0) { 2071 selection |= (1 << wasm::CodeRange::ImportInterpExit); 2072 p += 16; 2073 } else if (strncmp(p, "ImportJitExit", 13) == 0) { 2074 selection |= (1 << wasm::CodeRange::ImportJitExit); 2075 p += 13; 2076 } else if (strncmp(p, "all", 3) == 0) { 2077 selection = ~0; 2078 p += 3; 2079 } else { 2080 break; 2081 } 2082 if (p == end || *p != ',') { 2083 break; 2084 } 2085 p++; 2086 } 2087 if (p == end) { 2088 kindSelection = selection; 2089 } else { 2090 JS_ReportErrorASCII(cx, "argument object has invalid `kinds`"); 2091 return false; 2092 } 2093 } 2094 } 2095 2096 RootedFunction func(cx, args[0].toObject().maybeUnwrapIf<JSFunction>()); 2097 if (func && func->isWasm()) { 2098 return WasmDisassembleFunction(cx, func, tierSelection, asString, 2099 args.rval()); 2100 } 2101 if (args[0].toObject().is<WasmModuleObject>()) { 2102 return WasmDisassembleCode( 2103 cx, args[0].toObject().as<WasmModuleObject>().module().code(), 2104 tierSelection, kindSelection, asString, args.rval()); 2105 } 2106 if (args[0].toObject().is<WasmInstanceObject>()) { 2107 return WasmDisassembleCode( 2108 cx, args[0].toObject().as<WasmInstanceObject>().instance().code(), 2109 tierSelection, kindSelection, asString, args.rval()); 2110 } 2111 JS_ReportErrorASCII( 2112 cx, "argument is not an exported wasm function or a wasm module"); 2113 return false; 2114 } 2115 2116 static bool WasmModuleToText(JSContext* cx, unsigned argc, Value* vp) { 2117 if (!wasm::HasSupport(cx)) { 2118 JS_ReportErrorASCII(cx, "wasm support unavailable"); 2119 return false; 2120 } 2121 2122 CallArgs args = CallArgsFromVp(argc, vp); 2123 2124 if (!args.get(0).isObject()) { 2125 JS_ReportErrorASCII(cx, "argument is not an object"); 2126 return false; 2127 } 2128 2129 if (!args[0].toObject().is<WasmModuleObject>()) { 2130 JS_ReportErrorASCII(cx, "argument is not a wasm module"); 2131 return false; 2132 } 2133 2134 const wasm::Module& mod = args[0].toObject().as<WasmModuleObject>().module(); 2135 JSSprinter out(cx); 2136 if (!out.init()) { 2137 ReportOutOfMemory(cx); 2138 return false; 2139 } 2140 wasm::DumpModule(mod, out); 2141 2142 JSString* str = out.release(cx); 2143 if (!str) { 2144 ReportOutOfMemory(cx); 2145 return false; 2146 } 2147 args.rval().set(StringValue(str)); 2148 return true; 2149 } 2150 2151 static bool WasmFunctionTier(JSContext* cx, unsigned argc, Value* vp) { 2152 if (!wasm::HasSupport(cx)) { 2153 JS_ReportErrorASCII(cx, "wasm support unavailable"); 2154 return false; 2155 } 2156 2157 CallArgs args = CallArgsFromVp(argc, vp); 2158 2159 args.rval().set(UndefinedValue()); 2160 2161 if (!args.get(0).isObject()) { 2162 JS_ReportErrorASCII(cx, "argument is not an object"); 2163 return false; 2164 } 2165 2166 RootedFunction func(cx, args[0].toObject().maybeUnwrapIf<JSFunction>()); 2167 if (func && func->isWasm()) { 2168 uint32_t funcIndex = func->wasmFuncIndex(); 2169 wasm::Instance& instance = func->wasmInstance(); 2170 if (funcIndex < instance.code().funcImports().length()) { 2171 JS_ReportErrorASCII(cx, "argument is an imported function"); 2172 return false; 2173 } 2174 wasm::Tier tier = instance.code().funcTier(funcIndex); 2175 return ReturnStringCopy(cx, args, wasm::ToString(tier)); 2176 } 2177 JS_ReportErrorASCII(cx, "argument is not an exported wasm function"); 2178 return false; 2179 } 2180 2181 static bool WasmDumpIon(JSContext* cx, unsigned argc, Value* vp) { 2182 if (!wasm::HasSupport(cx)) { 2183 JS_ReportErrorASCII(cx, "wasm support unavailable"); 2184 return false; 2185 } 2186 2187 CallArgs args = CallArgsFromVp(argc, vp); 2188 2189 args.rval().set(UndefinedValue()); 2190 2191 if (!args.get(0).isObject()) { 2192 JS_ReportErrorASCII(cx, "argument is not an object"); 2193 return false; 2194 } 2195 2196 uint32_t targetFuncIndex; 2197 if (!ToUint32(cx, args.get(1), &targetFuncIndex)) { 2198 JS_ReportErrorASCII(cx, "argument is not a func index"); 2199 return false; 2200 } 2201 2202 SharedMem<uint8_t*> dataPointer; 2203 size_t byteLength; 2204 if (!IsBufferSource(cx, args.get(0).toObjectOrNull(), /*allowShared*/ false, 2205 /*allowResizable*/ false, &dataPointer, &byteLength)) { 2206 JS_ReportErrorASCII(cx, "argument is not a buffer source"); 2207 return false; 2208 } 2209 2210 wasm::MutableBytes bytecode = cx->new_<wasm::ShareableBytes>(); 2211 if (!bytecode) { 2212 return false; 2213 } 2214 if (!bytecode->append(dataPointer.unwrap(), byteLength)) { 2215 ReportOutOfMemory(cx); 2216 return false; 2217 } 2218 2219 UniqueChars error; 2220 JSSprinter out(cx); 2221 if (!out.init()) { 2222 ReportOutOfMemory(cx); 2223 return false; 2224 } 2225 2226 if (!wasm::DumpIonFunctionInModule(*bytecode, targetFuncIndex, out, &error)) { 2227 if (error) { 2228 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 2229 JSMSG_WASM_COMPILE_ERROR, error.get()); 2230 return false; 2231 } 2232 ReportOutOfMemory(cx); 2233 return false; 2234 } 2235 2236 JSString* str = out.release(cx); 2237 if (!str) { 2238 ReportOutOfMemory(cx); 2239 return false; 2240 } 2241 args.rval().set(StringValue(str)); 2242 return true; 2243 } 2244 2245 enum class Flag { Tier2Complete, Deserialized, ParsedBranchHints }; 2246 2247 static bool WasmReturnFlag(JSContext* cx, unsigned argc, Value* vp, Flag flag) { 2248 CallArgs args = CallArgsFromVp(argc, vp); 2249 2250 if (!args.get(0).isObject()) { 2251 JS_ReportErrorASCII(cx, "argument is not an object"); 2252 return false; 2253 } 2254 2255 Rooted<WasmModuleObject*> module( 2256 cx, args[0].toObject().maybeUnwrapIf<WasmModuleObject>()); 2257 if (!module) { 2258 JS_ReportErrorASCII(cx, "argument is not a WebAssembly.Module"); 2259 return false; 2260 } 2261 2262 bool b; 2263 switch (flag) { 2264 case Flag::Tier2Complete: 2265 b = !module->module().testingTier2Active(); 2266 break; 2267 case Flag::Deserialized: 2268 b = module->module().loggingDeserialized(); 2269 break; 2270 case Flag::ParsedBranchHints: 2271 b = !module->module().codeMeta().branchHints.failedParse(); 2272 break; 2273 } 2274 2275 args.rval().set(BooleanValue(b)); 2276 return true; 2277 } 2278 2279 static bool wasmMetadataAnalysis(JSContext* cx, unsigned argc, Value* vp) { 2280 CallArgs args = CallArgsFromVp(argc, vp); 2281 2282 if (!args.get(0).isObject()) { 2283 JS_ReportErrorASCII(cx, "argument is not an object"); 2284 return false; 2285 } 2286 2287 if (args[0].toObject().is<WasmModuleObject>()) { 2288 HashMap<const char*, uint32_t, mozilla::CStringHasher, SystemAllocPolicy> 2289 hashmap = args[0] 2290 .toObject() 2291 .as<WasmModuleObject>() 2292 .module() 2293 .code() 2294 .metadataAnalysis(cx); 2295 if (hashmap.empty()) { 2296 JS_ReportErrorASCII(cx, "Metadata analysis has failed"); 2297 return false; 2298 } 2299 2300 // metadataAnalysis returned a map of {key, value} with various statistics 2301 // convert it into a dictionary to be used by JS 2302 Rooted<IdValueVector> props(cx, IdValueVector(cx)); 2303 2304 for (auto iter = hashmap.iter(); !iter.done(); iter.next()) { 2305 const auto* key = iter.get().key(); 2306 auto value = iter.get().value(); 2307 2308 JSString* string = JS_NewStringCopyZ(cx, key); 2309 if (!string) { 2310 return false; 2311 } 2312 2313 if (!props.append( 2314 IdValuePair(NameToId(string->asLinear().toPropertyName(cx)), 2315 NumberValue(value)))) { 2316 return false; 2317 } 2318 } 2319 2320 JSObject* results = NewPlainObjectWithUniqueNames(cx, props); 2321 if (!results) { 2322 return false; 2323 } 2324 args.rval().setObject(*results); 2325 2326 return true; 2327 } 2328 2329 JS_ReportErrorASCII( 2330 cx, "argument is not an exported wasm function or a wasm module"); 2331 2332 return false; 2333 } 2334 2335 static bool WasmHasTier2CompilationCompleted(JSContext* cx, unsigned argc, 2336 Value* vp) { 2337 return WasmReturnFlag(cx, argc, vp, Flag::Tier2Complete); 2338 } 2339 2340 static bool WasmLoadedFromCache(JSContext* cx, unsigned argc, Value* vp) { 2341 return WasmReturnFlag(cx, argc, vp, Flag::Deserialized); 2342 } 2343 2344 #ifdef ENABLE_WASM_BRANCH_HINTING 2345 static bool WasmParsedBranchHints(JSContext* cx, unsigned argc, Value* vp) { 2346 return WasmReturnFlag(cx, argc, vp, Flag::ParsedBranchHints); 2347 } 2348 #endif // ENABLE_WASM_BRANCH_HINTING 2349 2350 static bool WasmBuiltinI8VecMul(JSContext* cx, unsigned argc, Value* vp) { 2351 if (!wasm::HasSupport(cx)) { 2352 JS_ReportErrorASCII(cx, "wasm support unavailable"); 2353 return false; 2354 } 2355 2356 CallArgs args = CallArgsFromVp(argc, vp); 2357 2358 Rooted<WasmModuleObject*> module(cx); 2359 if (!wasm::CompileBuiltinModule(cx, wasm::BuiltinModuleId::SelfTest, nullptr, 2360 &module)) { 2361 return false; 2362 } 2363 args.rval().set(ObjectValue(*module.get())); 2364 return true; 2365 } 2366 2367 static bool WasmGcReadField(JSContext* cx, unsigned argc, Value* vp) { 2368 CallArgs args = CallArgsFromVp(argc, vp); 2369 RootedObject callee(cx, &args.callee()); 2370 2371 if (!args.requireAtLeast(cx, "wasmGcReadField", 2)) { 2372 return false; 2373 } 2374 2375 if (!args[0].isObject() || !args[0].toObject().is<WasmGcObject>()) { 2376 ReportUsageErrorASCII(cx, callee, 2377 "First argument must be a WebAssembly GC object"); 2378 return false; 2379 } 2380 2381 int32_t fieldIndex; 2382 if (!JS::ToInt32(cx, args[1], &fieldIndex) || fieldIndex < 0) { 2383 ReportUsageErrorASCII(cx, callee, 2384 "Second argument must be a non-negative integer"); 2385 return false; 2386 } 2387 2388 Rooted<WasmGcObject*> gcObject(cx, &args[0].toObject().as<WasmGcObject>()); 2389 Rooted<Value> gcValue(cx); 2390 if (!WasmGcObject::loadValue(cx, gcObject, jsid::Int(int32_t(fieldIndex)), 2391 &gcValue)) { 2392 return false; 2393 } 2394 2395 args.rval().set(gcValue); 2396 return true; 2397 } 2398 2399 static bool WasmGcArrayLength(JSContext* cx, unsigned argc, Value* vp) { 2400 CallArgs args = CallArgsFromVp(argc, vp); 2401 RootedObject callee(cx, &args.callee()); 2402 2403 if (!args.requireAtLeast(cx, "wasmGcArrayLength", 1)) { 2404 return false; 2405 } 2406 2407 if (!args[0].isObject() || !args[0].toObject().is<WasmArrayObject>()) { 2408 ReportUsageErrorASCII(cx, callee, 2409 "First argument must be a WebAssembly GC array"); 2410 return false; 2411 } 2412 2413 WasmArrayObject& arr = args[0].toObject().as<WasmArrayObject>(); 2414 args.rval().setInt32(int32_t(arr.numElements_)); 2415 return true; 2416 } 2417 2418 static bool LargeArrayBufferSupported(JSContext* cx, unsigned argc, Value* vp) { 2419 CallArgs args = CallArgsFromVp(argc, vp); 2420 args.rval().setBoolean(ArrayBufferObject::ByteLengthLimit > 2421 ArrayBufferObject::ByteLengthLimitForSmallBuffer); 2422 return true; 2423 } 2424 2425 static bool IsLazyFunction(JSContext* cx, unsigned argc, Value* vp) { 2426 CallArgs args = CallArgsFromVp(argc, vp); 2427 if (args.length() != 1) { 2428 JS_ReportErrorASCII(cx, "The function takes exactly one argument."); 2429 return false; 2430 } 2431 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) { 2432 JS_ReportErrorASCII(cx, "The first argument should be a function."); 2433 return false; 2434 } 2435 JSFunction* fun = &args[0].toObject().as<JSFunction>(); 2436 args.rval().setBoolean(fun->isInterpreted() && !fun->hasBytecode()); 2437 return true; 2438 } 2439 2440 static bool IsRelazifiableFunction(JSContext* cx, unsigned argc, Value* vp) { 2441 CallArgs args = CallArgsFromVp(argc, vp); 2442 if (args.length() != 1) { 2443 JS_ReportErrorASCII(cx, "The function takes exactly one argument."); 2444 return false; 2445 } 2446 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) { 2447 JS_ReportErrorASCII(cx, "The first argument should be a function."); 2448 return false; 2449 } 2450 2451 JSFunction* fun = &args[0].toObject().as<JSFunction>(); 2452 args.rval().setBoolean(fun->hasBytecode() && 2453 fun->nonLazyScript()->allowRelazify()); 2454 return true; 2455 } 2456 2457 static bool IsCollectingDelazifications(JSContext* cx, unsigned argc, 2458 Value* vp) { 2459 CallArgs args = CallArgsFromVp(argc, vp); 2460 if (args.length() != 1) { 2461 JS_ReportErrorASCII(cx, "The function takes exactly one argument."); 2462 return false; 2463 } 2464 2465 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) { 2466 JS_ReportErrorASCII(cx, "The first argument should be a function."); 2467 return false; 2468 } 2469 2470 if (fuzzingSafe) { 2471 // When running code concurrently to fill-up the stencil cache, the content 2472 // is not garanteed to be present. 2473 args.rval().setBoolean(false); 2474 return true; 2475 } 2476 2477 JSFunction* fun = &args[0].toObject().as<JSFunction>(); 2478 BaseScript* script = fun->baseScript(); 2479 args.rval().setBoolean( 2480 bool(script->sourceObject()->isCollectingDelazifications())); 2481 return true; 2482 } 2483 2484 static bool IsDelazificationsPopulated(JSContext* cx, unsigned argc, 2485 Value* vp) { 2486 CallArgs args = CallArgsFromVp(argc, vp); 2487 if (args.length() != 1) { 2488 JS_ReportErrorASCII(cx, "The function takes exactly one argument."); 2489 return false; 2490 } 2491 2492 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) { 2493 JS_ReportErrorASCII(cx, "The first argument should be a function."); 2494 return false; 2495 } 2496 2497 if (fuzzingSafe) { 2498 // When running code concurrently to fill-up the stencil cache, the content 2499 // is not garanteed to be present. 2500 args.rval().setBoolean(false); 2501 return true; 2502 } 2503 2504 JSFunction* fun = &args[0].toObject().as<JSFunction>(); 2505 BaseScript* script = fun->baseScript(); 2506 RefPtr<frontend::InitialStencilAndDelazifications> stencils = 2507 script->sourceObject()->maybeGetStencils(); 2508 if (!stencils) { 2509 args.rval().setBoolean(false); 2510 return true; 2511 } 2512 2513 const frontend::CompilationStencil* stencil = 2514 stencils->getDelazificationFor(script->extent()); 2515 args.rval().setBoolean(bool(stencil)); 2516 return true; 2517 } 2518 2519 static bool WaitForDelazificationOf(JSContext* cx, unsigned argc, Value* vp) { 2520 CallArgs args = CallArgsFromVp(argc, vp); 2521 if (args.length() != 1) { 2522 JS_ReportErrorASCII(cx, "The function takes exactly one argument."); 2523 return false; 2524 } 2525 2526 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) { 2527 JS_ReportErrorASCII(cx, "The first argument should be a function."); 2528 return false; 2529 } 2530 args.rval().setUndefined(); 2531 2532 JSFunction* fun = &args[0].toObject().as<JSFunction>(); 2533 BaseScript* script = fun->baseScript(); 2534 2535 if (!script->sourceObject()->isSharingDelazifications()) { 2536 return true; 2537 } 2538 2539 RefPtr<frontend::InitialStencilAndDelazifications> stencils = 2540 script->sourceObject()->maybeGetStencils(); 2541 2542 AutoLockHelperThreadState lock; 2543 if (!HelperThreadState().isInitialized(lock)) { 2544 return true; 2545 } 2546 2547 while (true) { 2548 const frontend::CompilationStencil* stencil = 2549 stencils->getDelazificationFor(script->extent()); 2550 if (stencil) { 2551 break; 2552 } 2553 2554 HelperThreadState().wait(lock); 2555 } 2556 return true; 2557 } 2558 2559 static bool HasSameBytecodeData(JSContext* cx, unsigned argc, Value* vp) { 2560 CallArgs args = CallArgsFromVp(argc, vp); 2561 if (args.length() != 2) { 2562 JS_ReportErrorASCII(cx, "The function takes exactly two argument."); 2563 return false; 2564 } 2565 2566 auto GetSharedData = [](JSContext* cx, 2567 HandleValue v) -> SharedImmutableScriptData* { 2568 if (!v.isObject()) { 2569 JS_ReportErrorASCII(cx, "The arguments must be interpreted functions."); 2570 return nullptr; 2571 } 2572 2573 RootedObject obj(cx, CheckedUnwrapDynamic(&v.toObject(), cx)); 2574 if (!obj) { 2575 return nullptr; 2576 } 2577 2578 if (!obj->is<JSFunction>() || !obj->as<JSFunction>().isInterpreted()) { 2579 JS_ReportErrorASCII(cx, "The arguments must be interpreted functions."); 2580 return nullptr; 2581 } 2582 2583 AutoRealm ar(cx, obj); 2584 RootedFunction fun(cx, &obj->as<JSFunction>()); 2585 RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun)); 2586 if (!script) { 2587 return nullptr; 2588 } 2589 2590 MOZ_ASSERT(script->sharedData()); 2591 return script->sharedData(); 2592 }; 2593 2594 // NOTE: We use RefPtr below to keep the data alive across possible GC since 2595 // the functions may be in different Zones. 2596 2597 RefPtr<SharedImmutableScriptData> sharedData1 = GetSharedData(cx, args[0]); 2598 if (!sharedData1) { 2599 return false; 2600 } 2601 2602 RefPtr<SharedImmutableScriptData> sharedData2 = GetSharedData(cx, args[1]); 2603 if (!sharedData2) { 2604 return false; 2605 } 2606 2607 args.rval().setBoolean(sharedData1 == sharedData2); 2608 return true; 2609 } 2610 2611 static bool InternalConst(JSContext* cx, unsigned argc, Value* vp) { 2612 CallArgs args = CallArgsFromVp(argc, vp); 2613 if (args.length() == 0) { 2614 JS_ReportErrorASCII(cx, "the function takes exactly one argument"); 2615 return false; 2616 } 2617 2618 JSString* str = ToString(cx, args[0]); 2619 if (!str) { 2620 return false; 2621 } 2622 JSLinearString* linear = JS_EnsureLinearString(cx, str); 2623 if (!linear) { 2624 return false; 2625 } 2626 2627 if (JS_LinearStringEqualsLiteral(linear, "MARK_STACK_BASE_CAPACITY")) { 2628 args.rval().setNumber(uint32_t(js::MARK_STACK_BASE_CAPACITY)); 2629 } else { 2630 JS_ReportErrorASCII(cx, "unknown const name"); 2631 return false; 2632 } 2633 return true; 2634 } 2635 2636 static bool GCPreserveCode(JSContext* cx, unsigned argc, Value* vp) { 2637 CallArgs args = CallArgsFromVp(argc, vp); 2638 2639 if (args.length() != 0) { 2640 RootedObject callee(cx, &args.callee()); 2641 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 2642 return false; 2643 } 2644 2645 cx->runtime()->gc.setAlwaysPreserveCode(); 2646 2647 args.rval().setUndefined(); 2648 return true; 2649 } 2650 2651 #ifdef JS_GC_ZEAL 2652 2653 static bool ParseGCZealMode(JSContext* cx, const CallArgs& args, 2654 js::gc::GCRuntime::ZealSettings* zeal) { 2655 JSString* modestr = ToString(cx, args.get(0)); 2656 if (!modestr) { 2657 return false; 2658 } 2659 2660 UniqueChars mode = EncodeLatin1(cx, modestr); 2661 if (!mode) { 2662 return false; 2663 } 2664 bool invalid; 2665 if (!cx->runtime()->gc.parseZeal(mode.get(), strlen(mode.get()), zeal, 2666 &invalid)) { 2667 js::ReportOutOfMemory(cx); 2668 return false; 2669 } 2670 if (invalid) { 2671 JS_ReportErrorASCII(cx, "invalid gczeal argument"); 2672 return false; 2673 } 2674 2675 return true; 2676 } 2677 2678 static bool GCZeal(JSContext* cx, unsigned argc, Value* vp) { 2679 CallArgs args = CallArgsFromVp(argc, vp); 2680 2681 if (args.length() > 2) { 2682 RootedObject callee(cx, &args.callee()); 2683 ReportUsageErrorASCII(cx, callee, "Too many arguments"); 2684 return false; 2685 } 2686 2687 if (args.length() == 0) { 2688 uint32_t zealBits, unused1, unused2; 2689 cx->runtime()->gc.getZealBits(&zealBits, &unused1, &unused2); 2690 args.rval().setNumber(zealBits); 2691 return true; 2692 } 2693 2694 js::gc::GCRuntime::ZealSettings zeal; 2695 if (!ParseGCZealMode(cx, args, &zeal)) { 2696 return false; 2697 } 2698 2699 Maybe<uint32_t> forceFrequency; 2700 if (args.length() >= 2) { 2701 uint32_t frequency; 2702 if (!ToUint32(cx, args.get(1), &frequency)) { 2703 return false; 2704 } 2705 forceFrequency.emplace(frequency); 2706 } 2707 2708 for (auto [mode, frequency] : zeal) { 2709 JS::SetGCZeal(cx, mode, forceFrequency.valueOr(frequency)); 2710 } 2711 2712 args.rval().setUndefined(); 2713 return true; 2714 } 2715 2716 static bool UnsetGCZeal(JSContext* cx, unsigned argc, Value* vp) { 2717 CallArgs args = CallArgsFromVp(argc, vp); 2718 2719 if (args.length() > 1) { 2720 RootedObject callee(cx, &args.callee()); 2721 ReportUsageErrorASCII(cx, callee, "Too many arguments"); 2722 return false; 2723 } 2724 2725 js::gc::GCRuntime::ZealSettings zeal; 2726 if (!ParseGCZealMode(cx, args, &zeal)) { 2727 return false; 2728 } 2729 2730 for (auto [mode, _frequency] : zeal) { 2731 JS::UnsetGCZeal(cx, mode); 2732 } 2733 2734 args.rval().setUndefined(); 2735 return true; 2736 } 2737 2738 static bool ScheduleGC(JSContext* cx, unsigned argc, Value* vp) { 2739 CallArgs args = CallArgsFromVp(argc, vp); 2740 2741 if (args.length() > 1) { 2742 RootedObject callee(cx, &args.callee()); 2743 ReportUsageErrorASCII(cx, callee, "Too many arguments"); 2744 return false; 2745 } 2746 2747 if (args.length() == 0) { 2748 /* Fetch next zeal trigger only. */ 2749 } else if (args[0].isNumber()) { 2750 /* Schedule a GC to happen after |arg| allocations. */ 2751 JS::ScheduleGC(cx, std::max(int(args[0].toNumber()), 0)); 2752 } else { 2753 RootedObject callee(cx, &args.callee()); 2754 ReportUsageErrorASCII(cx, callee, "Bad argument - expecting number"); 2755 return false; 2756 } 2757 2758 uint32_t zealBits; 2759 uint32_t freq; 2760 uint32_t next; 2761 JS::GetGCZealBits(cx, &zealBits, &freq, &next); 2762 args.rval().setInt32(next); 2763 return true; 2764 } 2765 2766 static bool SelectForGC(JSContext* cx, unsigned argc, Value* vp) { 2767 CallArgs args = CallArgsFromVp(argc, vp); 2768 2769 /* 2770 * The selectedForMarking set is intended to be manually marked at slice 2771 * start to detect missing pre-barriers. It is invalid for nursery things 2772 * to be in the set, so evict the nursery before adding items. 2773 */ 2774 cx->runtime()->gc.evictNursery(); 2775 2776 for (unsigned i = 0; i < args.length(); i++) { 2777 if (args[i].isObject()) { 2778 if (!cx->runtime()->gc.selectForMarking(&args[i].toObject())) { 2779 ReportOutOfMemory(cx); 2780 return false; 2781 } 2782 } 2783 } 2784 2785 args.rval().setUndefined(); 2786 return true; 2787 } 2788 2789 static bool VerifyPreBarriers(JSContext* cx, unsigned argc, Value* vp) { 2790 CallArgs args = CallArgsFromVp(argc, vp); 2791 2792 if (args.length() > 0) { 2793 RootedObject callee(cx, &args.callee()); 2794 ReportUsageErrorASCII(cx, callee, "Too many arguments"); 2795 return false; 2796 } 2797 2798 gc::VerifyBarriers(cx->runtime(), gc::PreBarrierVerifier); 2799 args.rval().setUndefined(); 2800 return true; 2801 } 2802 2803 static bool VerifyPostBarriers(JSContext* cx, unsigned argc, Value* vp) { 2804 CallArgs args = CallArgsFromVp(argc, vp); 2805 2806 if (args.length()) { 2807 RootedObject callee(cx, &args.callee()); 2808 ReportUsageErrorASCII(cx, callee, "Too many arguments"); 2809 return false; 2810 } 2811 2812 gc::VerifyBarriers(cx->runtime(), gc::PostBarrierVerifier); 2813 args.rval().setUndefined(); 2814 return true; 2815 } 2816 2817 static bool CurrentGC(JSContext* cx, unsigned argc, Value* vp) { 2818 CallArgs args = CallArgsFromVp(argc, vp); 2819 2820 if (args.length() != 0) { 2821 RootedObject callee(cx, &args.callee()); 2822 ReportUsageErrorASCII(cx, callee, "Too many arguments"); 2823 return false; 2824 } 2825 2826 RootedObject result(cx, JS_NewPlainObject(cx)); 2827 if (!result) { 2828 return false; 2829 } 2830 2831 js::gc::GCRuntime& gc = cx->runtime()->gc; 2832 const char* state = StateName(gc.state()); 2833 2834 RootedString str(cx, JS_NewStringCopyZ(cx, state)); 2835 if (!str) { 2836 return false; 2837 } 2838 RootedValue val(cx, StringValue(str)); 2839 if (!JS_DefineProperty(cx, result, "incrementalState", val, 2840 JSPROP_ENUMERATE)) { 2841 return false; 2842 } 2843 2844 if (gc.state() == js::gc::State::Sweep) { 2845 val = Int32Value(gc.getCurrentSweepGroupIndex()); 2846 if (!JS_DefineProperty(cx, result, "sweepGroup", val, JSPROP_ENUMERATE)) { 2847 return false; 2848 } 2849 } 2850 2851 val = BooleanValue(gc.isIncrementalGCInProgress() && gc.isShrinkingGC()); 2852 if (!JS_DefineProperty(cx, result, "isShrinking", val, JSPROP_ENUMERATE)) { 2853 return false; 2854 } 2855 2856 val = Int32Value(gc.gcNumber()); 2857 if (!JS_DefineProperty(cx, result, "number", val, JSPROP_ENUMERATE)) { 2858 return false; 2859 } 2860 2861 val = Int32Value(gc.minorGCCount()); 2862 if (!JS_DefineProperty(cx, result, "minorCount", val, JSPROP_ENUMERATE)) { 2863 return false; 2864 } 2865 2866 val = Int32Value(gc.majorGCCount()); 2867 if (!JS_DefineProperty(cx, result, "majorCount", val, JSPROP_ENUMERATE)) { 2868 return false; 2869 } 2870 2871 val = BooleanValue(gc.isFullGc()); 2872 if (!JS_DefineProperty(cx, result, "isFull", val, JSPROP_ENUMERATE)) { 2873 return false; 2874 } 2875 2876 val = BooleanValue(gc.isCompactingGc()); 2877 if (!JS_DefineProperty(cx, result, "isCompacting", val, JSPROP_ENUMERATE)) { 2878 return false; 2879 } 2880 2881 # ifdef DEBUG 2882 val = Int32Value(gc.testMarkQueuePos()); 2883 if (!JS_DefineProperty(cx, result, "queuePos", val, JSPROP_ENUMERATE)) { 2884 return false; 2885 } 2886 # endif 2887 2888 val = BooleanValue(gc.finishMarkingDuringSweeping); 2889 if (!JS_DefineProperty(cx, result, "finishMarkingDuringSweeping", val, 2890 JSPROP_ENUMERATE)) { 2891 return false; 2892 } 2893 2894 args.rval().setObject(*result); 2895 return true; 2896 } 2897 2898 static bool DeterministicGC(JSContext* cx, unsigned argc, Value* vp) { 2899 CallArgs args = CallArgsFromVp(argc, vp); 2900 2901 if (args.length() != 1) { 2902 RootedObject callee(cx, &args.callee()); 2903 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 2904 return false; 2905 } 2906 2907 cx->runtime()->gc.setDeterministic(ToBoolean(args[0])); 2908 args.rval().setUndefined(); 2909 return true; 2910 } 2911 2912 static bool DumpGCArenaInfo(JSContext* cx, unsigned argc, Value* vp) { 2913 CallArgs args = CallArgsFromVp(argc, vp); 2914 js::gc::DumpArenaInfo(); 2915 args.rval().setUndefined(); 2916 return true; 2917 } 2918 2919 static bool SetMarkStackLimit(JSContext* cx, unsigned argc, Value* vp) { 2920 CallArgs args = CallArgsFromVp(argc, vp); 2921 if (args.length() != 1) { 2922 RootedObject callee(cx, &args.callee()); 2923 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 2924 return false; 2925 } 2926 2927 int32_t value; 2928 if (!ToInt32(cx, args[0], &value) || value <= 0) { 2929 JS_ReportErrorASCII(cx, "Bad argument to SetMarkStackLimit"); 2930 return false; 2931 } 2932 2933 if (JS::IsIncrementalGCInProgress(cx)) { 2934 JS_ReportErrorASCII( 2935 cx, "Attempt to set markStackLimit while a GC is in progress"); 2936 return false; 2937 } 2938 2939 JSRuntime* runtime = cx->runtime(); 2940 AutoLockGC lock(runtime); 2941 runtime->gc.setMarkStackLimit(value, lock); 2942 args.rval().setUndefined(); 2943 return true; 2944 } 2945 2946 #endif /* JS_GC_ZEAL */ 2947 2948 static bool SetMallocMaxDirtyPageModifier(JSContext* cx, unsigned argc, 2949 Value* vp) { 2950 CallArgs args = CallArgsFromVp(argc, vp); 2951 if (args.length() != 1) { 2952 RootedObject callee(cx, &args.callee()); 2953 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 2954 return false; 2955 } 2956 2957 constexpr int32_t MinSupportedValue = -5; 2958 constexpr int32_t MaxSupportedValue = 16; 2959 2960 int32_t value; 2961 if (!ToInt32(cx, args[0], &value)) { 2962 return false; 2963 } 2964 if (value < MinSupportedValue || value > MaxSupportedValue) { 2965 JS_ReportErrorASCII(cx, "Bad argument to setMallocMaxDirtyPageModifier"); 2966 return false; 2967 } 2968 2969 #ifdef MOZ_MEMORY 2970 moz_set_max_dirty_page_modifier(value); 2971 #endif 2972 2973 args.rval().setUndefined(); 2974 return true; 2975 } 2976 2977 static bool GCState(JSContext* cx, unsigned argc, Value* vp) { 2978 CallArgs args = CallArgsFromVp(argc, vp); 2979 2980 if (args.length() > 1) { 2981 RootedObject callee(cx, &args.callee()); 2982 ReportUsageErrorASCII(cx, callee, "Too many arguments"); 2983 return false; 2984 } 2985 2986 const char* state; 2987 2988 if (args.length() == 1) { 2989 if (!args[0].isObject()) { 2990 RootedObject callee(cx, &args.callee()); 2991 ReportUsageErrorASCII(cx, callee, "Expected object"); 2992 return false; 2993 } 2994 2995 JSObject* obj = UncheckedUnwrap(&args[0].toObject()); 2996 state = gc::StateName(obj->zone()->gcState()); 2997 } else { 2998 state = gc::StateName(cx->runtime()->gc.state()); 2999 } 3000 3001 return ReturnStringCopy(cx, args, state); 3002 } 3003 3004 static bool ScheduleZoneForGC(JSContext* cx, unsigned argc, Value* vp) { 3005 CallArgs args = CallArgsFromVp(argc, vp); 3006 3007 if (args.length() != 1) { 3008 RootedObject callee(cx, &args.callee()); 3009 ReportUsageErrorASCII(cx, callee, "Expecting a single argument"); 3010 return false; 3011 } 3012 3013 if (args[0].isObject()) { 3014 // Ensure that |zone| is collected during the next GC. 3015 Zone* zone = UncheckedUnwrap(&args[0].toObject())->zone(); 3016 PrepareZoneForGC(cx, zone); 3017 } else if (args[0].isString()) { 3018 // This allows us to schedule the atoms zone for GC. 3019 Zone* zone = args[0].toString()->zoneFromAnyThread(); 3020 if (!CurrentThreadCanAccessZone(zone)) { 3021 RootedObject callee(cx, &args.callee()); 3022 ReportUsageErrorASCII(cx, callee, "Specified zone not accessible for GC"); 3023 return false; 3024 } 3025 PrepareZoneForGC(cx, zone); 3026 } else { 3027 RootedObject callee(cx, &args.callee()); 3028 ReportUsageErrorASCII(cx, callee, 3029 "Bad argument - expecting object or string"); 3030 return false; 3031 } 3032 3033 args.rval().setUndefined(); 3034 return true; 3035 } 3036 3037 static bool StartGC(JSContext* cx, unsigned argc, Value* vp) { 3038 CallArgs args = CallArgsFromVp(argc, vp); 3039 3040 if (args.length() > 2) { 3041 RootedObject callee(cx, &args.callee()); 3042 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 3043 return false; 3044 } 3045 3046 auto budget = SliceBudget::unlimited(); 3047 if (args.length() >= 1) { 3048 uint32_t work = 0; 3049 if (!ToUint32(cx, args[0], &work)) { 3050 return false; 3051 } 3052 budget = SliceBudget(WorkBudget(work)); 3053 } 3054 3055 bool shrinking = false; 3056 if (args.length() >= 2) { 3057 Value arg = args[1]; 3058 if (arg.isString()) { 3059 if (!JS_StringEqualsLiteral(cx, arg.toString(), "shrinking", 3060 &shrinking)) { 3061 return false; 3062 } 3063 } 3064 } 3065 3066 JSRuntime* rt = cx->runtime(); 3067 if (rt->gc.isIncrementalGCInProgress()) { 3068 RootedObject callee(cx, &args.callee()); 3069 JS_ReportErrorASCII(cx, "Incremental GC already in progress"); 3070 return false; 3071 } 3072 3073 JS::GCOptions options = 3074 shrinking ? JS::GCOptions::Shrink : JS::GCOptions::Normal; 3075 rt->gc.startDebugGC(options, budget); 3076 3077 args.rval().setUndefined(); 3078 return true; 3079 } 3080 3081 static bool FinishGC(JSContext* cx, unsigned argc, Value* vp) { 3082 CallArgs args = CallArgsFromVp(argc, vp); 3083 3084 if (args.length() > 0) { 3085 RootedObject callee(cx, &args.callee()); 3086 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 3087 return false; 3088 } 3089 3090 JSRuntime* rt = cx->runtime(); 3091 if (rt->gc.isIncrementalGCInProgress()) { 3092 rt->gc.finishGC(JS::GCReason::DEBUG_GC); 3093 } 3094 3095 args.rval().setUndefined(); 3096 return true; 3097 } 3098 3099 static bool GCSlice(JSContext* cx, unsigned argc, Value* vp) { 3100 CallArgs args = CallArgsFromVp(argc, vp); 3101 3102 if (args.length() > 2) { 3103 RootedObject callee(cx, &args.callee()); 3104 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 3105 return false; 3106 } 3107 3108 auto budget = SliceBudget::unlimited(); 3109 if (args.length() >= 1) { 3110 uint32_t work = 0; 3111 if (!ToUint32(cx, args[0], &work)) { 3112 return false; 3113 } 3114 budget = SliceBudget(WorkBudget(work)); 3115 } 3116 3117 bool dontStart = false; 3118 if (args.get(1).isObject()) { 3119 RootedObject options(cx, &args[1].toObject()); 3120 RootedValue v(cx); 3121 if (!JS_GetProperty(cx, options, "dontStart", &v)) { 3122 return false; 3123 } 3124 dontStart = ToBoolean(v); 3125 } 3126 3127 JSRuntime* rt = cx->runtime(); 3128 if (rt->gc.isIncrementalGCInProgress()) { 3129 rt->gc.debugGCSlice(budget); 3130 } else if (!dontStart) { 3131 rt->gc.startDebugGC(JS::GCOptions::Normal, budget); 3132 } 3133 3134 args.rval().setUndefined(); 3135 return true; 3136 } 3137 3138 static bool AbortGC(JSContext* cx, unsigned argc, Value* vp) { 3139 CallArgs args = CallArgsFromVp(argc, vp); 3140 3141 if (args.length() != 0) { 3142 RootedObject callee(cx, &args.callee()); 3143 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 3144 return false; 3145 } 3146 3147 JS::AbortIncrementalGC(cx); 3148 args.rval().setUndefined(); 3149 return true; 3150 } 3151 3152 static bool FullCompartmentChecks(JSContext* cx, unsigned argc, Value* vp) { 3153 CallArgs args = CallArgsFromVp(argc, vp); 3154 3155 if (args.length() != 1) { 3156 RootedObject callee(cx, &args.callee()); 3157 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 3158 return false; 3159 } 3160 3161 cx->runtime()->gc.setFullCompartmentChecks(ToBoolean(args[0])); 3162 args.rval().setUndefined(); 3163 return true; 3164 } 3165 3166 static bool IsAtomMarked(JSContext* cx, unsigned argc, Value* vp) { 3167 CallArgs args = CallArgsFromVp(argc, vp); 3168 3169 RootedObject callee(cx, &args.callee()); 3170 if (args.length() != 2) { 3171 ReportUsageErrorASCII(cx, callee, "Expected two arguments"); 3172 return false; 3173 } 3174 3175 if (!args[0].isObject()) { 3176 ReportUsageErrorASCII(cx, callee, 3177 "Expected an object as the first argument"); 3178 return false; 3179 } 3180 Zone* zone = UncheckedUnwrap(&args[0].toObject())->zone(); 3181 3182 Maybe<bool> result; 3183 gc::GCRuntime* gc = &cx->runtime()->gc; 3184 if (args[1].isSymbol()) { 3185 result = Some(gc->atomMarking.atomIsMarked(zone, args[1].toSymbol())); 3186 } else if (args[1].isString()) { 3187 JSString* str = args[1].toString(); 3188 if (str->isAtom()) { 3189 result = Some(gc->atomMarking.atomIsMarked(zone, &str->asAtom())); 3190 } 3191 } 3192 3193 if (result.isNothing()) { 3194 ReportUsageErrorASCII(cx, callee, 3195 "Expected an atom as the second argument"); 3196 return false; 3197 } 3198 3199 args.rval().setBoolean(result.value()); 3200 return true; 3201 } 3202 3203 static bool GetAtomMarkIndex(JSContext* cx, unsigned argc, Value* vp) { 3204 CallArgs args = CallArgsFromVp(argc, vp); 3205 RootedObject callee(cx, &args.callee()); 3206 3207 if (args.length() != 1 || !args[0].isGCThing() || 3208 !args[0].toGCThing()->zone()->isAtomsZone()) { 3209 ReportUsageErrorASCII(cx, callee, 3210 "Expected an atom as the single argument"); 3211 return false; 3212 } 3213 3214 gc::TenuredCell* atom = &args[0].toGCThing()->asTenured(); 3215 if ((atom->is<JSString>() && 3216 atom->as<JSString>()->isPermanentAndMayBeShared()) || 3217 (atom->is<JS::Symbol>() && 3218 atom->as<JS::Symbol>()->isPermanentAndMayBeShared())) { 3219 ReportUsageErrorASCII( 3220 cx, callee, "Atom marking bitmap is not used for permanent atoms"); 3221 return false; 3222 } 3223 3224 if (atom->is<JSString>() && atom->as<JSString>()->asAtom().isPinned()) { 3225 ReportUsageErrorASCII(cx, callee, 3226 "Atom marking bitmap is not used for pinned atoms"); 3227 return false; 3228 } 3229 3230 size_t index = gc::AtomMarkingRuntime::getAtomBit(atom); 3231 MOZ_RELEASE_ASSERT(index <= INT32_MAX); 3232 args.rval().setInt32(index); 3233 return true; 3234 } 3235 3236 static bool GetAtomMarkColor(JSContext* cx, unsigned argc, Value* vp) { 3237 CallArgs args = CallArgsFromVp(argc, vp); 3238 RootedObject callee(cx, &args.callee()); 3239 3240 if (args.length() != 2) { 3241 ReportUsageErrorASCII(cx, callee, "Expected two arguments"); 3242 return false; 3243 } 3244 3245 if (!args[0].isObject()) { 3246 ReportUsageErrorASCII(cx, callee, 3247 "Expected an object as the first argument"); 3248 return false; 3249 } 3250 Zone* zone = UncheckedUnwrap(&args[0].toObject())->zone(); 3251 3252 if (!args[1].isInt32() || args[1].toInt32() < 0) { 3253 ReportUsageErrorASCII(cx, callee, 3254 "Expected a positive integer as the second argument"); 3255 return false; 3256 } 3257 size_t index = args[1].toInt32(); 3258 3259 gc::GCRuntime* gc = &cx->runtime()->gc; 3260 gc::CellColor color = gc->atomMarking.getAtomMarkColorForIndex(zone, index); 3261 RootedString name(cx, JS_NewStringCopyZ(cx, gc::CellColorName(color))); 3262 if (!name) { 3263 return false; 3264 } 3265 3266 args.rval().setString(name); 3267 return true; 3268 } 3269 3270 static WeakMapObject* MaybeWeakMapObject(const Value& value) { 3271 if (!value.isObject()) { 3272 return nullptr; 3273 } 3274 3275 JSObject* obj = UncheckedUnwrap(&value.toObject()); 3276 if (!obj->is<WeakMapObject>()) { 3277 return nullptr; 3278 } 3279 3280 return &obj->as<WeakMapObject>(); 3281 } 3282 3283 static bool NondeterministicGetWeakMapSize(JSContext* cx, unsigned argc, 3284 Value* vp) { 3285 CallArgs args = CallArgsFromVp(argc, vp); 3286 3287 WeakMapObject* weakmap = MaybeWeakMapObject(args.get(0)); 3288 if (args.length() != 1 || !weakmap) { 3289 RootedObject callee(cx, &args.callee()); 3290 ReportUsageErrorASCII(cx, callee, "Expected a single WeakMap argument "); 3291 return false; 3292 } 3293 3294 args.rval().setNumber(weakmap->nondeterministicGetSize()); 3295 return true; 3296 } 3297 3298 static bool NondeterministicGetWeakMapKeys(JSContext* cx, unsigned argc, 3299 Value* vp) { 3300 CallArgs args = CallArgsFromVp(argc, vp); 3301 3302 Rooted<WeakMapObject*> weakmap(cx, MaybeWeakMapObject(args.get(0))); 3303 if (args.length() != 1 || !weakmap) { 3304 RootedObject callee(cx, &args.callee()); 3305 ReportUsageErrorASCII(cx, callee, "Expected a single WeakMap argument "); 3306 return false; 3307 } 3308 3309 RootedObject arr(cx); 3310 if (!JS_NondeterministicGetWeakMapKeys(cx, weakmap, &arr)) { 3311 return false; 3312 } 3313 3314 MOZ_ASSERT(arr); 3315 args.rval().setObject(*arr); 3316 return true; 3317 } 3318 3319 static bool GrayBitsValid(JSContext* cx, unsigned argc, Value* vp) { 3320 CallArgs args = CallArgsFromVp(argc, vp); 3321 args.rval().setBoolean(cx->runtime()->gc.areGrayBitsValid()); 3322 return true; 3323 } 3324 3325 static bool SetGrayBitsInvalid(JSContext* cx, unsigned argc, Value* vp) { 3326 CallArgs args = CallArgsFromVp(argc, vp); 3327 cx->runtime()->gc.setGrayBitsInvalid(); 3328 args.rval().setUndefined(); 3329 return true; 3330 } 3331 3332 class HasChildTracer final : public JS::CallbackTracer { 3333 RootedValue child_; 3334 bool found_; 3335 3336 void onChild(JS::GCCellPtr thing, const char* name) override { 3337 if (thing.asCell() == child_.toGCThing()) { 3338 found_ = true; 3339 } 3340 } 3341 3342 public: 3343 HasChildTracer(JSContext* cx, HandleValue child) 3344 : JS::CallbackTracer(cx, JS::TracerKind::Callback, 3345 JS::WeakMapTraceAction::TraceKeysAndValues), 3346 child_(cx, child), 3347 found_(false) {} 3348 3349 bool found() const { return found_; } 3350 }; 3351 3352 static bool HasChild(JSContext* cx, unsigned argc, Value* vp) { 3353 CallArgs args = CallArgsFromVp(argc, vp); 3354 RootedValue parent(cx, args.get(0)); 3355 RootedValue child(cx, args.get(1)); 3356 3357 if (!parent.isGCThing() || !child.isGCThing()) { 3358 args.rval().setBoolean(false); 3359 return true; 3360 } 3361 3362 HasChildTracer trc(cx, child); 3363 TraceChildren(&trc, JS::GCCellPtr(parent.toGCThing(), parent.traceKind())); 3364 args.rval().setBoolean(trc.found()); 3365 return true; 3366 } 3367 3368 static bool SetSavedStacksRNGState(JSContext* cx, unsigned argc, Value* vp) { 3369 CallArgs args = CallArgsFromVp(argc, vp); 3370 if (!args.requireAtLeast(cx, "setSavedStacksRNGState", 1)) { 3371 return false; 3372 } 3373 3374 int32_t seed; 3375 if (!ToInt32(cx, args[0], &seed)) { 3376 return false; 3377 } 3378 3379 // Either one or the other of the seed arguments must be non-zero; 3380 // make this true no matter what value 'seed' has. 3381 cx->realm()->savedStacks().setRNGState(seed, (seed + 1) * 33); 3382 return true; 3383 } 3384 3385 static bool GetSavedFrameCount(JSContext* cx, unsigned argc, Value* vp) { 3386 CallArgs args = CallArgsFromVp(argc, vp); 3387 args.rval().setNumber(cx->realm()->savedStacks().count()); 3388 return true; 3389 } 3390 3391 static bool ClearSavedFrames(JSContext* cx, unsigned argc, Value* vp) { 3392 CallArgs args = CallArgsFromVp(argc, vp); 3393 3394 js::SavedStacks& savedStacks = cx->realm()->savedStacks(); 3395 savedStacks.clear(); 3396 3397 for (ActivationIterator iter(cx); !iter.done(); ++iter) { 3398 iter->clearLiveSavedFrameCache(); 3399 } 3400 3401 args.rval().setUndefined(); 3402 return true; 3403 } 3404 3405 static bool SaveStack(JSContext* cx, unsigned argc, Value* vp) { 3406 CallArgs args = CallArgsFromVp(argc, vp); 3407 3408 JS::StackCapture capture((JS::AllFrames())); 3409 if (args.length() >= 1) { 3410 double maxDouble; 3411 if (!ToNumber(cx, args[0], &maxDouble)) { 3412 return false; 3413 } 3414 if (std::isnan(maxDouble) || maxDouble < 0 || maxDouble > UINT32_MAX) { 3415 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0], 3416 nullptr, "not a valid maximum frame count"); 3417 return false; 3418 } 3419 uint32_t max = uint32_t(maxDouble); 3420 if (max > 0) { 3421 capture = JS::StackCapture(JS::MaxFrames(max)); 3422 } 3423 } 3424 3425 RootedObject compartmentObject(cx); 3426 if (args.length() >= 2) { 3427 if (!args[1].isObject()) { 3428 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0], 3429 nullptr, "not an object"); 3430 return false; 3431 } 3432 compartmentObject = UncheckedUnwrap(&args[1].toObject()); 3433 if (!compartmentObject) { 3434 return false; 3435 } 3436 } 3437 3438 RootedObject stack(cx); 3439 { 3440 Maybe<AutoRealm> ar; 3441 if (compartmentObject) { 3442 ar.emplace(cx, compartmentObject); 3443 } 3444 if (!JS::CaptureCurrentStack(cx, &stack, std::move(capture))) { 3445 return false; 3446 } 3447 } 3448 3449 if (stack && !cx->compartment()->wrap(cx, &stack)) { 3450 return false; 3451 } 3452 3453 args.rval().setObjectOrNull(stack); 3454 return true; 3455 } 3456 3457 static bool CaptureFirstSubsumedFrame(JSContext* cx, unsigned argc, 3458 JS::Value* vp) { 3459 CallArgs args = CallArgsFromVp(argc, vp); 3460 if (!args.requireAtLeast(cx, "captureFirstSubsumedFrame", 1)) { 3461 return false; 3462 } 3463 3464 if (!args[0].isObject()) { 3465 JS_ReportErrorASCII(cx, "The argument must be an object"); 3466 return false; 3467 } 3468 3469 RootedObject obj(cx, &args[0].toObject()); 3470 obj = CheckedUnwrapStatic(obj); 3471 if (!obj) { 3472 JS_ReportErrorASCII(cx, "Denied permission to object."); 3473 return false; 3474 } 3475 3476 JS::StackCapture capture( 3477 JS::FirstSubsumedFrame(cx, obj->nonCCWRealm()->principals())); 3478 if (args.length() > 1) { 3479 capture.as<JS::FirstSubsumedFrame>().ignoreSelfHosted = 3480 JS::ToBoolean(args[1]); 3481 } 3482 3483 JS::RootedObject capturedStack(cx); 3484 if (!JS::CaptureCurrentStack(cx, &capturedStack, std::move(capture))) { 3485 return false; 3486 } 3487 3488 args.rval().setObjectOrNull(capturedStack); 3489 return true; 3490 } 3491 3492 static bool CallFunctionFromNativeFrame(JSContext* cx, unsigned argc, 3493 Value* vp) { 3494 CallArgs args = CallArgsFromVp(argc, vp); 3495 3496 if (args.length() != 1) { 3497 JS_ReportErrorASCII(cx, "The function takes exactly one argument."); 3498 return false; 3499 } 3500 if (!args[0].isObject() || !IsCallable(args[0])) { 3501 JS_ReportErrorASCII(cx, "The first argument should be a function."); 3502 return false; 3503 } 3504 3505 RootedObject function(cx, &args[0].toObject()); 3506 return Call(cx, UndefinedHandleValue, function, JS::HandleValueArray::empty(), 3507 args.rval()); 3508 } 3509 3510 static bool CallFunctionWithAsyncStack(JSContext* cx, unsigned argc, 3511 Value* vp) { 3512 CallArgs args = CallArgsFromVp(argc, vp); 3513 3514 if (args.length() != 3) { 3515 JS_ReportErrorASCII(cx, "The function takes exactly three arguments."); 3516 return false; 3517 } 3518 if (!args[0].isObject() || !IsCallable(args[0])) { 3519 JS_ReportErrorASCII(cx, "The first argument should be a function."); 3520 return false; 3521 } 3522 if (!args[1].isObject() || !args[1].toObject().is<SavedFrame>()) { 3523 JS_ReportErrorASCII(cx, "The second argument should be a SavedFrame."); 3524 return false; 3525 } 3526 if (!args[2].isString() || args[2].toString()->empty()) { 3527 JS_ReportErrorASCII(cx, "The third argument should be a non-empty string."); 3528 return false; 3529 } 3530 3531 RootedObject function(cx, &args[0].toObject()); 3532 RootedObject stack(cx, &args[1].toObject()); 3533 RootedString asyncCause(cx, args[2].toString()); 3534 UniqueChars utf8Cause = JS_EncodeStringToUTF8(cx, asyncCause); 3535 if (!utf8Cause) { 3536 MOZ_ASSERT(cx->isExceptionPending()); 3537 return false; 3538 } 3539 3540 JS::AutoSetAsyncStackForNewCalls sas( 3541 cx, stack, utf8Cause.get(), 3542 JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT); 3543 return Call(cx, UndefinedHandleValue, function, JS::HandleValueArray::empty(), 3544 args.rval()); 3545 } 3546 3547 static bool EnableTrackAllocations(JSContext* cx, unsigned argc, Value* vp) { 3548 SetAllocationMetadataBuilder(cx, &SavedStacks::metadataBuilder); 3549 return true; 3550 } 3551 3552 static bool DisableTrackAllocations(JSContext* cx, unsigned argc, Value* vp) { 3553 SetAllocationMetadataBuilder(cx, nullptr); 3554 return true; 3555 } 3556 3557 static bool SetTestFilenameValidationCallback(JSContext* cx, unsigned argc, 3558 Value* vp) { 3559 CallArgs args = CallArgsFromVp(argc, vp); 3560 3561 // Accept all filenames that start with "safe". In system code also accept 3562 // filenames starting with "system". 3563 auto testCb = [](JSContext* cx, const char* filename) -> bool { 3564 if (strstr(filename, "safe") == filename) { 3565 return true; 3566 } 3567 if (cx->realm()->isSystem() && strstr(filename, "system") == filename) { 3568 return true; 3569 } 3570 3571 return false; 3572 }; 3573 JS::SetFilenameValidationCallback(testCb); 3574 3575 args.rval().setUndefined(); 3576 return true; 3577 } 3578 3579 static JSAtom* GetPropertiesAddedName(JSContext* cx) { 3580 const char* propName = "_propertiesAdded"; 3581 return Atomize(cx, propName, strlen(propName)); 3582 } 3583 3584 static bool NewObjectWithAddPropertyHook(JSContext* cx, unsigned argc, 3585 Value* vp) { 3586 CallArgs args = CallArgsFromVp(argc, vp); 3587 3588 auto addPropHook = [](JSContext* cx, HandleObject obj, HandleId id, 3589 HandleValue v) -> bool { 3590 Rooted<JSAtom*> propName(cx, GetPropertiesAddedName(cx)); 3591 if (!propName) { 3592 return false; 3593 } 3594 // Don't do anything if we're adding the _propertiesAdded property. 3595 RootedId propId(cx, AtomToId(propName)); 3596 if (id == propId) { 3597 return true; 3598 } 3599 // Increment _propertiesAdded. 3600 RootedValue val(cx); 3601 if (!JS_GetPropertyById(cx, obj, propId, &val)) { 3602 return false; 3603 } 3604 if (!val.isInt32() || val.toInt32() == INT32_MAX) { 3605 return true; 3606 } 3607 val.setInt32(val.toInt32() + 1); 3608 return JS_DefinePropertyById(cx, obj, propId, val, 0); 3609 }; 3610 3611 static const JSClassOps classOps = { 3612 addPropHook, // addProperty 3613 nullptr, // delProperty 3614 nullptr, // enumerate 3615 nullptr, // newEnumerate 3616 nullptr, // resolve 3617 nullptr, // mayResolve 3618 nullptr, // finalize 3619 nullptr, // call 3620 nullptr, // construct 3621 nullptr, // trace 3622 }; 3623 static const JSClass cls = { 3624 "ObjectWithAddPropHook", 3625 0, 3626 &classOps, 3627 }; 3628 3629 RootedObject obj(cx, JS_NewObject(cx, &cls)); 3630 if (!obj) { 3631 return false; 3632 } 3633 3634 // Initialize _propertiesAdded to 0. 3635 Rooted<JSAtom*> propName(cx, GetPropertiesAddedName(cx)); 3636 if (!propName) { 3637 return false; 3638 } 3639 RootedId propId(cx, AtomToId(propName)); 3640 RootedValue val(cx, Int32Value(0)); 3641 if (!JS_DefinePropertyById(cx, obj, propId, val, 0)) { 3642 return false; 3643 } 3644 3645 args.rval().setObject(*obj); 3646 return true; 3647 } 3648 3649 static bool NewObjectWithCallHook(JSContext* cx, unsigned argc, Value* vp) { 3650 CallArgs args = CallArgsFromVp(argc, vp); 3651 3652 static auto hookShared = [](JSContext* cx, CallArgs& args) { 3653 Rooted<PlainObject*> obj(cx, NewPlainObject(cx)); 3654 if (!obj) { 3655 return false; 3656 } 3657 3658 // Define |this|. We can't expose the MagicValue to JS, so we use 3659 // "<is_constructing>" in that case. 3660 Rooted<Value> thisv(cx, args.thisv()); 3661 if (thisv.isMagic(JS_IS_CONSTRUCTING)) { 3662 JSString* str = NewStringCopyZ<CanGC>(cx, "<is_constructing>"); 3663 if (!str) { 3664 return false; 3665 } 3666 thisv.setString(str); 3667 } 3668 if (!DefineDataProperty(cx, obj, cx->names().this_, thisv, 3669 JSPROP_ENUMERATE)) { 3670 return false; 3671 } 3672 3673 // Define |callee|. 3674 if (!DefineDataProperty(cx, obj, cx->names().callee, args.calleev(), 3675 JSPROP_ENUMERATE)) { 3676 return false; 3677 } 3678 3679 // Define |arguments| array. 3680 Rooted<ArrayObject*> arr( 3681 cx, NewDenseCopiedArray(cx, args.length(), args.array())); 3682 if (!arr) { 3683 return false; 3684 } 3685 Rooted<Value> arrVal(cx, ObjectValue(*arr)); 3686 if (!DefineDataProperty(cx, obj, cx->names().arguments, arrVal, 3687 JSPROP_ENUMERATE)) { 3688 return false; 3689 } 3690 3691 // Define |newTarget| if constructing. 3692 if (args.isConstructing()) { 3693 const char* propName = "newTarget"; 3694 Rooted<JSAtom*> name(cx, Atomize(cx, propName, strlen(propName))); 3695 if (!name) { 3696 return false; 3697 } 3698 Rooted<PropertyKey> key(cx, NameToId(name->asPropertyName())); 3699 if (!DefineDataProperty(cx, obj, key, args.newTarget(), 3700 JSPROP_ENUMERATE)) { 3701 return false; 3702 } 3703 } 3704 3705 args.rval().setObject(*obj); 3706 return true; 3707 }; 3708 3709 static auto callHook = [](JSContext* cx, unsigned argc, Value* vp) { 3710 CallArgs args = CallArgsFromVp(argc, vp); 3711 MOZ_ASSERT(!args.isConstructing()); 3712 return hookShared(cx, args); 3713 }; 3714 static auto constructHook = [](JSContext* cx, unsigned argc, Value* vp) { 3715 CallArgs args = CallArgsFromVp(argc, vp); 3716 MOZ_ASSERT(args.isConstructing()); 3717 return hookShared(cx, args); 3718 }; 3719 3720 static const JSClassOps classOps = { 3721 nullptr, // addProperty 3722 nullptr, // delProperty 3723 nullptr, // enumerate 3724 nullptr, // newEnumerate 3725 nullptr, // resolve 3726 nullptr, // mayResolve 3727 nullptr, // finalize 3728 callHook, // call 3729 constructHook, // construct 3730 nullptr, // trace 3731 }; 3732 static const JSClass cls = { 3733 "ObjectWithCallHook", 3734 0, 3735 &classOps, 3736 }; 3737 3738 Rooted<JSObject*> obj(cx, JS_NewObject(cx, &cls)); 3739 if (!obj) { 3740 return false; 3741 } 3742 3743 args.rval().setObject(*obj); 3744 return true; 3745 } 3746 3747 static constexpr JSClass ObjectWithManyReservedSlotsClass = { 3748 "ObjectWithManyReservedSlots", 3749 JSCLASS_HAS_RESERVED_SLOTS(40), 3750 }; 3751 3752 static bool NewObjectWithManyReservedSlots(JSContext* cx, unsigned argc, 3753 Value* vp) { 3754 CallArgs args = CallArgsFromVp(argc, vp); 3755 3756 static constexpr size_t NumReservedSlots = 3757 JSCLASS_RESERVED_SLOTS(&ObjectWithManyReservedSlotsClass); 3758 static_assert(NumReservedSlots > NativeObject::MAX_FIXED_SLOTS); 3759 3760 RootedObject obj(cx, JS_NewObject(cx, &ObjectWithManyReservedSlotsClass)); 3761 if (!obj) { 3762 return false; 3763 } 3764 3765 for (size_t i = 0; i < NumReservedSlots; i++) { 3766 JS_SetReservedSlot(obj, i, Int32Value(i)); 3767 } 3768 3769 args.rval().setObject(*obj); 3770 return true; 3771 } 3772 3773 static bool CheckObjectWithManyReservedSlots(JSContext* cx, unsigned argc, 3774 Value* vp) { 3775 CallArgs args = CallArgsFromVp(argc, vp); 3776 3777 if (args.length() != 1 || !args[0].isObject() || 3778 args[0].toObject().getClass() != &ObjectWithManyReservedSlotsClass) { 3779 JS_ReportErrorASCII(cx, 3780 "Expected object from newObjectWithManyReservedSlots"); 3781 return false; 3782 } 3783 3784 JSObject* obj = &args[0].toObject(); 3785 3786 static constexpr size_t NumReservedSlots = 3787 JSCLASS_RESERVED_SLOTS(&ObjectWithManyReservedSlotsClass); 3788 3789 for (size_t i = 0; i < NumReservedSlots; i++) { 3790 MOZ_RELEASE_ASSERT(JS::GetReservedSlot(obj, i).toInt32() == int32_t(i)); 3791 } 3792 3793 args.rval().setUndefined(); 3794 return true; 3795 } 3796 3797 static bool AddObjectFuse(JSContext* cx, unsigned argc, Value* vp) { 3798 CallArgs args = CallArgsFromVp(argc, vp); 3799 3800 if (args.length() != 1 || !args[0].isObject() || 3801 !args[0].toObject().is<NativeObject>()) { 3802 JS_ReportErrorASCII(cx, "Expected native object argument"); 3803 return false; 3804 } 3805 3806 Rooted<NativeObject*> obj(cx, &args[0].toObject().as<NativeObject>()); 3807 if (!NativeObject::setHasObjectFuse(cx, obj)) { 3808 return false; 3809 } 3810 3811 // The ObjectFuse is normally created lazily the first time it's needed for an 3812 // IC stub, but here we create it eagerly to simplify tests. 3813 if (!cx->zone()->objectFuses.getOrCreate(cx, obj)) { 3814 return false; 3815 } 3816 3817 args.rval().setUndefined(); 3818 return true; 3819 } 3820 3821 static bool GetObjectFuseState(JSContext* cx, unsigned argc, Value* vp) { 3822 CallArgs args = CallArgsFromVp(argc, vp); 3823 3824 if (args.length() != 1 || !args[0].isObject() || 3825 !args[0].toObject().hasObjectFuse()) { 3826 JS_ReportErrorASCII(cx, "Expected object with fuse"); 3827 return false; 3828 } 3829 3830 Rooted<NativeObject*> obj(cx, &args[0].toObject().as<NativeObject>()); 3831 ObjectFuse* objFuse = cx->zone()->objectFuses.getOrCreate(cx, obj); 3832 if (!objFuse) { 3833 return false; 3834 } 3835 3836 Rooted<PlainObject*> result(cx, NewPlainObject(cx)); 3837 if (!result) { 3838 return false; 3839 } 3840 3841 Rooted<Value> val(cx, NumberValue(objFuse->generationMaybeInvalid())); 3842 if (!JS_DefineProperty(cx, result, "generation", val, JSPROP_ENUMERATE)) { 3843 return false; 3844 } 3845 3846 Rooted<PlainObject*> props(cx, NewPlainObjectWithProto(cx, nullptr)); 3847 if (!props) { 3848 return false; 3849 } 3850 3851 // Add all properties to a Vector so that we can iterate over them in property 3852 // definition order. 3853 Rooted<PropertyInfoWithKeyVector> propsVec(cx, PropertyInfoWithKeyVector(cx)); 3854 for (ShapePropertyIter<CanGC> iter(cx, obj->shape()); !iter.done(); iter++) { 3855 if (iter->hasSlot() && !propsVec.append(*iter)) { 3856 return false; 3857 } 3858 } 3859 3860 // For each property, define a property on |props| with the value a string 3861 // describing the property's current state (eg "Constant"). 3862 for (size_t i = propsVec.length(); i > 0; i--) { 3863 PropertyInfoWithKey prop = propsVec[i - 1]; 3864 Rooted<PropertyKey> key(cx, prop.key()); 3865 JSString* s = 3866 NewStringCopyZ<CanGC>(cx, objFuse->getPropertyStateString(prop)); 3867 if (!s) { 3868 return false; 3869 }; 3870 val.setString(s); 3871 if (!JS_DefinePropertyById(cx, props, key, val, JSPROP_ENUMERATE)) { 3872 return false; 3873 } 3874 } 3875 if (!JS_DefineProperty(cx, result, "properties", props, JSPROP_ENUMERATE)) { 3876 return false; 3877 } 3878 3879 args.rval().setObject(*result); 3880 return true; 3881 } 3882 3883 static bool GetWatchtowerLog(JSContext* cx, unsigned argc, Value* vp) { 3884 CallArgs args = CallArgsFromVp(argc, vp); 3885 3886 Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx)); 3887 3888 if (auto* log = cx->runtime()->watchtowerTestingLog.ref().get()) { 3889 Rooted<JSObject*> elem(cx); 3890 for (PlainObject* obj : *log) { 3891 elem = obj; 3892 if (!cx->compartment()->wrap(cx, &elem)) { 3893 return false; 3894 } 3895 if (!values.append(ObjectValue(*elem))) { 3896 return false; 3897 } 3898 } 3899 log->clearAndFree(); 3900 } 3901 3902 ArrayObject* arr = NewDenseCopiedArray(cx, values.length(), values.begin()); 3903 if (!arr) { 3904 return false; 3905 } 3906 3907 args.rval().setObject(*arr); 3908 return true; 3909 } 3910 3911 static bool AddWatchtowerTarget(JSContext* cx, unsigned argc, Value* vp) { 3912 CallArgs args = CallArgsFromVp(argc, vp); 3913 3914 if (args.length() != 1 || !args[0].isObject()) { 3915 JS_ReportErrorASCII(cx, "Expected a single object argument."); 3916 return false; 3917 } 3918 3919 if (!cx->runtime()->watchtowerTestingLog.ref()) { 3920 auto vec = cx->make_unique<JSRuntime::RootedPlainObjVec>(cx); 3921 if (!vec) { 3922 return false; 3923 } 3924 cx->runtime()->watchtowerTestingLog = std::move(vec); 3925 } 3926 3927 RootedObject obj(cx, &args[0].toObject()); 3928 if (!JSObject::setUseWatchtowerTestingLog(cx, obj)) { 3929 return false; 3930 } 3931 3932 args.rval().setUndefined(); 3933 return true; 3934 } 3935 3936 struct TestExternalString : public JSExternalStringCallbacks { 3937 void finalize(JS::Latin1Char* chars) const override { js_free(chars); } 3938 void finalize(char16_t* chars) const override { js_free(chars); } 3939 size_t sizeOfBuffer(const JS::Latin1Char* chars, 3940 mozilla::MallocSizeOf mallocSizeOf) const override { 3941 return mallocSizeOf(chars); 3942 } 3943 size_t sizeOfBuffer(const char16_t* chars, 3944 mozilla::MallocSizeOf mallocSizeOf) const override { 3945 return mallocSizeOf(chars); 3946 } 3947 }; 3948 3949 static constexpr TestExternalString TestExternalStringCallbacks; 3950 3951 static bool NewString(JSContext* cx, unsigned argc, Value* vp) { 3952 CallArgs args = CallArgsFromVp(argc, vp); 3953 3954 RootedString src(cx, ToString(cx, args.get(0))); 3955 if (!src) { 3956 return false; 3957 } 3958 3959 gc::Heap heap = gc::Heap::Default; 3960 bool wantTwoByte = false; 3961 bool forceExternal = false; 3962 bool maybeExternal = false; 3963 bool newStringBuffer = false; 3964 bool shareStringBuffer = false; 3965 uint32_t capacity = 0; 3966 3967 if (args.get(1).isObject()) { 3968 RootedObject options(cx, &args[1].toObject()); 3969 RootedValue v(cx); 3970 bool requestTenured = false; 3971 struct BoolSetting { 3972 const char* name; 3973 bool* value; 3974 }; 3975 for (auto [name, setting] : 3976 {BoolSetting{"tenured", &requestTenured}, 3977 BoolSetting{"twoByte", &wantTwoByte}, 3978 BoolSetting{"external", &forceExternal}, 3979 BoolSetting{"maybeExternal", &maybeExternal}, 3980 BoolSetting{"newStringBuffer", &newStringBuffer}, 3981 BoolSetting{"shareStringBuffer", &shareStringBuffer}}) { 3982 if (!JS_GetProperty(cx, options, name, &v)) { 3983 return false; 3984 } 3985 *setting = ToBoolean(v); // false if not given (or otherwise undefined) 3986 } 3987 struct Uint32Setting { 3988 const char* name; 3989 uint32_t* value; 3990 }; 3991 for (auto [name, setting] : {Uint32Setting{"capacity", &capacity}}) { 3992 if (!JS_GetProperty(cx, options, name, &v)) { 3993 return false; 3994 } 3995 int32_t i32; 3996 if (!ToInt32(cx, v, &i32)) { 3997 return false; 3998 } 3999 if (i32 < 0) { 4000 JS_ReportErrorASCII(cx, "nonnegative value required"); 4001 return false; 4002 } 4003 *setting = static_cast<uint32_t>(i32); 4004 } 4005 4006 heap = requestTenured ? gc::Heap::Tenured : gc::Heap::Default; 4007 if (forceExternal || maybeExternal) { 4008 wantTwoByte = true; 4009 } 4010 unsigned kinds = forceExternal + maybeExternal + (capacity != 0) + 4011 newStringBuffer + shareStringBuffer; 4012 if (kinds > 1) { 4013 JS_ReportErrorASCII(cx, 4014 "external, capacity, and stringBuffer options can " 4015 "not be used at the same time"); 4016 return false; 4017 } 4018 } 4019 4020 auto len = src->length(); 4021 RootedString dest(cx); 4022 4023 if (forceExternal || maybeExternal) { 4024 auto buf = cx->make_pod_array<char16_t>(len); 4025 if (!buf) { 4026 return false; 4027 } 4028 4029 if (!JS_CopyStringChars(cx, mozilla::Range<char16_t>(buf.get(), len), 4030 src)) { 4031 return false; 4032 } 4033 4034 bool isExternal = true; 4035 if (forceExternal) { 4036 dest = JSExternalString::new_(cx, buf.get(), len, 4037 &TestExternalStringCallbacks); 4038 } else { 4039 dest = NewMaybeExternalString( 4040 cx, buf.get(), len, &TestExternalStringCallbacks, &isExternal, heap); 4041 } 4042 if (dest && isExternal) { 4043 (void)buf.release(); // Ownership was transferred. 4044 } 4045 } else if (shareStringBuffer) { 4046 if (!src->isLinear() || !src->asLinear().hasStringBuffer()) { 4047 JS_ReportErrorASCII(cx, "source string must have a string buffer"); 4048 return false; 4049 } 4050 RefPtr<mozilla::StringBuffer> buffer = src->asLinear().stringBuffer(); 4051 if (src->hasLatin1Chars()) { 4052 Rooted<JSString::OwnedChars<Latin1Char>> owned(cx, std::move(buffer), 4053 len); 4054 dest = 4055 JSLinearString::newValidLength<CanGC, Latin1Char>(cx, &owned, heap); 4056 } else { 4057 Rooted<JSString::OwnedChars<char16_t>> owned(cx, std::move(buffer), len); 4058 dest = JSLinearString::newValidLength<CanGC, char16_t>(cx, &owned, heap); 4059 } 4060 } else { 4061 AutoStableStringChars stable(cx); 4062 if (!wantTwoByte && src->hasLatin1Chars()) { 4063 if (!stable.init(cx, src)) { 4064 return false; 4065 } 4066 } else { 4067 if (!stable.initTwoByte(cx, src)) { 4068 return false; 4069 } 4070 } 4071 if (newStringBuffer) { 4072 auto allocString = [&](const auto* chars) -> JSLinearString* { 4073 using CharT = 4074 std::remove_const_t<std::remove_pointer_t<decltype(chars)>>; 4075 4076 if (JSInlineString::lengthFits<CharT>(len)) { 4077 JS_ReportErrorASCII(cx, "Cannot create small non-inline strings"); 4078 return nullptr; 4079 } 4080 4081 RefPtr<mozilla::StringBuffer> buffer = 4082 mozilla::StringBuffer::Create(chars, len); 4083 if (!buffer) { 4084 ReportOutOfMemory(cx); 4085 return nullptr; 4086 } 4087 4088 Rooted<JSString::OwnedChars<CharT>> owned(cx, std::move(buffer), len); 4089 return JSLinearString::newValidLength<CanGC, CharT>(cx, &owned, heap); 4090 }; 4091 if (stable.isLatin1()) { 4092 dest = allocString(stable.latin1Chars()); 4093 } else { 4094 dest = allocString(stable.twoByteChars()); 4095 } 4096 } else if (capacity) { 4097 if (capacity < len) { 4098 capacity = len; 4099 } 4100 if (len == 0) { 4101 JS_ReportErrorASCII(cx, "Cannot set capacity of empty string"); 4102 return false; 4103 } 4104 4105 auto createLinearString = [&](const auto* chars) -> JSLinearString* { 4106 using CharT = 4107 std::remove_const_t<std::remove_pointer_t<decltype(chars)>>; 4108 4109 if (JSInlineString::lengthFits<CharT>(len)) { 4110 JS_ReportErrorASCII(cx, "Cannot create small non-inline strings"); 4111 return nullptr; 4112 } 4113 4114 auto news = 4115 cx->make_pod_arena_array<CharT>(js::StringBufferArena, capacity); 4116 if (!news) { 4117 return nullptr; 4118 } 4119 mozilla::PodCopy(news.get(), chars, len); 4120 Rooted<JSString::OwnedChars<CharT>> owned(cx, std::move(news), len); 4121 return JSLinearString::newValidLength<CanGC, CharT>(cx, &owned, heap); 4122 }; 4123 4124 if (stable.isLatin1()) { 4125 dest = createLinearString(stable.latin1Chars()); 4126 } else { 4127 dest = createLinearString(stable.twoByteChars()); 4128 } 4129 if (dest) { 4130 dest->asLinear().makeExtensible(capacity); 4131 } 4132 } else if (wantTwoByte) { 4133 dest = NewStringCopyNDontDeflate<CanGC>(cx, stable.twoByteChars(), len, 4134 heap); 4135 } else if (stable.isLatin1()) { 4136 dest = NewStringCopyN<CanGC>(cx, stable.latin1Chars(), len, heap); 4137 } else { 4138 // Normal behavior: auto-deflate to latin1 if possible. 4139 dest = NewStringCopyN<CanGC>(cx, stable.twoByteChars(), len, heap); 4140 } 4141 } 4142 4143 if (!dest) { 4144 return false; 4145 } 4146 4147 args.rval().setString(dest); 4148 return true; 4149 } 4150 4151 static bool NewDependentString(JSContext* cx, unsigned argc, Value* vp) { 4152 CallArgs args = CallArgsFromVp(argc, vp); 4153 4154 RootedString src(cx, ToString(cx, args.get(0))); 4155 if (!src) { 4156 return false; 4157 } 4158 4159 uint64_t indexStart = 0; 4160 mozilla::Maybe<uint64_t> indexEnd; 4161 gc::Heap heap = gc::Heap::Default; 4162 mozilla::Maybe<gc::Heap> requiredHeap; 4163 bool suppressContraction = false; 4164 4165 if (!ToIndex(cx, args.get(1), &indexStart)) { 4166 return false; 4167 } 4168 4169 Rooted<Value> options(cx); 4170 if (args.get(2).isObject()) { 4171 options = args[2]; 4172 } else { 4173 uint64_t idx; 4174 if (args.hasDefined(2)) { 4175 if (!ToIndex(cx, args.get(2), &idx)) { 4176 return false; 4177 } 4178 indexEnd.emplace(idx); 4179 } 4180 options = args.get(3); 4181 } 4182 4183 if (options.isObject()) { 4184 Rooted<Value> v(cx); 4185 Rooted<JSObject*> optObj(cx, &options.toObject()); 4186 if (!JS_GetProperty(cx, optObj, "tenured", &v)) { 4187 return false; 4188 } 4189 if (v.isBoolean()) { 4190 requiredHeap.emplace(v.toBoolean() ? gc::Heap::Tenured 4191 : gc::Heap::Default); 4192 heap = *requiredHeap; 4193 } 4194 if (!JS_GetProperty(cx, optObj, "suppress-contraction", &v)) { 4195 return false; 4196 } 4197 suppressContraction = ToBoolean(v); 4198 } 4199 4200 if (indexEnd.isNothing()) { 4201 // Read the length now that no more JS code can run. 4202 indexEnd.emplace(src->length()); 4203 } 4204 if (indexStart > src->length() || *indexEnd > src->length() || 4205 indexStart >= *indexEnd) { 4206 JS_ReportErrorASCII(cx, "invalid dependent string bounds"); 4207 return false; 4208 } 4209 if (!src->ensureLinear(cx)) { 4210 return false; 4211 } 4212 Rooted<JSString*> result( 4213 cx, js::NewDependentStringForTesting( 4214 cx, src, indexStart, *indexEnd - indexStart, 4215 suppressContraction ? JS::ContractBaseChain::AllowLong 4216 : JS::ContractBaseChain::Contract, 4217 heap)); 4218 if (!result) { 4219 return false; 4220 } 4221 if (!result->isDependent()) { 4222 JS_ReportErrorASCII(cx, "resulting string is not dependent (too short?)"); 4223 return false; 4224 } 4225 4226 if (requiredHeap.isSome()) { 4227 if ((*requiredHeap == gc::Heap::Tenured) != result->isTenured()) { 4228 if (result->isTenured()) { 4229 JS_ReportErrorASCII(cx, "nursery string created in tenured heap"); 4230 return false; 4231 } else { 4232 JS_ReportErrorASCII(cx, "tenured string created in nursery heap"); 4233 return false; 4234 } 4235 } 4236 } 4237 4238 args.rval().setString(result); 4239 return true; 4240 } 4241 4242 // Warning! This will let you create ropes that I'm not sure would be possible 4243 // otherwise, specifically: 4244 // 4245 // - a rope with a zero-length child 4246 // - a rope that would fit into an inline string 4247 // 4248 static bool NewRope(JSContext* cx, unsigned argc, Value* vp) { 4249 CallArgs args = CallArgsFromVp(argc, vp); 4250 4251 if (!args.get(0).isString() || !args.get(1).isString()) { 4252 JS_ReportErrorASCII(cx, "newRope requires two string arguments."); 4253 return false; 4254 } 4255 4256 gc::Heap heap = js::gc::Heap::Default; 4257 if (args.get(2).isObject()) { 4258 RootedObject options(cx, &args[2].toObject()); 4259 RootedValue v(cx); 4260 if (!JS_GetProperty(cx, options, "nursery", &v)) { 4261 return false; 4262 } 4263 if (!v.isUndefined() && !ToBoolean(v)) { 4264 heap = js::gc::Heap::Tenured; 4265 } 4266 } 4267 4268 RootedString left(cx, args[0].toString()); 4269 RootedString right(cx, args[1].toString()); 4270 size_t length = JS_GetStringLength(left) + JS_GetStringLength(right); 4271 if (length > JSString::MAX_LENGTH) { 4272 JS_ReportErrorASCII(cx, "rope length exceeds maximum string length"); 4273 return false; 4274 } 4275 4276 // Disallow creating ropes where one side is empty. 4277 if (left->empty() || right->empty()) { 4278 JS_ReportErrorASCII(cx, "rope child mustn't be the empty string"); 4279 return false; 4280 } 4281 4282 // Disallow creating ropes which fit into inline strings. 4283 if (left->hasLatin1Chars() && right->hasLatin1Chars()) { 4284 if (JSInlineString::lengthFits<JS::Latin1Char>(length)) { 4285 JS_ReportErrorASCII(cx, "Cannot create small non-inline ropes"); 4286 return false; 4287 } 4288 } else { 4289 if (JSInlineString::lengthFits<char16_t>(length)) { 4290 JS_ReportErrorASCII(cx, "Cannot create small non-inline ropes"); 4291 return false; 4292 } 4293 } 4294 4295 auto* str = JSRope::new_<CanGC>(cx, left, right, length, heap); 4296 if (!str) { 4297 return false; 4298 } 4299 4300 args.rval().setString(str); 4301 return true; 4302 } 4303 4304 static bool IsRope(JSContext* cx, unsigned argc, Value* vp) { 4305 CallArgs args = CallArgsFromVp(argc, vp); 4306 4307 if (!args.get(0).isString()) { 4308 JS_ReportErrorASCII(cx, "isRope requires a string argument."); 4309 return false; 4310 } 4311 4312 JSString* str = args[0].toString(); 4313 args.rval().setBoolean(str->isRope()); 4314 return true; 4315 } 4316 4317 static bool EnsureLinearString(JSContext* cx, unsigned argc, Value* vp) { 4318 CallArgs args = CallArgsFromVp(argc, vp); 4319 4320 if (args.length() != 1 || !args[0].isString()) { 4321 JS_ReportErrorASCII( 4322 cx, "ensureLinearString takes exactly one string argument."); 4323 return false; 4324 } 4325 4326 JSLinearString* linear = args[0].toString()->ensureLinear(cx); 4327 if (!linear) { 4328 return false; 4329 } 4330 4331 args.rval().setString(linear); 4332 return true; 4333 } 4334 4335 static bool RepresentativeStringArray(JSContext* cx, unsigned argc, Value* vp) { 4336 CallArgs args = CallArgsFromVp(argc, vp); 4337 4338 RootedObject array(cx, JS::NewArrayObject(cx, 0)); 4339 if (!array) { 4340 return false; 4341 } 4342 4343 if (!JSString::fillWithRepresentatives(cx, array.as<ArrayObject>())) { 4344 return false; 4345 } 4346 4347 args.rval().setObject(*array); 4348 return true; 4349 } 4350 4351 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT) 4352 4353 static bool OOMThreadTypes(JSContext* cx, unsigned argc, Value* vp) { 4354 CallArgs args = CallArgsFromVp(argc, vp); 4355 args.rval().setInt32(js::THREAD_TYPE_MAX); 4356 return true; 4357 } 4358 4359 static bool CheckCanSimulateOOM(JSContext* cx) { 4360 if (js::oom::GetThreadType() != js::THREAD_TYPE_MAIN) { 4361 JS_ReportErrorASCII( 4362 cx, "Simulated OOM failure is only supported on the main thread"); 4363 return false; 4364 } 4365 4366 return true; 4367 } 4368 4369 static bool SetupOOMFailure(JSContext* cx, bool failAlways, unsigned argc, 4370 Value* vp) { 4371 CallArgs args = CallArgsFromVp(argc, vp); 4372 4373 if (disableOOMFunctions) { 4374 args.rval().setUndefined(); 4375 return true; 4376 } 4377 4378 if (args.length() < 1) { 4379 JS_ReportErrorASCII(cx, "Count argument required"); 4380 return false; 4381 } 4382 4383 if (args.length() > 2) { 4384 JS_ReportErrorASCII(cx, "Too many arguments"); 4385 return false; 4386 } 4387 4388 int32_t count; 4389 if (!JS::ToInt32(cx, args.get(0), &count)) { 4390 return false; 4391 } 4392 4393 if (count <= 0) { 4394 JS_ReportErrorASCII(cx, "OOM cutoff should be positive"); 4395 return false; 4396 } 4397 4398 uint32_t targetThread = js::THREAD_TYPE_MAIN; 4399 if (args.length() > 1 && !ToUint32(cx, args[1], &targetThread)) { 4400 return false; 4401 } 4402 4403 if (targetThread == js::THREAD_TYPE_NONE || 4404 targetThread == js::THREAD_TYPE_WORKER || 4405 targetThread >= js::THREAD_TYPE_MAX) { 4406 JS_ReportErrorASCII(cx, "Invalid thread type specified"); 4407 return false; 4408 } 4409 4410 if (!CheckCanSimulateOOM(cx)) { 4411 return false; 4412 } 4413 4414 js::oom::simulator.simulateFailureAfter(js::oom::FailureSimulator::Kind::OOM, 4415 count, targetThread, failAlways); 4416 args.rval().setUndefined(); 4417 return true; 4418 } 4419 4420 static bool OOMAfterAllocations(JSContext* cx, unsigned argc, Value* vp) { 4421 return SetupOOMFailure(cx, true, argc, vp); 4422 } 4423 4424 static bool OOMAtAllocation(JSContext* cx, unsigned argc, Value* vp) { 4425 return SetupOOMFailure(cx, false, argc, vp); 4426 } 4427 4428 static bool ResetOOMFailure(JSContext* cx, unsigned argc, Value* vp) { 4429 CallArgs args = CallArgsFromVp(argc, vp); 4430 4431 if (!CheckCanSimulateOOM(cx)) { 4432 return false; 4433 } 4434 4435 args.rval().setBoolean(js::oom::HadSimulatedOOM()); 4436 js::oom::simulator.reset(); 4437 return true; 4438 } 4439 4440 static size_t CountCompartments(JSContext* cx) { 4441 size_t count = 0; 4442 for (auto zone : cx->runtime()->gc.zones()) { 4443 count += zone->compartments().length(); 4444 } 4445 return count; 4446 } 4447 4448 // Iterative failure testing: test a function by simulating failures at indexed 4449 // locations throughout the normal execution path and checking that the 4450 // resulting state of the environment is consistent with the error result. 4451 // 4452 // For example, trigger OOM at every allocation point and test that the function 4453 // either recovers and succeeds or raises an exception and fails. 4454 4455 class MOZ_STACK_CLASS IterativeFailureTest { 4456 public: 4457 struct FailureSimulator { 4458 virtual void setup(JSContext* cx) {} 4459 virtual void teardown(JSContext* cx) {} 4460 virtual void startSimulating(JSContext* cx, unsigned iteration, 4461 unsigned thread, bool keepFailing) = 0; 4462 virtual bool stopSimulating() = 0; 4463 virtual void cleanup(JSContext* cx) {} 4464 }; 4465 4466 IterativeFailureTest(JSContext* cx, FailureSimulator& simulator); 4467 bool initParams(const CallArgs& args); 4468 bool test(); 4469 4470 private: 4471 bool setup(); 4472 bool testThread(unsigned thread); 4473 bool testIteration(unsigned thread, unsigned iteration, 4474 bool& failureWasSimulated, MutableHandleValue exception); 4475 void cleanup(); 4476 void teardown(); 4477 4478 JSContext* const cx; 4479 FailureSimulator& simulator; 4480 size_t compartmentCount; 4481 4482 // Test parameters set by initParams. 4483 RootedFunction testFunction; 4484 unsigned threadStart = 0; 4485 unsigned threadEnd = 0; 4486 bool expectExceptionOnFailure = true; 4487 bool keepFailing = false; 4488 bool verbose = false; 4489 }; 4490 4491 bool RunIterativeFailureTest( 4492 JSContext* cx, const CallArgs& args, 4493 IterativeFailureTest::FailureSimulator& simulator) { 4494 IterativeFailureTest test(cx, simulator); 4495 return test.initParams(args) && test.test(); 4496 } 4497 4498 IterativeFailureTest::IterativeFailureTest(JSContext* cx, 4499 FailureSimulator& simulator) 4500 : cx(cx), simulator(simulator), testFunction(cx) {} 4501 4502 bool IterativeFailureTest::test() { 4503 if (disableOOMFunctions) { 4504 return true; 4505 } 4506 4507 if (!setup()) { 4508 return false; 4509 } 4510 4511 auto onExit = mozilla::MakeScopeExit([this] { teardown(); }); 4512 4513 for (unsigned thread = threadStart; thread <= threadEnd; thread++) { 4514 if (!testThread(thread)) { 4515 return false; 4516 } 4517 } 4518 4519 return true; 4520 } 4521 4522 bool IterativeFailureTest::setup() { 4523 if (!CheckCanSimulateOOM(cx)) { 4524 return false; 4525 } 4526 4527 // Disallow nested tests. 4528 if (cx->runningOOMTest) { 4529 JS_ReportErrorASCII( 4530 cx, "Nested call to iterative failure test is not allowed."); 4531 return false; 4532 } 4533 cx->runningOOMTest = true; 4534 4535 MOZ_ASSERT(!cx->isExceptionPending()); 4536 4537 # ifdef JS_GC_ZEAL 4538 JS::SetGCZeal(cx, 0, JS::ShellDefaultGCZealFrequency); 4539 # endif 4540 4541 // Delazify the function here if necessary so we don't end up testing that. 4542 if (testFunction->isInterpreted() && 4543 !JSFunction::getOrCreateScript(cx, testFunction)) { 4544 return false; 4545 } 4546 4547 compartmentCount = CountCompartments(cx); 4548 4549 simulator.setup(cx); 4550 4551 return true; 4552 } 4553 4554 bool IterativeFailureTest::testThread(unsigned thread) { 4555 if (verbose) { 4556 fprintf(stderr, "thread %u\n", thread); 4557 } 4558 4559 RootedValue exception(cx); 4560 4561 unsigned iteration = 1; 4562 bool failureWasSimulated; 4563 do { 4564 if (!testIteration(thread, iteration, failureWasSimulated, &exception)) { 4565 return false; 4566 } 4567 4568 iteration++; 4569 } while (failureWasSimulated); 4570 4571 if (verbose) { 4572 fprintf(stderr, " finished after %u iterations\n", iteration - 1); 4573 if (!exception.isUndefined()) { 4574 RootedString str(cx, JS::ToString(cx, exception)); 4575 if (!str) { 4576 fprintf(stderr, " error while trying to print exception, giving up\n"); 4577 return false; 4578 } 4579 UniqueChars bytes(JS_EncodeStringToUTF8(cx, str)); 4580 if (!bytes) { 4581 return false; 4582 } 4583 fprintf(stderr, " threw %s\n", bytes.get()); 4584 } 4585 } 4586 4587 return true; 4588 } 4589 4590 bool IterativeFailureTest::testIteration(unsigned thread, unsigned iteration, 4591 bool& failureWasSimulated, 4592 MutableHandleValue exception) { 4593 if (verbose) { 4594 fprintf(stderr, " iteration %u\n", iteration); 4595 } 4596 4597 MOZ_RELEASE_ASSERT(!cx->isExceptionPending()); 4598 4599 simulator.startSimulating(cx, iteration, thread, keepFailing); 4600 4601 RootedValue result(cx); 4602 bool ok = JS_CallFunction(cx, cx->global(), testFunction, 4603 HandleValueArray::empty(), &result); 4604 4605 failureWasSimulated = simulator.stopSimulating(); 4606 4607 if (ok && cx->isExceptionPending()) { 4608 MOZ_CRASH( 4609 "Thunk execution succeeded but an exception was raised - missing error " 4610 "check?"); 4611 } 4612 4613 if (!ok && !cx->isExceptionPending() && expectExceptionOnFailure) { 4614 MOZ_CRASH( 4615 "Thunk execution failed but no exception was raised - missing call to " 4616 "js::ReportOutOfMemory()?"); 4617 } 4618 4619 // Note that it is possible that the function throws an exception unconnected 4620 // to the simulated failure, in which case we ignore it. More correct would be 4621 // to have the caller pass some kind of exception specification and to check 4622 // the exception against it. 4623 if (!failureWasSimulated && cx->isExceptionPending()) { 4624 if (!cx->getPendingException(exception)) { 4625 return false; 4626 } 4627 } 4628 cx->clearPendingException(); 4629 4630 cleanup(); 4631 4632 return true; 4633 } 4634 4635 void IterativeFailureTest::cleanup() { 4636 simulator.cleanup(cx); 4637 4638 gc::FinishGC(cx); 4639 4640 // Some tests create a new compartment or zone on every iteration. Our GC is 4641 // triggered by GC allocations and not by number of compartments or zones, so 4642 // these won't normally get cleaned up. The check here stops some tests 4643 // running out of memory. ("Gentlemen, you can't fight in here! This is the 4644 // War oom!") 4645 if (CountCompartments(cx) > compartmentCount + 100) { 4646 JS_GC(cx); 4647 compartmentCount = CountCompartments(cx); 4648 } 4649 } 4650 4651 void IterativeFailureTest::teardown() { 4652 simulator.teardown(cx); 4653 4654 cx->runningOOMTest = false; 4655 } 4656 4657 bool IterativeFailureTest::initParams(const CallArgs& args) { 4658 if (args.length() < 1 || args.length() > 2) { 4659 JS_ReportErrorASCII(cx, "function takes between 1 and 2 arguments."); 4660 return false; 4661 } 4662 4663 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) { 4664 JS_ReportErrorASCII(cx, "The first argument must be the function to test."); 4665 return false; 4666 } 4667 testFunction = &args[0].toObject().as<JSFunction>(); 4668 4669 if (args.length() == 2) { 4670 if (args[1].isBoolean()) { 4671 expectExceptionOnFailure = args[1].toBoolean(); 4672 } else if (args[1].isObject()) { 4673 RootedObject options(cx, &args[1].toObject()); 4674 RootedValue value(cx); 4675 4676 if (!JS_GetProperty(cx, options, "expectExceptionOnFailure", &value)) { 4677 return false; 4678 } 4679 if (!value.isUndefined()) { 4680 expectExceptionOnFailure = ToBoolean(value); 4681 } 4682 4683 if (!JS_GetProperty(cx, options, "keepFailing", &value)) { 4684 return false; 4685 } 4686 if (!value.isUndefined()) { 4687 keepFailing = ToBoolean(value); 4688 } 4689 } else { 4690 JS_ReportErrorASCII( 4691 cx, "The optional second argument must be an object or a boolean."); 4692 return false; 4693 } 4694 } 4695 4696 // There are some places where we do fail without raising an exception, so 4697 // we can't expose this to the fuzzers by default. 4698 if (fuzzingSafe) { 4699 expectExceptionOnFailure = false; 4700 } 4701 4702 // Test all threads by default except worker threads. 4703 threadStart = oom::FirstThreadTypeToTest; 4704 threadEnd = oom::LastThreadTypeToTest; 4705 4706 // Test a single thread type if specified by the OOM_THREAD environment 4707 // variable. 4708 int threadOption = 0; 4709 if (EnvVarAsInt("OOM_THREAD", &threadOption)) { 4710 if (threadOption < oom::FirstThreadTypeToTest || 4711 threadOption > oom::LastThreadTypeToTest) { 4712 JS_ReportErrorASCII(cx, "OOM_THREAD value out of range."); 4713 return false; 4714 } 4715 4716 threadStart = threadOption; 4717 threadEnd = threadOption; 4718 } 4719 4720 verbose = EnvVarIsDefined("OOM_VERBOSE"); 4721 4722 return true; 4723 } 4724 4725 struct OOMSimulator : public IterativeFailureTest::FailureSimulator { 4726 void setup(JSContext* cx) override { cx->runtime()->hadOutOfMemory = false; } 4727 4728 void startSimulating(JSContext* cx, unsigned i, unsigned thread, 4729 bool keepFailing) override { 4730 MOZ_ASSERT(!cx->runtime()->hadOutOfMemory); 4731 js::oom::simulator.simulateFailureAfter( 4732 js::oom::FailureSimulator::Kind::OOM, i, thread, keepFailing); 4733 } 4734 4735 bool stopSimulating() override { 4736 bool handledOOM = js::oom::HadSimulatedOOM(); 4737 js::oom::simulator.reset(); 4738 return handledOOM; 4739 } 4740 4741 void cleanup(JSContext* cx) override { 4742 cx->runtime()->hadOutOfMemory = false; 4743 } 4744 }; 4745 4746 static bool OOMTest(JSContext* cx, unsigned argc, Value* vp) { 4747 CallArgs args = CallArgsFromVp(argc, vp); 4748 4749 OOMSimulator simulator; 4750 if (!RunIterativeFailureTest(cx, args, simulator)) { 4751 return false; 4752 } 4753 4754 args.rval().setUndefined(); 4755 return true; 4756 } 4757 4758 struct StackOOMSimulator : public IterativeFailureTest::FailureSimulator { 4759 void startSimulating(JSContext* cx, unsigned i, unsigned thread, 4760 bool keepFailing) override { 4761 js::oom::simulator.simulateFailureAfter( 4762 js::oom::FailureSimulator::Kind::StackOOM, i, thread, keepFailing); 4763 } 4764 4765 bool stopSimulating() override { 4766 bool handledOOM = js::oom::HadSimulatedStackOOM(); 4767 js::oom::simulator.reset(); 4768 return handledOOM; 4769 } 4770 }; 4771 4772 static bool StackTest(JSContext* cx, unsigned argc, Value* vp) { 4773 CallArgs args = CallArgsFromVp(argc, vp); 4774 4775 StackOOMSimulator simulator; 4776 if (!RunIterativeFailureTest(cx, args, simulator)) { 4777 return false; 4778 } 4779 4780 args.rval().setUndefined(); 4781 return true; 4782 } 4783 4784 struct FailingIterruptSimulator 4785 : public IterativeFailureTest::FailureSimulator { 4786 JSInterruptCallback* prevEnd = nullptr; 4787 4788 static bool failingInterruptCallback(JSContext* cx) { return false; } 4789 4790 void setup(JSContext* cx) override { 4791 prevEnd = cx->interruptCallbacks().end(); 4792 JS_AddInterruptCallback(cx, failingInterruptCallback); 4793 } 4794 4795 void teardown(JSContext* cx) override { 4796 cx->interruptCallbacks().erase(prevEnd, cx->interruptCallbacks().end()); 4797 } 4798 4799 void startSimulating(JSContext* cx, unsigned i, unsigned thread, 4800 bool keepFailing) override { 4801 js::oom::simulator.simulateFailureAfter( 4802 js::oom::FailureSimulator::Kind::Interrupt, i, thread, keepFailing); 4803 } 4804 4805 bool stopSimulating() override { 4806 bool handledInterrupt = js::oom::HadSimulatedInterrupt(); 4807 js::oom::simulator.reset(); 4808 return handledInterrupt; 4809 } 4810 }; 4811 4812 static bool InterruptTest(JSContext* cx, unsigned argc, Value* vp) { 4813 CallArgs args = CallArgsFromVp(argc, vp); 4814 4815 FailingIterruptSimulator simulator; 4816 if (!RunIterativeFailureTest(cx, args, simulator)) { 4817 return false; 4818 } 4819 4820 args.rval().setUndefined(); 4821 return true; 4822 } 4823 4824 #endif // defined(DEBUG) || defined(JS_OOM_BREAKPOINT) 4825 4826 static bool SettlePromiseNow(JSContext* cx, unsigned argc, Value* vp) { 4827 CallArgs args = CallArgsFromVp(argc, vp); 4828 if (!args.requireAtLeast(cx, "settlePromiseNow", 1)) { 4829 return false; 4830 } 4831 if (!args[0].isObject() || !args[0].toObject().is<PromiseObject>()) { 4832 JS_ReportErrorASCII(cx, "first argument must be a Promise object"); 4833 return false; 4834 } 4835 4836 Rooted<PromiseObject*> promise(cx, &args[0].toObject().as<PromiseObject>()); 4837 if (IsPromiseForAsyncFunctionOrGenerator(promise)) { 4838 JS_ReportErrorASCII( 4839 cx, "async function/generator's promise shouldn't be manually settled"); 4840 return false; 4841 } 4842 4843 if (promise->state() != JS::PromiseState::Pending) { 4844 JS_ReportErrorASCII(cx, "cannot settle an already-resolved promise"); 4845 return false; 4846 } 4847 4848 if (IsPromiseWithDefaultResolvingFunction(promise)) { 4849 SetAlreadyResolvedPromiseWithDefaultResolvingFunction(promise); 4850 } 4851 4852 int32_t flags = promise->flags(); 4853 promise->setFixedSlot( 4854 PromiseSlot_Flags, 4855 Int32Value(flags | PROMISE_FLAG_RESOLVED | PROMISE_FLAG_FULFILLED)); 4856 promise->setFixedSlot(PromiseSlot_ReactionsOrResult, UndefinedValue()); 4857 4858 DebugAPI::onPromiseSettled(cx, promise); 4859 return true; 4860 } 4861 4862 static bool GetWaitForAllPromise(JSContext* cx, unsigned argc, Value* vp) { 4863 CallArgs args = CallArgsFromVp(argc, vp); 4864 if (!args.requireAtLeast(cx, "getWaitForAllPromise", 1)) { 4865 return false; 4866 } 4867 if (!args[0].isObject() || !args[0].toObject().is<ArrayObject>() || 4868 args[0].toObject().as<NativeObject>().isIndexed()) { 4869 JS_ReportErrorASCII( 4870 cx, "first argument must be a dense Array of Promise objects"); 4871 return false; 4872 } 4873 Rooted<NativeObject*> list(cx, &args[0].toObject().as<NativeObject>()); 4874 RootedObjectVector promises(cx); 4875 uint32_t count = list->getDenseInitializedLength(); 4876 if (!promises.resize(count)) { 4877 return false; 4878 } 4879 4880 for (uint32_t i = 0; i < count; i++) { 4881 RootedValue elem(cx, list->getDenseElement(i)); 4882 if (!elem.isObject() || !elem.toObject().is<PromiseObject>()) { 4883 JS_ReportErrorASCII( 4884 cx, "Each entry in the passed-in Array must be a Promise"); 4885 return false; 4886 } 4887 promises[i].set(&elem.toObject()); 4888 } 4889 4890 RootedObject resultPromise(cx, JS::GetWaitForAllPromise(cx, promises)); 4891 if (!resultPromise) { 4892 return false; 4893 } 4894 4895 args.rval().set(ObjectValue(*resultPromise)); 4896 return true; 4897 } 4898 4899 static bool ResolvePromise(JSContext* cx, unsigned argc, Value* vp) { 4900 CallArgs args = CallArgsFromVp(argc, vp); 4901 if (!args.requireAtLeast(cx, "resolvePromise", 2)) { 4902 return false; 4903 } 4904 if (!args[0].isObject() || 4905 !UncheckedUnwrap(&args[0].toObject())->is<PromiseObject>()) { 4906 JS_ReportErrorASCII( 4907 cx, "first argument must be a maybe-wrapped Promise object"); 4908 return false; 4909 } 4910 4911 RootedObject promise(cx, &args[0].toObject()); 4912 RootedValue resolution(cx, args[1]); 4913 mozilla::Maybe<AutoRealm> ar; 4914 if (IsWrapper(promise)) { 4915 promise = UncheckedUnwrap(promise); 4916 ar.emplace(cx, promise); 4917 if (!cx->compartment()->wrap(cx, &resolution)) { 4918 return false; 4919 } 4920 } 4921 4922 if (IsPromiseForAsyncFunctionOrGenerator(promise)) { 4923 JS_ReportErrorASCII( 4924 cx, 4925 "async function/generator's promise shouldn't be manually resolved"); 4926 return false; 4927 } 4928 4929 bool result = JS::ResolvePromise(cx, promise, resolution); 4930 if (result) { 4931 args.rval().setUndefined(); 4932 } 4933 return result; 4934 } 4935 4936 static bool RejectPromise(JSContext* cx, unsigned argc, Value* vp) { 4937 CallArgs args = CallArgsFromVp(argc, vp); 4938 if (!args.requireAtLeast(cx, "rejectPromise", 2)) { 4939 return false; 4940 } 4941 if (!args[0].isObject() || 4942 !UncheckedUnwrap(&args[0].toObject())->is<PromiseObject>()) { 4943 JS_ReportErrorASCII( 4944 cx, "first argument must be a maybe-wrapped Promise object"); 4945 return false; 4946 } 4947 4948 RootedObject promise(cx, &args[0].toObject()); 4949 RootedValue reason(cx, args[1]); 4950 mozilla::Maybe<AutoRealm> ar; 4951 if (IsWrapper(promise)) { 4952 promise = UncheckedUnwrap(promise); 4953 ar.emplace(cx, promise); 4954 if (!cx->compartment()->wrap(cx, &reason)) { 4955 return false; 4956 } 4957 } 4958 4959 if (IsPromiseForAsyncFunctionOrGenerator(promise)) { 4960 JS_ReportErrorASCII( 4961 cx, 4962 "async function/generator's promise shouldn't be manually rejected"); 4963 return false; 4964 } 4965 4966 bool result = JS::RejectPromise(cx, promise, reason); 4967 if (result) { 4968 args.rval().setUndefined(); 4969 } 4970 return result; 4971 } 4972 4973 static unsigned finalizeCount = 0; 4974 4975 static void finalize_counter_finalize(JS::GCContext* gcx, JSObject* obj) { 4976 ++finalizeCount; 4977 } 4978 4979 static const JSClassOps FinalizeCounterClassOps = { 4980 nullptr, // addProperty 4981 nullptr, // delProperty 4982 nullptr, // enumerate 4983 nullptr, // newEnumerate 4984 nullptr, // resolve 4985 nullptr, // mayResolve 4986 finalize_counter_finalize, // finalize 4987 nullptr, // call 4988 nullptr, // construct 4989 nullptr, // trace 4990 }; 4991 4992 static const JSClass FinalizeCounterClass = { 4993 "FinalizeCounter", 4994 JSCLASS_FOREGROUND_FINALIZE, 4995 &FinalizeCounterClassOps, 4996 }; 4997 4998 static bool MakeFinalizeObserver(JSContext* cx, unsigned argc, Value* vp) { 4999 CallArgs args = CallArgsFromVp(argc, vp); 5000 5001 JSObject* obj = 5002 JS_NewObjectWithGivenProto(cx, &FinalizeCounterClass, nullptr); 5003 if (!obj) { 5004 return false; 5005 } 5006 5007 args.rval().setObject(*obj); 5008 return true; 5009 } 5010 5011 static bool FinalizeCount(JSContext* cx, unsigned argc, Value* vp) { 5012 CallArgs args = CallArgsFromVp(argc, vp); 5013 args.rval().setInt32(finalizeCount); 5014 return true; 5015 } 5016 5017 static bool ResetFinalizeCount(JSContext* cx, unsigned argc, Value* vp) { 5018 CallArgs args = CallArgsFromVp(argc, vp); 5019 finalizeCount = 0; 5020 args.rval().setUndefined(); 5021 return true; 5022 } 5023 5024 static bool DumpHeap(JSContext* cx, unsigned argc, Value* vp) { 5025 CallArgs args = CallArgsFromVp(argc, vp); 5026 5027 FILE* dumpFile = stdout; 5028 auto closeFile = mozilla::MakeScopeExit([&dumpFile] { 5029 if (dumpFile && dumpFile != stdout) { 5030 fclose(dumpFile); 5031 } 5032 }); 5033 5034 if (args.length() > 1) { 5035 RootedObject callee(cx, &args.callee()); 5036 ReportUsageErrorASCII(cx, callee, "Too many arguments"); 5037 return false; 5038 } 5039 5040 if (!args.get(0).isUndefined()) { 5041 RootedString str(cx, ToString(cx, args[0])); 5042 if (!str) { 5043 return false; 5044 } 5045 if (!fuzzingSafe) { 5046 UniqueChars fileNameBytes = JS_EncodeStringToUTF8(cx, str); 5047 if (!fileNameBytes) { 5048 return false; 5049 } 5050 #ifdef XP_WIN 5051 UniqueWideChars wideFileNameBytes = 5052 JS::EncodeUtf8ToWide(cx, fileNameBytes.get()); 5053 if (!wideFileNameBytes) { 5054 return false; 5055 } 5056 dumpFile = _wfopen(wideFileNameBytes.get(), L"w"); 5057 #else 5058 UniqueChars narrowFileNameBytes = 5059 JS::EncodeUtf8ToNarrow(cx, fileNameBytes.get()); 5060 if (!narrowFileNameBytes) { 5061 return false; 5062 } 5063 dumpFile = fopen(narrowFileNameBytes.get(), "w"); 5064 #endif 5065 if (!dumpFile) { 5066 JS_ReportErrorUTF8(cx, "can't open %s", fileNameBytes.get()); 5067 return false; 5068 } 5069 } 5070 } 5071 5072 js::DumpHeap(cx, dumpFile, js::IgnoreNurseryObjects); 5073 5074 args.rval().setUndefined(); 5075 return true; 5076 } 5077 5078 static bool Terminate(JSContext* cx, unsigned arg, Value* vp) { 5079 // Print a message to stderr in differential testing to help jsfunfuzz 5080 // find uncatchable-exception bugs. 5081 if (js::SupportDifferentialTesting()) { 5082 fprintf(stderr, "terminate called\n"); 5083 } 5084 5085 JS::ReportUncatchableException(cx); 5086 return false; 5087 } 5088 5089 static bool ReadGeckoProfilingStack(JSContext* cx, unsigned argc, Value* vp) { 5090 CallArgs args = CallArgsFromVp(argc, vp); 5091 args.rval().setUndefined(); 5092 5093 // Return boolean 'false' if profiler is not enabled. 5094 if (!cx->runtime()->geckoProfiler().enabled()) { 5095 args.rval().setBoolean(false); 5096 return true; 5097 } 5098 5099 // Array holding physical jit stack frames. 5100 RootedObject stack(cx, NewDenseEmptyArray(cx)); 5101 if (!stack) { 5102 return false; 5103 } 5104 5105 // If profiler sampling has been suppressed, return an empty 5106 // stack. 5107 if (!cx->isProfilerSamplingEnabled()) { 5108 args.rval().setObject(*stack); 5109 return true; 5110 } 5111 5112 struct InlineFrameInfo { 5113 InlineFrameInfo(const char* kind, UniqueChars label) 5114 : kind(kind), label(std::move(label)) {} 5115 const char* kind; 5116 UniqueChars label; 5117 }; 5118 5119 Vector<Vector<InlineFrameInfo, 0, TempAllocPolicy>, 0, TempAllocPolicy> 5120 frameInfo(cx); 5121 5122 JS::ProfilingFrameIterator::RegisterState state; 5123 for (JS::ProfilingFrameIterator i(cx, state); !i.done(); ++i) { 5124 MOZ_ASSERT(i.stackAddress() != nullptr); 5125 5126 if (!frameInfo.emplaceBack(cx)) { 5127 return false; 5128 } 5129 5130 const size_t MaxInlineFrames = 16; 5131 JS::ProfilingFrameIterator::Frame frames[MaxInlineFrames]; 5132 uint32_t nframes = i.extractStack(frames, 0, MaxInlineFrames); 5133 MOZ_ASSERT(nframes <= MaxInlineFrames); 5134 for (uint32_t i = 0; i < nframes; i++) { 5135 const char* frameKindStr = nullptr; 5136 switch (frames[i].kind) { 5137 case JS::ProfilingFrameIterator::Frame_BaselineInterpreter: 5138 frameKindStr = "baseline-interpreter"; 5139 break; 5140 case JS::ProfilingFrameIterator::Frame_Baseline: 5141 frameKindStr = "baseline-jit"; 5142 break; 5143 case JS::ProfilingFrameIterator::Frame_Ion: 5144 frameKindStr = "ion"; 5145 break; 5146 case JS::ProfilingFrameIterator::Frame_WasmBaseline: 5147 case JS::ProfilingFrameIterator::Frame_WasmIon: 5148 case JS::ProfilingFrameIterator::Frame_WasmOther: 5149 frameKindStr = "wasm"; 5150 break; 5151 default: 5152 frameKindStr = "unknown"; 5153 } 5154 5155 UniqueChars label = 5156 DuplicateStringToArena(js::StringBufferArena, cx, frames[i].label); 5157 if (!label) { 5158 return false; 5159 } 5160 5161 if (!frameInfo.back().emplaceBack(frameKindStr, std::move(label))) { 5162 return false; 5163 } 5164 } 5165 } 5166 5167 RootedObject inlineFrameInfo(cx); 5168 RootedString frameKind(cx); 5169 RootedString frameLabel(cx); 5170 RootedId idx(cx); 5171 5172 const unsigned propAttrs = JSPROP_ENUMERATE; 5173 5174 uint32_t physicalFrameNo = 0; 5175 for (auto& frame : frameInfo) { 5176 // Array holding all inline frames in a single physical jit stack frame. 5177 RootedObject inlineStack(cx, NewDenseEmptyArray(cx)); 5178 if (!inlineStack) { 5179 return false; 5180 } 5181 5182 uint32_t inlineFrameNo = 0; 5183 for (auto& inlineFrame : frame) { 5184 // Object holding frame info. 5185 RootedObject inlineFrameInfo(cx, NewPlainObject(cx)); 5186 if (!inlineFrameInfo) { 5187 return false; 5188 } 5189 5190 frameKind = NewStringCopyZ<CanGC>(cx, inlineFrame.kind); 5191 if (!frameKind) { 5192 return false; 5193 } 5194 5195 if (!JS_DefineProperty(cx, inlineFrameInfo, "kind", frameKind, 5196 propAttrs)) { 5197 return false; 5198 } 5199 5200 frameLabel = NewLatin1StringZ(cx, std::move(inlineFrame.label)); 5201 if (!frameLabel) { 5202 return false; 5203 } 5204 5205 if (!JS_DefineProperty(cx, inlineFrameInfo, "label", frameLabel, 5206 propAttrs)) { 5207 return false; 5208 } 5209 5210 idx = PropertyKey::Int(inlineFrameNo); 5211 if (!JS_DefinePropertyById(cx, inlineStack, idx, inlineFrameInfo, 0)) { 5212 return false; 5213 } 5214 5215 ++inlineFrameNo; 5216 } 5217 5218 // Push inline array into main array. 5219 idx = PropertyKey::Int(physicalFrameNo); 5220 if (!JS_DefinePropertyById(cx, stack, idx, inlineStack, 0)) { 5221 return false; 5222 } 5223 5224 ++physicalFrameNo; 5225 } 5226 5227 args.rval().setObject(*stack); 5228 return true; 5229 } 5230 5231 static bool ReadGeckoInterpProfilingStack(JSContext* cx, unsigned argc, 5232 Value* vp) { 5233 CallArgs args = CallArgsFromVp(argc, vp); 5234 args.rval().setUndefined(); 5235 5236 // Return boolean 'false' if profiler is not enabled. 5237 if (!cx->runtime()->geckoProfiler().enabled()) { 5238 args.rval().setBoolean(false); 5239 return true; 5240 } 5241 5242 // Array with information about each frame. 5243 Rooted<JSObject*> stack(cx, NewDenseEmptyArray(cx)); 5244 if (!stack) { 5245 return false; 5246 } 5247 uint32_t stackIndex = 0; 5248 5249 ProfilingStack* profStack = cx->geckoProfiler().getProfilingStack(); 5250 MOZ_ASSERT(profStack); 5251 5252 for (size_t i = 0; i < profStack->stackSize(); i++) { 5253 const auto& frame = profStack->frames[i]; 5254 if (!frame.isJsFrame()) { 5255 continue; 5256 } 5257 5258 // Skip fake JS frame pushed for js::RunScript by GeckoProfilerEntryMarker. 5259 const char* dynamicStr = frame.dynamicString(); 5260 if (!dynamicStr) { 5261 continue; 5262 } 5263 5264 Rooted<PlainObject*> frameInfo(cx, NewPlainObject(cx)); 5265 if (!frameInfo) { 5266 return false; 5267 } 5268 5269 Rooted<JSString*> dynamicString( 5270 cx, JS_NewStringCopyUTF8Z( 5271 cx, JS::ConstUTF8CharsZ(dynamicStr, strlen(dynamicStr)))); 5272 if (!dynamicString) { 5273 return false; 5274 } 5275 if (!JS_DefineProperty(cx, frameInfo, "dynamicString", dynamicString, 5276 JSPROP_ENUMERATE)) { 5277 return false; 5278 } 5279 5280 if (!JS_DefineElement(cx, stack, stackIndex, frameInfo, JSPROP_ENUMERATE)) { 5281 return false; 5282 } 5283 stackIndex++; 5284 } 5285 5286 args.rval().setObject(*stack); 5287 return true; 5288 } 5289 5290 static bool EnableOsiPointRegisterChecks(JSContext*, unsigned argc, Value* vp) { 5291 CallArgs args = CallArgsFromVp(argc, vp); 5292 #ifdef CHECK_OSIPOINT_REGISTERS 5293 jit::JitOptions.checkOsiPointRegisters = true; 5294 #endif 5295 args.rval().setUndefined(); 5296 return true; 5297 } 5298 5299 static bool DisplayName(JSContext* cx, unsigned argc, Value* vp) { 5300 CallArgs args = CallArgsFromVp(argc, vp); 5301 if (!args.get(0).isObject() || !args[0].toObject().is<JSFunction>()) { 5302 RootedObject arg(cx, &args.callee()); 5303 ReportUsageErrorASCII(cx, arg, "Must have one function argument"); 5304 return false; 5305 } 5306 5307 JSFunction* fun = &args[0].toObject().as<JSFunction>(); 5308 JS::Rooted<JSAtom*> str(cx); 5309 if (!fun->getDisplayAtom(cx, &str)) { 5310 return false; 5311 } 5312 args.rval().setString(str ? str : cx->runtime()->emptyString.ref()); 5313 return true; 5314 } 5315 5316 static bool IsAvxPresent(JSContext* cx, unsigned argc, Value* vp) { 5317 CallArgs args = CallArgsFromVp(argc, vp); 5318 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) 5319 int minVersion = 1; 5320 if (argc > 0 && args.get(0).isNumber()) { 5321 minVersion = std::max(1, int(args[0].toNumber())); 5322 } 5323 switch (minVersion) { 5324 case 1: 5325 args.rval().setBoolean(jit::Assembler::HasAVX()); 5326 return true; 5327 case 2: 5328 args.rval().setBoolean(jit::Assembler::HasAVX2()); 5329 return true; 5330 } 5331 #endif 5332 args.rval().setBoolean(false); 5333 return true; 5334 } 5335 5336 class ShellAllocationMetadataBuilder : public AllocationMetadataBuilder { 5337 public: 5338 ShellAllocationMetadataBuilder() = default; 5339 5340 virtual JSObject* build(JSContext* cx, HandleObject, 5341 AutoEnterOOMUnsafeRegion& oomUnsafe) const override; 5342 5343 static const ShellAllocationMetadataBuilder metadataBuilder; 5344 }; 5345 5346 JSObject* ShellAllocationMetadataBuilder::build( 5347 JSContext* cx, HandleObject, AutoEnterOOMUnsafeRegion& oomUnsafe) const { 5348 RootedObject obj(cx, NewPlainObject(cx)); 5349 if (!obj) { 5350 oomUnsafe.crash("ShellAllocationMetadataBuilder::build"); 5351 } 5352 5353 RootedObject stack(cx, NewDenseEmptyArray(cx)); 5354 if (!stack) { 5355 oomUnsafe.crash("ShellAllocationMetadataBuilder::build"); 5356 } 5357 5358 static int createdIndex = 0; 5359 createdIndex++; 5360 5361 if (!JS_DefineProperty(cx, obj, "index", createdIndex, 0)) { 5362 oomUnsafe.crash("ShellAllocationMetadataBuilder::build"); 5363 } 5364 5365 if (!JS_DefineProperty(cx, obj, "stack", stack, 0)) { 5366 oomUnsafe.crash("ShellAllocationMetadataBuilder::build"); 5367 } 5368 5369 int stackIndex = 0; 5370 RootedId id(cx); 5371 RootedValue callee(cx); 5372 for (NonBuiltinScriptFrameIter iter(cx); !iter.done(); ++iter) { 5373 if (iter.isFunctionFrame() && iter.compartment() == cx->compartment()) { 5374 id = PropertyKey::Int(stackIndex); 5375 RootedObject callee(cx, iter.callee(cx)); 5376 if (!JS_DefinePropertyById(cx, stack, id, callee, JSPROP_ENUMERATE)) { 5377 oomUnsafe.crash("ShellAllocationMetadataBuilder::build"); 5378 } 5379 stackIndex++; 5380 } 5381 } 5382 5383 return obj; 5384 } 5385 5386 const ShellAllocationMetadataBuilder 5387 ShellAllocationMetadataBuilder::metadataBuilder; 5388 5389 static bool EnableShellAllocationMetadataBuilder(JSContext* cx, unsigned argc, 5390 Value* vp) { 5391 CallArgs args = CallArgsFromVp(argc, vp); 5392 5393 SetAllocationMetadataBuilder( 5394 cx, &ShellAllocationMetadataBuilder::metadataBuilder); 5395 5396 args.rval().setUndefined(); 5397 return true; 5398 } 5399 5400 static bool GetAllocationMetadata(JSContext* cx, unsigned argc, Value* vp) { 5401 CallArgs args = CallArgsFromVp(argc, vp); 5402 if (args.length() != 1 || !args[0].isObject()) { 5403 JS_ReportErrorASCII(cx, "Argument must be an object"); 5404 return false; 5405 } 5406 5407 args.rval().setObjectOrNull(GetAllocationMetadata(&args[0].toObject())); 5408 return true; 5409 } 5410 5411 static bool testingFunc_bailout(JSContext* cx, unsigned argc, Value* vp) { 5412 CallArgs args = CallArgsFromVp(argc, vp); 5413 5414 // NOP when not in IonMonkey 5415 args.rval().setUndefined(); 5416 return true; 5417 } 5418 5419 static bool testingFunc_bailAfter(JSContext* cx, unsigned argc, Value* vp) { 5420 CallArgs args = CallArgsFromVp(argc, vp); 5421 if (args.length() != 1 || !args[0].isInt32() || args[0].toInt32() < 0) { 5422 JS_ReportErrorASCII( 5423 cx, "Argument must be a positive number that fits in an int32"); 5424 return false; 5425 } 5426 5427 #ifdef DEBUG 5428 if (auto* jitRuntime = cx->runtime()->jitRuntime()) { 5429 uint32_t bailAfter = args[0].toInt32(); 5430 bool enableBailAfter = bailAfter > 0; 5431 if (jitRuntime->ionBailAfterEnabled() != enableBailAfter) { 5432 // Force JIT code to be recompiled with (or without) instrumentation. 5433 ReleaseAllJITCode(cx->gcContext()); 5434 jitRuntime->setIonBailAfterEnabled(enableBailAfter); 5435 } 5436 jitRuntime->setIonBailAfterCounter(bailAfter); 5437 } 5438 #endif 5439 5440 args.rval().setUndefined(); 5441 return true; 5442 } 5443 5444 static bool testingFunc_invalidate(JSContext* cx, unsigned argc, Value* vp) { 5445 CallArgs args = CallArgsFromVp(argc, vp); 5446 5447 // If the topmost frame is Ion/Warp, find the IonScript and invalidate it. 5448 FrameIter iter(cx); 5449 if (!iter.done() && iter.isIon()) { 5450 while (!iter.isPhysicalJitFrame()) { 5451 ++iter; 5452 } 5453 if (iter.script()->hasIonScript()) { 5454 js::jit::Invalidate(cx, iter.script()); 5455 } 5456 } 5457 5458 args.rval().setUndefined(); 5459 return true; 5460 } 5461 5462 static constexpr unsigned JitWarmupResetLimit = 20; 5463 static_assert(JitWarmupResetLimit <= 5464 unsigned(JSScript::MutableFlags::WarmupResets_MASK), 5465 "JitWarmupResetLimit exceeds max value"); 5466 5467 static bool testingFunc_inJit(JSContext* cx, unsigned argc, Value* vp) { 5468 CallArgs args = CallArgsFromVp(argc, vp); 5469 5470 if (!jit::IsBaselineJitEnabled(cx)) { 5471 return ReturnStringCopy(cx, args, "Baseline is disabled."); 5472 } 5473 5474 // Use frame iterator to inspect caller. 5475 FrameIter iter(cx); 5476 5477 // We may be invoked directly, not in a JS context, e.g. if inJit is added as 5478 // a callback on the event queue. 5479 if (iter.done()) { 5480 args.rval().setBoolean(false); 5481 return true; 5482 } 5483 5484 if (iter.hasScript()) { 5485 // Detect repeated attempts to compile, resetting the counter if inJit 5486 // succeeds. Note: This script may have be inlined into its caller. 5487 if (iter.isJSJit()) { 5488 iter.script()->resetWarmUpResetCounter(); 5489 } else if (iter.script()->getWarmUpResetCount() >= JitWarmupResetLimit) { 5490 return ReturnStringCopy( 5491 cx, args, "Compilation is being repeatedly prevented. Giving up."); 5492 } 5493 } 5494 5495 // Returns true for any JIT (including WASM). 5496 MOZ_ASSERT_IF(iter.isJSJit(), cx->currentlyRunningInJit()); 5497 args.rval().setBoolean(cx->currentlyRunningInJit()); 5498 return true; 5499 } 5500 5501 static bool testingFunc_inIon(JSContext* cx, unsigned argc, Value* vp) { 5502 CallArgs args = CallArgsFromVp(argc, vp); 5503 5504 if (!jit::IsIonEnabled(cx)) { 5505 return ReturnStringCopy(cx, args, "Ion is disabled."); 5506 } 5507 5508 // Use frame iterator to inspect caller. 5509 FrameIter iter(cx); 5510 5511 // We may be invoked directly, not in a JS context, e.g. if inIon is added as 5512 // a callback on the event queue. 5513 if (iter.done()) { 5514 args.rval().setBoolean(false); 5515 return true; 5516 } 5517 5518 if (iter.hasScript()) { 5519 // Detect repeated attempts to compile, resetting the counter if inIon 5520 // succeeds. Note: This script may have be inlined into its caller. 5521 if (iter.isIon()) { 5522 iter.script()->resetWarmUpResetCounter(); 5523 } else if (!iter.script()->canIonCompile()) { 5524 return ReturnStringCopy(cx, args, "Unable to Ion-compile this script."); 5525 } else if (iter.script()->getWarmUpResetCount() >= JitWarmupResetLimit) { 5526 return ReturnStringCopy( 5527 cx, args, "Compilation is being repeatedly prevented. Giving up."); 5528 } 5529 } 5530 5531 args.rval().setBoolean(iter.isIon()); 5532 return true; 5533 } 5534 5535 bool js::testingFunc_assertFloat32(JSContext* cx, unsigned argc, Value* vp) { 5536 CallArgs args = CallArgsFromVp(argc, vp); 5537 if (args.length() != 2) { 5538 JS_ReportErrorASCII(cx, "Expects only 2 arguments"); 5539 return false; 5540 } 5541 5542 // NOP when not in IonMonkey 5543 args.rval().setUndefined(); 5544 return true; 5545 } 5546 5547 static bool TestingFunc_assertJitStackInvariants(JSContext* cx, unsigned argc, 5548 Value* vp) { 5549 CallArgs args = CallArgsFromVp(argc, vp); 5550 5551 jit::AssertJitStackInvariants(cx); 5552 args.rval().setUndefined(); 5553 return true; 5554 } 5555 5556 bool js::testingFunc_assertRecoveredOnBailout(JSContext* cx, unsigned argc, 5557 Value* vp) { 5558 CallArgs args = CallArgsFromVp(argc, vp); 5559 if (args.length() != 2) { 5560 JS_ReportErrorASCII(cx, "Expects only 2 arguments"); 5561 return false; 5562 } 5563 5564 // NOP when not in IonMonkey 5565 args.rval().setUndefined(); 5566 return true; 5567 } 5568 5569 static bool GetJitCompilerOptions(JSContext* cx, unsigned argc, Value* vp) { 5570 CallArgs args = CallArgsFromVp(argc, vp); 5571 RootedObject info(cx, JS_NewPlainObject(cx)); 5572 if (!info) { 5573 return false; 5574 } 5575 5576 uint32_t intValue = 0; 5577 RootedValue value(cx); 5578 5579 #define JIT_COMPILER_MATCH(key, string) \ 5580 opt = JSJITCOMPILER_##key; \ 5581 if (JS_GetGlobalJitCompilerOption(cx, opt, &intValue)) { \ 5582 value.setInt32(intValue); \ 5583 if (!JS_SetProperty(cx, info, string, value)) return false; \ 5584 } 5585 5586 JSJitCompilerOption opt = JSJITCOMPILER_NOT_AN_OPTION; 5587 JIT_COMPILER_OPTIONS(JIT_COMPILER_MATCH); 5588 #undef JIT_COMPILER_MATCH 5589 5590 args.rval().setObject(*info); 5591 return true; 5592 } 5593 5594 static bool SetIonCheckGraphCoherency(JSContext* cx, unsigned argc, Value* vp) { 5595 CallArgs args = CallArgsFromVp(argc, vp); 5596 jit::JitOptions.checkGraphConsistency = ToBoolean(args.get(0)); 5597 args.rval().setUndefined(); 5598 return true; 5599 } 5600 5601 // A JSObject that holds structured clone data, similar to the C++ class 5602 // JSAutoStructuredCloneBuffer. 5603 class CloneBufferObject : public NativeObject { 5604 static const JSPropertySpec props_[3]; 5605 5606 static const size_t DATA_SLOT = 0; 5607 static const size_t SYNTHETIC_SLOT = 1; 5608 static const size_t NUM_SLOTS = 2; 5609 5610 public: 5611 static const JSClass class_; 5612 5613 static CloneBufferObject* Create(JSContext* cx) { 5614 RootedObject obj(cx, JS_NewObject(cx, &class_)); 5615 if (!obj) { 5616 return nullptr; 5617 } 5618 obj->as<CloneBufferObject>().setReservedSlot(DATA_SLOT, 5619 PrivateValue(nullptr)); 5620 obj->as<CloneBufferObject>().setReservedSlot(SYNTHETIC_SLOT, 5621 BooleanValue(false)); 5622 5623 if (!JS_DefineProperties(cx, obj, props_)) { 5624 return nullptr; 5625 } 5626 5627 return &obj->as<CloneBufferObject>(); 5628 } 5629 5630 static CloneBufferObject* Create(JSContext* cx, 5631 JSAutoStructuredCloneBuffer* buffer) { 5632 Rooted<CloneBufferObject*> obj(cx, Create(cx)); 5633 if (!obj) { 5634 return nullptr; 5635 } 5636 auto data = js::MakeUnique<JSStructuredCloneData>(buffer->scope()); 5637 if (!data) { 5638 ReportOutOfMemory(cx); 5639 return nullptr; 5640 } 5641 buffer->giveTo(data.get()); 5642 obj->setData(data.release(), false); 5643 return obj; 5644 } 5645 5646 JSStructuredCloneData* data() const { 5647 return static_cast<JSStructuredCloneData*>( 5648 getReservedSlot(DATA_SLOT).toPrivate()); 5649 } 5650 5651 bool isSynthetic() const { 5652 return getReservedSlot(SYNTHETIC_SLOT).toBoolean(); 5653 } 5654 5655 void setData(JSStructuredCloneData* aData, bool synthetic) { 5656 MOZ_ASSERT(!data()); 5657 setReservedSlot(DATA_SLOT, PrivateValue(aData)); 5658 setReservedSlot(SYNTHETIC_SLOT, BooleanValue(synthetic)); 5659 } 5660 5661 // Discard an owned clone buffer. 5662 void discard() { 5663 js_delete(data()); 5664 setReservedSlot(DATA_SLOT, PrivateValue(nullptr)); 5665 } 5666 5667 static bool setCloneBuffer_impl(JSContext* cx, const CallArgs& args) { 5668 Rooted<CloneBufferObject*> obj( 5669 cx, &args.thisv().toObject().as<CloneBufferObject>()); 5670 5671 const char* data = nullptr; 5672 UniqueChars dataOwner; 5673 size_t nbytes; 5674 5675 if (args.get(0).isObject() && args[0].toObject().is<ArrayBufferObject>()) { 5676 ArrayBufferObject* buffer = &args[0].toObject().as<ArrayBufferObject>(); 5677 bool isSharedMemory; 5678 uint8_t* dataBytes = nullptr; 5679 JS::GetArrayBufferLengthAndData(buffer, &nbytes, &isSharedMemory, 5680 &dataBytes); 5681 MOZ_ASSERT(!isSharedMemory); 5682 data = reinterpret_cast<char*>(dataBytes); 5683 } else { 5684 JSString* str = JS::ToString(cx, args.get(0)); 5685 if (!str) { 5686 return false; 5687 } 5688 dataOwner = JS_EncodeStringToLatin1(cx, str); 5689 if (!dataOwner) { 5690 return false; 5691 } 5692 data = dataOwner.get(); 5693 nbytes = JS_GetStringLength(str); 5694 } 5695 5696 if (nbytes == 0 || (nbytes % sizeof(uint64_t) != 0)) { 5697 JS_ReportErrorASCII(cx, "Invalid length for clonebuffer data"); 5698 return false; 5699 } 5700 5701 auto buf = js::MakeUnique<JSStructuredCloneData>( 5702 JS::StructuredCloneScope::DifferentProcess); 5703 if (!buf || !buf->Init(nbytes)) { 5704 ReportOutOfMemory(cx); 5705 return false; 5706 } 5707 5708 MOZ_ALWAYS_TRUE(buf->AppendBytes(data, nbytes)); 5709 obj->discard(); 5710 obj->setData(buf.release(), true); 5711 5712 args.rval().setUndefined(); 5713 return true; 5714 } 5715 5716 static bool is(HandleValue v) { 5717 return v.isObject() && v.toObject().is<CloneBufferObject>(); 5718 } 5719 5720 static bool setCloneBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) { 5721 CallArgs args = CallArgsFromVp(argc, vp); 5722 return CallNonGenericMethod<is, setCloneBuffer_impl>(cx, args); 5723 } 5724 5725 static bool getData(JSContext* cx, Handle<CloneBufferObject*> obj, 5726 JSStructuredCloneData** data) { 5727 if (!obj->data()) { 5728 *data = nullptr; 5729 return true; 5730 } 5731 5732 bool hasTransferable; 5733 if (!JS_StructuredCloneHasTransferables(*obj->data(), &hasTransferable)) { 5734 return false; 5735 } 5736 5737 if (hasTransferable) { 5738 JS_ReportErrorASCII( 5739 cx, "cannot retrieve structured clone buffer with transferables"); 5740 return false; 5741 } 5742 5743 *data = obj->data(); 5744 return true; 5745 } 5746 5747 static bool getCloneBuffer_impl(JSContext* cx, const CallArgs& args) { 5748 Rooted<CloneBufferObject*> obj( 5749 cx, &args.thisv().toObject().as<CloneBufferObject>()); 5750 if (args.length() != 0) { 5751 RootedObject callee(cx, &args.callee()); 5752 ReportUsageErrorASCII(cx, callee, "Too many arguments"); 5753 return false; 5754 } 5755 5756 JSStructuredCloneData* data; 5757 if (!getData(cx, obj, &data)) { 5758 return false; 5759 } 5760 5761 if (data == nullptr) { 5762 args.rval().setUndefined(); 5763 return true; 5764 } 5765 5766 size_t size = data->Size(); 5767 UniqueChars buffer(js_pod_malloc<char>(size)); 5768 if (!buffer) { 5769 ReportOutOfMemory(cx); 5770 return false; 5771 } 5772 auto iter = data->Start(); 5773 if (!data->ReadBytes(iter, buffer.get(), size)) { 5774 ReportOutOfMemory(cx); 5775 return false; 5776 } 5777 JSString* str = JS_NewStringCopyN(cx, buffer.get(), size); 5778 if (!str) { 5779 return false; 5780 } 5781 args.rval().setString(str); 5782 return true; 5783 } 5784 5785 static bool getCloneBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) { 5786 CallArgs args = CallArgsFromVp(argc, vp); 5787 return CallNonGenericMethod<is, getCloneBuffer_impl>(cx, args); 5788 } 5789 5790 static bool getCloneBufferAsArrayBuffer_impl(JSContext* cx, 5791 const CallArgs& args) { 5792 Rooted<CloneBufferObject*> obj( 5793 cx, &args.thisv().toObject().as<CloneBufferObject>()); 5794 if (args.length() != 0) { 5795 RootedObject callee(cx, &args.callee()); 5796 ReportUsageErrorASCII(cx, callee, "Too many arguments"); 5797 return false; 5798 } 5799 5800 JSStructuredCloneData* data; 5801 if (!getData(cx, obj, &data)) { 5802 return false; 5803 } 5804 5805 if (data == nullptr) { 5806 args.rval().setUndefined(); 5807 return true; 5808 } 5809 5810 size_t size = data->Size(); 5811 UniqueChars buffer(js_pod_malloc<char>(size)); 5812 if (!buffer) { 5813 ReportOutOfMemory(cx); 5814 return false; 5815 } 5816 auto iter = data->Start(); 5817 if (!data->ReadBytes(iter, buffer.get(), size)) { 5818 ReportOutOfMemory(cx); 5819 return false; 5820 } 5821 5822 JSObject* arrayBuffer = 5823 JS::NewArrayBufferWithContents(cx, size, std::move(buffer)); 5824 if (!arrayBuffer) { 5825 return false; 5826 } 5827 5828 args.rval().setObject(*arrayBuffer); 5829 return true; 5830 } 5831 5832 static bool getCloneBufferAsArrayBuffer(JSContext* cx, unsigned int argc, 5833 JS::Value* vp) { 5834 CallArgs args = CallArgsFromVp(argc, vp); 5835 return CallNonGenericMethod<is, getCloneBufferAsArrayBuffer_impl>(cx, args); 5836 } 5837 5838 static void Finalize(JS::GCContext* gcx, JSObject* obj) { 5839 obj->as<CloneBufferObject>().discard(); 5840 } 5841 }; 5842 5843 static const JSClassOps CloneBufferObjectClassOps = { 5844 nullptr, // addProperty 5845 nullptr, // delProperty 5846 nullptr, // enumerate 5847 nullptr, // newEnumerate 5848 nullptr, // resolve 5849 nullptr, // mayResolve 5850 CloneBufferObject::Finalize, // finalize 5851 nullptr, // call 5852 nullptr, // construct 5853 nullptr, // trace 5854 }; 5855 5856 const JSClass CloneBufferObject::class_ = { 5857 "CloneBuffer", 5858 JSCLASS_HAS_RESERVED_SLOTS(CloneBufferObject::NUM_SLOTS) | 5859 JSCLASS_FOREGROUND_FINALIZE, 5860 &CloneBufferObjectClassOps, 5861 }; 5862 5863 const JSPropertySpec CloneBufferObject::props_[] = { 5864 JS_PSGS("clonebuffer", getCloneBuffer, setCloneBuffer, 0), 5865 JS_PSGS("arraybuffer", getCloneBufferAsArrayBuffer, setCloneBuffer, 0), 5866 JS_PS_END, 5867 }; 5868 5869 static mozilla::Maybe<JS::StructuredCloneScope> ParseCloneScope( 5870 JSContext* cx, HandleString str) { 5871 mozilla::Maybe<JS::StructuredCloneScope> scope; 5872 5873 JSLinearString* scopeStr = str->ensureLinear(cx); 5874 if (!scopeStr) { 5875 return scope; 5876 } 5877 5878 if (StringEqualsLiteral(scopeStr, "SameProcess")) { 5879 scope.emplace(JS::StructuredCloneScope::SameProcess); 5880 } else if (StringEqualsLiteral(scopeStr, "DifferentProcess")) { 5881 scope.emplace(JS::StructuredCloneScope::DifferentProcess); 5882 } else if (StringEqualsLiteral(scopeStr, "DifferentProcessForIndexedDB")) { 5883 scope.emplace(JS::StructuredCloneScope::DifferentProcessForIndexedDB); 5884 } 5885 5886 return scope; 5887 } 5888 5889 // A custom object that is serializable and transferable using 5890 // the engine's custom hooks. The callbacks log their activity 5891 // to a JSRuntime-wide log (tagging actions with IDs to distinguish them). 5892 class CustomSerializableObject : public NativeObject { 5893 static const size_t ID_SLOT = 0; 5894 static const size_t DETACHED_SLOT = 1; 5895 static const size_t BEHAVIOR_SLOT = 2; 5896 static const size_t NUM_SLOTS = 3; 5897 5898 static constexpr size_t MAX_LOG_LEN = 100; 5899 5900 // The activity log should be specific to a JSRuntime. 5901 struct ActivityLog { 5902 uint32_t buffer[MAX_LOG_LEN]; 5903 size_t length = 0; 5904 5905 static MOZ_THREAD_LOCAL(ActivityLog*) self; 5906 static ActivityLog* getThreadLog() { 5907 if (!self.initialized() || !self.get()) { 5908 self.infallibleInit(); 5909 AutoEnterOOMUnsafeRegion oomUnsafe; 5910 self.set(js_new<ActivityLog>()); 5911 if (!self.get()) { 5912 oomUnsafe.crash("allocating activity log"); 5913 } 5914 if (!TlsContext.get()->runtime()->atExit( 5915 [](void* vpData) { 5916 auto* log = static_cast<ActivityLog*>(vpData); 5917 js_delete(log); 5918 }, 5919 self.get())) { 5920 oomUnsafe.crash("atExit"); 5921 } 5922 } 5923 return self.get(); 5924 } 5925 5926 static bool log(int32_t id, char action) { 5927 return getThreadLog()->logImpl(id, action); 5928 } 5929 5930 bool logImpl(int32_t id, char action) { 5931 if (length + 2 > MAX_LOG_LEN) { 5932 return false; 5933 } 5934 buffer[length++] = id; 5935 buffer[length++] = uint32_t(action); 5936 return true; 5937 } 5938 }; 5939 5940 public: 5941 enum class Behavior { 5942 Nothing = 0, 5943 FailDuringReadTransfer = 1, 5944 FailDuringRead = 2 5945 }; 5946 5947 static constexpr JSClass class_ = { 5948 "CustomSerializable", 5949 JSCLASS_HAS_RESERVED_SLOTS(NUM_SLOTS), 5950 }; 5951 5952 static bool is(HandleValue v) { 5953 return v.isObject() && v.toObject().is<CustomSerializableObject>(); 5954 } 5955 5956 static CustomSerializableObject* Create(JSContext* cx, int32_t id, 5957 Behavior behavior) { 5958 Rooted<CustomSerializableObject*> obj( 5959 cx, static_cast<CustomSerializableObject*>(JS_NewObject(cx, &class_))); 5960 if (!obj) { 5961 return nullptr; 5962 } 5963 obj->setReservedSlot(ID_SLOT, Int32Value(id)); 5964 obj->setReservedSlot(DETACHED_SLOT, BooleanValue(false)); 5965 obj->setReservedSlot(BEHAVIOR_SLOT, 5966 Int32Value(static_cast<int32_t>(behavior))); 5967 5968 if (!JS_DefineProperty(cx, obj, "log", getLog, clearLog, 0)) { 5969 return nullptr; 5970 } 5971 5972 return obj; 5973 } 5974 5975 public: 5976 static uint32_t tag() { return JS_SCTAG_USER_MIN; } 5977 5978 static bool log(int32_t id, char action) { 5979 return ActivityLog::log(id, action); 5980 } 5981 bool log(char action) { 5982 return log(getReservedSlot(ID_SLOT).toInt32(), action); 5983 } 5984 5985 void detach() { setReservedSlot(DETACHED_SLOT, BooleanValue(true)); } 5986 bool isDetached() { return getReservedSlot(DETACHED_SLOT).toBoolean(); } 5987 5988 uint32_t id() const { return getReservedSlot(ID_SLOT).toInt32(); } 5989 Behavior behavior() { 5990 return static_cast<Behavior>(getReservedSlot(BEHAVIOR_SLOT).toInt32()); 5991 } 5992 5993 static bool getLog(JSContext* cx, unsigned int argc, JS::Value* vp) { 5994 CallArgs args = CallArgsFromVp(argc, vp); 5995 return CallNonGenericMethod<is, getLog_impl>(cx, args); 5996 } 5997 5998 static bool getLog_impl(JSContext* cx, const CallArgs& args) { 5999 Rooted<CustomSerializableObject*> obj( 6000 cx, &args.thisv().toObject().as<CustomSerializableObject>()); 6001 6002 size_t len = ActivityLog::getThreadLog()->length; 6003 uint32_t* logBuffer = ActivityLog::getThreadLog()->buffer; 6004 6005 Rooted<ArrayObject*> result(cx, NewDenseFullyAllocatedArray(cx, len)); 6006 if (!result) { 6007 return false; 6008 } 6009 result->ensureDenseInitializedLength(0, len); 6010 6011 for (size_t p = 0; p < len; p += 2) { 6012 int32_t id = int32_t(logBuffer[p]); 6013 char action = char(logBuffer[p + 1]); 6014 result->setDenseElement(p, Int32Value(id)); 6015 JSString* str = JS_NewStringCopyN(cx, &action, 1); 6016 if (!str) { 6017 return false; 6018 } 6019 result->setDenseElement(p + 1, StringValue(str)); 6020 } 6021 6022 args.rval().setObject(*result); 6023 return true; 6024 } 6025 6026 static bool clearLog(JSContext* cx, unsigned int argc, JS::Value* vp) { 6027 CallArgs args = CallArgsFromVp(argc, vp); 6028 if (!args.get(0).isNullOrUndefined()) { 6029 JS_ReportErrorASCII(cx, "log may only be assigned null/undefined"); 6030 return false; 6031 } 6032 ActivityLog::getThreadLog()->length = 0; 6033 args.rval().setUndefined(); 6034 return true; 6035 } 6036 6037 static bool Write(JSContext* cx, JSStructuredCloneWriter* w, 6038 JS::HandleObject aObj, bool* sameProcessScopeRequired, 6039 void* closure) { 6040 Rooted<CustomSerializableObject*> obj(cx); 6041 6042 if ((obj = aObj->maybeUnwrapIf<CustomSerializableObject>())) { 6043 obj->log('w'); 6044 // Write a regular clone as a <tag, id> pair, followed by <0, behavior>. 6045 // Note that transferring will communicate the behavior via a different 6046 // mechanism. 6047 return JS_WriteUint32Pair(w, obj->tag(), obj->id()) && 6048 JS_WriteUint32Pair(w, 0, static_cast<uint32_t>(obj->behavior())); 6049 } 6050 6051 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 6052 JSMSG_SC_UNSUPPORTED_TYPE); 6053 return false; 6054 } 6055 6056 static JSObject* Read(JSContext* cx, JSStructuredCloneReader* r, 6057 const JS::CloneDataPolicy& cloneDataPolicy, 6058 uint32_t tag, uint32_t id, void* closure) { 6059 uint32_t dummy, behaviorData; 6060 if (!JS_ReadUint32Pair(r, &dummy, &behaviorData)) { 6061 return nullptr; 6062 } 6063 if (dummy != 0 || id > INT32_MAX) { 6064 JS_ReportErrorASCII(cx, "out of range"); 6065 return nullptr; 6066 } 6067 6068 auto b = static_cast<Behavior>(behaviorData); 6069 Rooted<CustomSerializableObject*> obj( 6070 cx, Create(cx, static_cast<int32_t>(id), b)); 6071 if (!obj) { 6072 return nullptr; 6073 } 6074 6075 obj->log('r'); 6076 if (obj->behavior() == Behavior::FailDuringRead) { 6077 JS_ReportErrorASCII(cx, 6078 "Failed as requested in read during deserialization"); 6079 return nullptr; 6080 } 6081 return obj; 6082 } 6083 6084 static bool CanTransfer(JSContext* cx, JS::Handle<JSObject*> wrapped, 6085 bool* sameProcessScopeRequired, void* closure) { 6086 Rooted<CustomSerializableObject*> obj(cx); 6087 6088 if ((obj = wrapped->maybeUnwrapIf<CustomSerializableObject>())) { 6089 obj->log('?'); 6090 // For now, all CustomSerializable objects are considered to be 6091 // transferable. 6092 return true; 6093 } 6094 6095 return false; 6096 } 6097 6098 static bool WriteTransfer(JSContext* cx, JS::Handle<JSObject*> aObj, 6099 void* closure, uint32_t* tag, 6100 JS::TransferableOwnership* ownership, 6101 void** content, uint64_t* extraData) { 6102 Rooted<CustomSerializableObject*> obj(cx); 6103 6104 if ((obj = aObj->maybeUnwrapIf<CustomSerializableObject>())) { 6105 if (obj->isDetached()) { 6106 JS_ReportErrorASCII(cx, "Attempted to transfer detached object"); 6107 return false; 6108 } 6109 obj->log('W'); 6110 *content = reinterpret_cast<void*>(obj->id()); 6111 *extraData = static_cast<uint64_t>(obj->behavior()); 6112 *tag = obj->tag(); 6113 *ownership = JS::SCTAG_TMO_CUSTOM; 6114 obj->detach(); 6115 return true; 6116 } 6117 6118 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 6119 JSMSG_SC_NOT_TRANSFERABLE); 6120 return false; 6121 } 6122 6123 static bool ReadTransfer(JSContext* cx, JSStructuredCloneReader* r, 6124 const JS::CloneDataPolicy& cloneDataPolicy, 6125 uint32_t tag, void* content, uint64_t extraData, 6126 void* closure, 6127 JS::MutableHandleObject returnObject) { 6128 if (tag == CustomSerializableObject::tag()) { 6129 int32_t id = int32_t(reinterpret_cast<uintptr_t>(content)); 6130 Rooted<CustomSerializableObject*> obj( 6131 cx, CustomSerializableObject::Create( 6132 cx, id, static_cast<Behavior>(extraData))); 6133 if (!obj) { 6134 return false; 6135 } 6136 obj->log('R'); 6137 if (obj->behavior() == Behavior::FailDuringReadTransfer) { 6138 return false; 6139 } 6140 returnObject.set(obj); 6141 return true; 6142 } 6143 6144 return false; 6145 } 6146 6147 static void FreeTransfer(uint32_t tag, JS::TransferableOwnership ownership, 6148 void* content, uint64_t extraData, void* closure) { 6149 CustomSerializableObject::log(uint32_t(reinterpret_cast<intptr_t>(content)), 6150 'F'); 6151 } 6152 }; 6153 6154 MOZ_THREAD_LOCAL(CustomSerializableObject::ActivityLog*) 6155 CustomSerializableObject::ActivityLog::self; 6156 6157 static bool MakeSerializable(JSContext* cx, unsigned argc, Value* vp) { 6158 CallArgs args = CallArgsFromVp(argc, vp); 6159 6160 int32_t id = 0; 6161 if (args.get(0).isInt32()) { 6162 id = args[0].toInt32(); 6163 if (id < 0) { 6164 JS_ReportErrorASCII(cx, "id out of range"); 6165 return false; 6166 } 6167 } 6168 CustomSerializableObject::Behavior behavior = 6169 CustomSerializableObject::Behavior::Nothing; 6170 if (args.get(1).isInt32()) { 6171 int32_t iv = args[1].toInt32(); 6172 constexpr int32_t min = 6173 static_cast<int32_t>(CustomSerializableObject::Behavior::Nothing); 6174 constexpr int32_t max = static_cast<int32_t>( 6175 CustomSerializableObject::Behavior::FailDuringRead); 6176 if (iv < min || iv > max) { 6177 JS_ReportErrorASCII(cx, "behavior out of range"); 6178 return false; 6179 } 6180 behavior = static_cast<CustomSerializableObject::Behavior>(iv); 6181 } 6182 6183 JSObject* obj = CustomSerializableObject::Create(cx, id, behavior); 6184 if (!obj) { 6185 return false; 6186 } 6187 6188 args.rval().setObject(*obj); 6189 return true; 6190 } 6191 6192 static JSStructuredCloneCallbacks gCloneCallbacks = { 6193 .read = CustomSerializableObject::Read, 6194 .write = CustomSerializableObject::Write, 6195 .reportError = nullptr, 6196 .readTransfer = CustomSerializableObject::ReadTransfer, 6197 .writeTransfer = CustomSerializableObject::WriteTransfer, 6198 .freeTransfer = CustomSerializableObject::FreeTransfer, 6199 .canTransfer = CustomSerializableObject::CanTransfer, 6200 .sabCloned = nullptr}; 6201 6202 bool js::testingFunc_serialize(JSContext* cx, unsigned argc, Value* vp) { 6203 CallArgs args = CallArgsFromVp(argc, vp); 6204 6205 if (js::SupportDifferentialTesting()) { 6206 RootedObject callee(cx, &args.callee()); 6207 ReportUsageErrorASCII(cx, callee, 6208 "Function unavailable in differential testing mode."); 6209 return false; 6210 } 6211 6212 mozilla::Maybe<JSAutoStructuredCloneBuffer> clonebuf; 6213 JS::CloneDataPolicy policy; 6214 6215 if (!args.get(2).isUndefined()) { 6216 RootedObject opts(cx, ToObject(cx, args.get(2))); 6217 if (!opts) { 6218 return false; 6219 } 6220 6221 RootedValue v(cx); 6222 if (!JS_GetProperty(cx, opts, "SharedArrayBuffer", &v)) { 6223 return false; 6224 } 6225 6226 if (!v.isUndefined()) { 6227 JSString* str = JS::ToString(cx, v); 6228 if (!str) { 6229 return false; 6230 } 6231 JSLinearString* poli = str->ensureLinear(cx); 6232 if (!poli) { 6233 return false; 6234 } 6235 6236 if (StringEqualsLiteral(poli, "allow")) { 6237 policy.allowSharedMemoryObjects(); 6238 policy.allowIntraClusterClonableSharedObjects(); 6239 } else if (StringEqualsLiteral(poli, "deny")) { 6240 // default 6241 } else { 6242 JS_ReportErrorASCII(cx, "Invalid policy value for 'SharedArrayBuffer'"); 6243 return false; 6244 } 6245 } 6246 6247 if (!JS_GetProperty(cx, opts, "scope", &v)) { 6248 return false; 6249 } 6250 6251 if (!v.isUndefined()) { 6252 RootedString str(cx, JS::ToString(cx, v)); 6253 if (!str) { 6254 return false; 6255 } 6256 auto scope = ParseCloneScope(cx, str); 6257 if (!scope) { 6258 JS_ReportErrorASCII(cx, "Invalid structured clone scope"); 6259 return false; 6260 } 6261 clonebuf.emplace(*scope, &gCloneCallbacks, nullptr); 6262 } 6263 } 6264 6265 if (!clonebuf) { 6266 clonebuf.emplace(JS::StructuredCloneScope::SameProcess, &gCloneCallbacks, 6267 nullptr); 6268 } 6269 6270 if (!clonebuf->write(cx, args.get(0), args.get(1), policy)) { 6271 return false; 6272 } 6273 6274 RootedObject obj(cx, CloneBufferObject::Create(cx, clonebuf.ptr())); 6275 if (!obj) { 6276 return false; 6277 } 6278 6279 args.rval().setObject(*obj); 6280 return true; 6281 } 6282 6283 static bool Deserialize(JSContext* cx, unsigned argc, Value* vp) { 6284 CallArgs args = CallArgsFromVp(argc, vp); 6285 6286 if (js::SupportDifferentialTesting()) { 6287 RootedObject callee(cx, &args.callee()); 6288 ReportUsageErrorASCII(cx, callee, 6289 "Function unavailable in differential testing mode."); 6290 return false; 6291 } 6292 6293 if (!args.get(0).isObject() || !args[0].toObject().is<CloneBufferObject>()) { 6294 JS_ReportErrorASCII(cx, "deserialize requires a clonebuffer argument"); 6295 return false; 6296 } 6297 Rooted<CloneBufferObject*> obj(cx, 6298 &args[0].toObject().as<CloneBufferObject>()); 6299 6300 JS::CloneDataPolicy policy; 6301 6302 JS::StructuredCloneScope scope = 6303 obj->isSynthetic() ? JS::StructuredCloneScope::DifferentProcess 6304 : JS::StructuredCloneScope::SameProcess; 6305 if (args.get(1).isObject()) { 6306 RootedObject opts(cx, &args[1].toObject()); 6307 if (!opts) { 6308 return false; 6309 } 6310 6311 RootedValue v(cx); 6312 if (!JS_GetProperty(cx, opts, "SharedArrayBuffer", &v)) { 6313 return false; 6314 } 6315 6316 if (!v.isUndefined()) { 6317 JSString* str = JS::ToString(cx, v); 6318 if (!str) { 6319 return false; 6320 } 6321 JSLinearString* poli = str->ensureLinear(cx); 6322 if (!poli) { 6323 return false; 6324 } 6325 6326 if (StringEqualsLiteral(poli, "allow")) { 6327 policy.allowSharedMemoryObjects(); 6328 policy.allowIntraClusterClonableSharedObjects(); 6329 } else if (StringEqualsLiteral(poli, "deny")) { 6330 // default 6331 } else { 6332 JS_ReportErrorASCII(cx, "Invalid policy value for 'SharedArrayBuffer'"); 6333 return false; 6334 } 6335 } 6336 6337 if (!JS_GetProperty(cx, opts, "scope", &v)) { 6338 return false; 6339 } 6340 6341 if (!v.isUndefined()) { 6342 RootedString str(cx, JS::ToString(cx, v)); 6343 if (!str) { 6344 return false; 6345 } 6346 auto maybeScope = ParseCloneScope(cx, str); 6347 if (!maybeScope) { 6348 JS_ReportErrorASCII(cx, "Invalid structured clone scope"); 6349 return false; 6350 } 6351 6352 if (*maybeScope < scope) { 6353 JS_ReportErrorASCII(cx, 6354 "Cannot use less restrictive scope " 6355 "than the deserialized clone buffer's scope"); 6356 return false; 6357 } 6358 6359 scope = *maybeScope; 6360 } 6361 } 6362 6363 // Clone buffer was already consumed? 6364 if (!obj->data()) { 6365 JS_ReportErrorASCII(cx, 6366 "deserialize given invalid clone buffer " 6367 "(transferables already consumed?)"); 6368 return false; 6369 } 6370 6371 bool hasTransferable; 6372 if (!JS_StructuredCloneHasTransferables(*obj->data(), &hasTransferable)) { 6373 return false; 6374 } 6375 6376 RootedValue deserialized(cx); 6377 if (!JS_ReadStructuredClone(cx, *obj->data(), JS_STRUCTURED_CLONE_VERSION, 6378 scope, &deserialized, policy, &gCloneCallbacks, 6379 nullptr)) { 6380 return false; 6381 } 6382 args.rval().set(deserialized); 6383 6384 // Consume any clone buffer with transferables; throw an error if it is 6385 // deserialized again. 6386 if (hasTransferable) { 6387 obj->discard(); 6388 } 6389 6390 return true; 6391 } 6392 6393 static bool DetachArrayBuffer(JSContext* cx, unsigned argc, Value* vp) { 6394 CallArgs args = CallArgsFromVp(argc, vp); 6395 6396 if (args.length() != 1) { 6397 JS_ReportErrorASCII(cx, "detachArrayBuffer() requires a single argument"); 6398 return false; 6399 } 6400 6401 if (!args[0].isObject()) { 6402 JS_ReportErrorASCII(cx, "detachArrayBuffer must be passed an object"); 6403 return false; 6404 } 6405 6406 RootedObject obj(cx, &args[0].toObject()); 6407 if (!JS::DetachArrayBuffer(cx, obj)) { 6408 return false; 6409 } 6410 6411 args.rval().setUndefined(); 6412 return true; 6413 } 6414 6415 static bool EnsureNonInline(JSContext* cx, unsigned argc, Value* vp) { 6416 CallArgs args = CallArgsFromVp(argc, vp); 6417 Rooted<JSObject*> callee(cx, &args.callee()); 6418 6419 if (!args.get(0).isObject()) { 6420 js::ReportUsageErrorASCII(cx, callee, "Single object argument required"); 6421 return false; 6422 } 6423 6424 RootedObject obj(cx, &args[0].toObject()); 6425 if (!JS::EnsureNonInlineArrayBufferOrView(cx, obj)) { 6426 return false; 6427 } 6428 6429 args.rval().setUndefined(); 6430 return true; 6431 } 6432 6433 static bool PinArrayBufferOrViewLength(JSContext* cx, unsigned argc, 6434 Value* vp) { 6435 CallArgs args = CallArgsFromVp(argc, vp); 6436 Rooted<JSObject*> callee(cx, &args.callee()); 6437 6438 if (!args.get(0).isObject()) { 6439 js::ReportUsageErrorASCII( 6440 cx, callee, "ArrayBuffer or ArrayBufferView argument required"); 6441 return false; 6442 } 6443 RootedObject obj(cx, &args[0].toObject()); 6444 if (!obj->canUnwrapAs<ArrayBufferViewObject>() && 6445 !obj->canUnwrapAs<ArrayBufferObjectMaybeShared>()) { 6446 js::ReportUsageErrorASCII( 6447 cx, callee, "ArrayBuffer or ArrayBufferView argument required"); 6448 return false; 6449 } 6450 6451 bool pin = args.get(1).isUndefined() ? true : ToBoolean(args.get(1)); 6452 6453 args.rval().setBoolean(JS::PinArrayBufferOrViewLength(obj, pin)); 6454 return true; 6455 } 6456 6457 static bool JSONStringify(JSContext* cx, unsigned argc, Value* vp) { 6458 CallArgs args = CallArgsFromVp(argc, vp); 6459 6460 RootedValue value(cx, args.get(0)); 6461 RootedValue behaviorVal(cx, args.get(1)); 6462 StringifyBehavior behavior = StringifyBehavior::Normal; 6463 if (behaviorVal.isString()) { 6464 bool matches; 6465 #define MATCH(name) \ 6466 if (!JS_StringEqualsLiteral(cx, behaviorVal.toString(), #name, &matches)) { \ 6467 return false; \ 6468 } \ 6469 if (matches) { \ 6470 behavior = StringifyBehavior::name; \ 6471 } 6472 MATCH(Normal) 6473 MATCH(FastOnly) 6474 MATCH(SlowOnly) 6475 MATCH(Compare) 6476 #undef MATCH 6477 } 6478 6479 JSStringBuilder sb(cx); 6480 if (!Stringify(cx, &value, nullptr, UndefinedValue(), sb, behavior)) { 6481 return false; 6482 } 6483 6484 if (!sb.empty()) { 6485 JSString* str = sb.finishString(); 6486 if (!str) { 6487 return false; 6488 } 6489 args.rval().setString(str); 6490 } else { 6491 args.rval().setUndefined(); 6492 } 6493 6494 return true; 6495 } 6496 6497 static bool HelperThreadCount(JSContext* cx, unsigned argc, Value* vp) { 6498 CallArgs args = CallArgsFromVp(argc, vp); 6499 6500 if (js::SupportDifferentialTesting()) { 6501 // Always return 0 to get consistent output with and without --no-threads. 6502 args.rval().setInt32(0); 6503 return true; 6504 } 6505 6506 if (CanUseExtraThreads()) { 6507 args.rval().setInt32(GetHelperThreadCount()); 6508 } else { 6509 args.rval().setInt32(0); 6510 } 6511 return true; 6512 } 6513 6514 static bool EnableShapeConsistencyChecks(JSContext* cx, unsigned argc, 6515 Value* vp) { 6516 CallArgs args = CallArgsFromVp(argc, vp); 6517 #ifdef DEBUG 6518 NativeObject::enableShapeConsistencyChecks(); 6519 #endif 6520 args.rval().setUndefined(); 6521 return true; 6522 } 6523 6524 // ShapeSnapshot holds information about an object's properties. This is used 6525 // for checking object and shape changes between two points in time. 6526 class ShapeSnapshot { 6527 HeapPtr<JSObject*> object_; 6528 HeapPtr<Shape*> shape_; 6529 HeapPtr<BaseShape*> baseShape_; 6530 ObjectFlags objectFlags_; 6531 6532 GCVector<HeapPtr<Value>, 8> slots_; 6533 6534 struct PropertySnapshot { 6535 HeapPtr<PropMap*> propMap; 6536 uint32_t propMapIndex; 6537 HeapPtr<PropertyKey> key; 6538 PropertyInfo prop; 6539 6540 explicit PropertySnapshot(PropMap* map, uint32_t index) 6541 : propMap(map), 6542 propMapIndex(index), 6543 key(map->getKey(index)), 6544 prop(map->getPropertyInfo(index)) {} 6545 6546 void trace(JSTracer* trc) { 6547 TraceEdge(trc, &propMap, "propMap"); 6548 TraceEdge(trc, &key, "key"); 6549 } 6550 6551 bool operator==(const PropertySnapshot& other) const { 6552 return propMap == other.propMap && propMapIndex == other.propMapIndex && 6553 key == other.key && prop == other.prop; 6554 } 6555 bool operator!=(const PropertySnapshot& other) const { 6556 return !operator==(other); 6557 } 6558 }; 6559 GCVector<PropertySnapshot, 8> properties_; 6560 6561 public: 6562 explicit ShapeSnapshot(JSContext* cx) : slots_(cx), properties_(cx) {} 6563 void checkSelf(JSContext* cx) const; 6564 void check(JSContext* cx, const ShapeSnapshot& other) const; 6565 bool init(JSObject* obj); 6566 void trace(JSTracer* trc); 6567 6568 JSObject* object() const { return object_; } 6569 }; 6570 6571 // A JSObject that holds a ShapeSnapshot. 6572 class ShapeSnapshotObject : public NativeObject { 6573 static constexpr size_t SnapshotSlot = 0; 6574 static constexpr size_t ReservedSlots = 1; 6575 6576 public: 6577 static const JSClassOps classOps_; 6578 static const JSClass class_; 6579 6580 bool hasSnapshot() const { 6581 // The snapshot may not be present yet if we GC during initialization. 6582 return !getReservedSlot(SnapshotSlot).isUndefined(); 6583 } 6584 6585 ShapeSnapshot& snapshot() const { 6586 void* ptr = getReservedSlot(SnapshotSlot).toPrivate(); 6587 MOZ_ASSERT(ptr); 6588 return *static_cast<ShapeSnapshot*>(ptr); 6589 } 6590 6591 static ShapeSnapshotObject* create(JSContext* cx, HandleObject obj); 6592 6593 static void finalize(JS::GCContext* gcx, JSObject* obj) { 6594 if (obj->as<ShapeSnapshotObject>().hasSnapshot()) { 6595 js_delete(&obj->as<ShapeSnapshotObject>().snapshot()); 6596 } 6597 } 6598 static void trace(JSTracer* trc, JSObject* obj) { 6599 if (obj->as<ShapeSnapshotObject>().hasSnapshot()) { 6600 obj->as<ShapeSnapshotObject>().snapshot().trace(trc); 6601 } 6602 } 6603 }; 6604 6605 /*static */ const JSClassOps ShapeSnapshotObject::classOps_ = { 6606 nullptr, // addProperty 6607 nullptr, // delProperty 6608 nullptr, // enumerate 6609 nullptr, // newEnumerate 6610 nullptr, // resolve 6611 nullptr, // mayResolve 6612 ShapeSnapshotObject::finalize, // finalize 6613 nullptr, // call 6614 nullptr, // construct 6615 ShapeSnapshotObject::trace, // trace 6616 }; 6617 6618 /*static */ const JSClass ShapeSnapshotObject::class_ = { 6619 "ShapeSnapshotObject", 6620 JSCLASS_HAS_RESERVED_SLOTS(ShapeSnapshotObject::ReservedSlots) | 6621 JSCLASS_BACKGROUND_FINALIZE, 6622 &ShapeSnapshotObject::classOps_, 6623 }; 6624 6625 bool ShapeSnapshot::init(JSObject* obj) { 6626 object_ = obj; 6627 shape_ = obj->shape(); 6628 baseShape_ = shape_->base(); 6629 objectFlags_ = shape_->objectFlags(); 6630 6631 if (obj->is<NativeObject>()) { 6632 NativeObject* nobj = &obj->as<NativeObject>(); 6633 6634 // Snapshot the slot values. 6635 size_t slotSpan = nobj->slotSpan(); 6636 if (!slots_.growBy(slotSpan)) { 6637 return false; 6638 } 6639 for (size_t i = 0; i < slotSpan; i++) { 6640 slots_[i] = nobj->getSlot(i); 6641 } 6642 6643 // Snapshot property information. 6644 if (uint32_t len = nobj->shape()->propMapLength(); len > 0) { 6645 PropMap* map = nobj->shape()->propMap(); 6646 while (true) { 6647 for (uint32_t i = 0; i < len; i++) { 6648 if (!map->hasKey(i)) { 6649 continue; 6650 } 6651 if (!properties_.append(PropertySnapshot(map, i))) { 6652 return false; 6653 } 6654 } 6655 if (!map->hasPrevious()) { 6656 break; 6657 } 6658 map = map->asLinked()->previous(); 6659 len = PropMap::Capacity; 6660 } 6661 } 6662 } 6663 6664 return true; 6665 } 6666 6667 void ShapeSnapshot::trace(JSTracer* trc) { 6668 TraceEdge(trc, &object_, "object"); 6669 TraceEdge(trc, &shape_, "shape"); 6670 TraceEdge(trc, &baseShape_, "baseShape"); 6671 slots_.trace(trc); 6672 properties_.trace(trc); 6673 } 6674 6675 void ShapeSnapshot::checkSelf(JSContext* cx) const { 6676 // Assertions based on a single snapshot. 6677 6678 // Non-dictionary shapes must not be mutated. 6679 if (!shape_->isDictionary()) { 6680 MOZ_RELEASE_ASSERT(shape_->base() == baseShape_); 6681 MOZ_RELEASE_ASSERT(shape_->objectFlags() == objectFlags_); 6682 } 6683 6684 for (const PropertySnapshot& propSnapshot : properties_) { 6685 PropMap* propMap = propSnapshot.propMap; 6686 uint32_t propMapIndex = propSnapshot.propMapIndex; 6687 PropertyInfo prop = propSnapshot.prop; 6688 6689 // Skip if the map no longer matches the snapshotted data. This can 6690 // only happen for dictionary maps because they can be mutated or compacted 6691 // after a shape change. 6692 if (!propMap->hasKey(propMapIndex) || 6693 PropertySnapshot(propMap, propMapIndex) != propSnapshot) { 6694 MOZ_RELEASE_ASSERT(propMap->isDictionary()); 6695 MOZ_RELEASE_ASSERT(object_->shape() != shape_); 6696 continue; 6697 } 6698 6699 // Ensure ObjectFlags depending on property information are set if needed. 6700 ObjectFlags expectedFlags = GetObjectFlagsForNewProperty( 6701 shape_->getObjectClass(), shape_->objectFlags(), propSnapshot.key, 6702 prop.flags(), cx); 6703 MOZ_RELEASE_ASSERT(expectedFlags == objectFlags_); 6704 6705 // Accessors must have a PrivateGCThingValue(GetterSetter*) slot value. 6706 if (prop.isAccessorProperty()) { 6707 Value slotVal = slots_[prop.slot()]; 6708 MOZ_RELEASE_ASSERT(slotVal.isPrivateGCThing()); 6709 MOZ_RELEASE_ASSERT(slotVal.toGCThing()->is<GetterSetter>()); 6710 } 6711 6712 // Data properties must not have a PrivateGCThingValue slot value. 6713 if (prop.isDataProperty()) { 6714 Value slotVal = slots_[prop.slot()]; 6715 MOZ_RELEASE_ASSERT(!slotVal.isPrivateGCThing()); 6716 } 6717 } 6718 } 6719 6720 void ShapeSnapshot::check(JSContext* cx, const ShapeSnapshot& later) const { 6721 checkSelf(cx); 6722 later.checkSelf(cx); 6723 6724 if (object_ != later.object_) { 6725 // Snapshots are for different objects. Assert dictionary shapes aren't 6726 // shared. 6727 if (object_->is<NativeObject>()) { 6728 NativeObject* nobj = &object_->as<NativeObject>(); 6729 if (nobj->inDictionaryMode()) { 6730 MOZ_RELEASE_ASSERT(nobj->shape() != later.shape_); 6731 } 6732 } 6733 return; 6734 } 6735 6736 // We have two snapshots for the same object. Check the shape information 6737 // wasn't changed in invalid ways. 6738 6739 // If the Shape is still the same, the object must have the same BaseShape, 6740 // ObjectFlags and property information. 6741 if (shape_ == later.shape_) { 6742 MOZ_RELEASE_ASSERT(objectFlags_ == later.objectFlags_); 6743 MOZ_RELEASE_ASSERT(baseShape_ == later.baseShape_); 6744 MOZ_RELEASE_ASSERT(slots_.length() == later.slots_.length()); 6745 MOZ_RELEASE_ASSERT(properties_.length() == later.properties_.length()); 6746 6747 for (size_t i = 0; i < properties_.length(); i++) { 6748 MOZ_RELEASE_ASSERT(properties_[i] == later.properties_[i]); 6749 // Non-configurable accessor properties and non-configurable, non-writable 6750 // data properties shouldn't have had their slot mutated. 6751 PropertyInfo prop = properties_[i].prop; 6752 if (!prop.configurable()) { 6753 if (prop.isAccessorProperty() || 6754 (prop.isDataProperty() && !prop.writable())) { 6755 size_t slot = prop.slot(); 6756 MOZ_RELEASE_ASSERT(slots_[slot] == later.slots_[slot]); 6757 } 6758 } 6759 } 6760 } 6761 6762 // Object flags should not be lost. The exception is the Indexed flag, it 6763 // can be cleared when densifying elements, so clear that flag first. 6764 { 6765 ObjectFlags flags = objectFlags_; 6766 ObjectFlags flagsLater = later.objectFlags_; 6767 flags.clearFlag(ObjectFlag::Indexed); 6768 flagsLater.clearFlag(ObjectFlag::Indexed); 6769 MOZ_RELEASE_ASSERT((flags.toRaw() & flagsLater.toRaw()) == flags.toRaw()); 6770 } 6771 6772 // If the HadGetterSetterChange flag wasn't set, all GetterSetter slots must 6773 // be unchanged. 6774 if (!later.objectFlags_.hasFlag(ObjectFlag::HadGetterSetterChange)) { 6775 for (size_t i = 0; i < slots_.length(); i++) { 6776 if (slots_[i].isPrivateGCThing() && 6777 slots_[i].toGCThing()->is<GetterSetter>()) { 6778 MOZ_RELEASE_ASSERT(i < later.slots_.length()); 6779 MOZ_RELEASE_ASSERT(later.slots_[i] == slots_[i]); 6780 } 6781 } 6782 } 6783 } 6784 6785 // static 6786 ShapeSnapshotObject* ShapeSnapshotObject::create(JSContext* cx, 6787 HandleObject obj) { 6788 Rooted<UniquePtr<ShapeSnapshot>> snapshot(cx, 6789 cx->make_unique<ShapeSnapshot>(cx)); 6790 if (!snapshot || !snapshot->init(obj)) { 6791 return nullptr; 6792 } 6793 6794 auto* snapshotObj = NewObjectWithGivenProto<ShapeSnapshotObject>(cx, nullptr); 6795 if (!snapshotObj) { 6796 return nullptr; 6797 } 6798 snapshotObj->initReservedSlot(SnapshotSlot, PrivateValue(snapshot.release())); 6799 return snapshotObj; 6800 } 6801 6802 static bool CreateShapeSnapshot(JSContext* cx, unsigned argc, Value* vp) { 6803 CallArgs args = CallArgsFromVp(argc, vp); 6804 6805 if (!args.get(0).isObject()) { 6806 JS_ReportErrorASCII(cx, "createShapeSnapshot requires an object argument"); 6807 return false; 6808 } 6809 6810 RootedObject obj(cx, &args[0].toObject()); 6811 auto* res = ShapeSnapshotObject::create(cx, obj); 6812 if (!res) { 6813 return false; 6814 } 6815 6816 res->snapshot().check(cx, res->snapshot()); 6817 6818 args.rval().setObject(*res); 6819 return true; 6820 } 6821 6822 static bool CheckShapeSnapshot(JSContext* cx, unsigned argc, Value* vp) { 6823 CallArgs args = CallArgsFromVp(argc, vp); 6824 6825 if (!args.get(0).isObject() || 6826 !args[0].toObject().is<ShapeSnapshotObject>()) { 6827 JS_ReportErrorASCII(cx, "checkShapeSnapshot requires a snapshot argument"); 6828 return false; 6829 } 6830 6831 // Get the object to use from the snapshot if the second argument is not an 6832 // object. 6833 RootedObject obj(cx); 6834 if (args.get(1).isObject()) { 6835 obj = &args[1].toObject(); 6836 } else { 6837 auto& snapshot = args[0].toObject().as<ShapeSnapshotObject>().snapshot(); 6838 obj = snapshot.object(); 6839 } 6840 6841 RootedObject otherSnapshot(cx, ShapeSnapshotObject::create(cx, obj)); 6842 if (!otherSnapshot) { 6843 return false; 6844 } 6845 6846 auto& snapshot1 = args[0].toObject().as<ShapeSnapshotObject>().snapshot(); 6847 auto& snapshot2 = otherSnapshot->as<ShapeSnapshotObject>().snapshot(); 6848 snapshot1.check(cx, snapshot2); 6849 6850 args.rval().setUndefined(); 6851 return true; 6852 } 6853 6854 #if defined(DEBUG) || defined(JS_JITSPEW) 6855 static bool DumpObject(JSContext* cx, unsigned argc, Value* vp) { 6856 CallArgs args = CallArgsFromVp(argc, vp); 6857 RootedObject obj(cx, ToObject(cx, args.get(0))); 6858 if (!obj) { 6859 return false; 6860 } 6861 6862 DumpObject(obj); 6863 6864 args.rval().setUndefined(); 6865 return true; 6866 } 6867 6868 static bool DumpValue(JSContext* cx, unsigned argc, Value* vp) { 6869 CallArgs args = CallArgsFromVp(argc, vp); 6870 args.get(0).get().dump(); 6871 args.rval().setUndefined(); 6872 return true; 6873 } 6874 6875 static bool DumpValueToString(JSContext* cx, unsigned argc, Value* vp) { 6876 CallArgs args = CallArgsFromVp(argc, vp); 6877 6878 JSSprinter out(cx); 6879 if (!out.init()) { 6880 return false; 6881 } 6882 args.get(0).get().dump(out); 6883 6884 JSString* rep = out.release(cx); 6885 if (!rep) { 6886 return false; 6887 } 6888 6889 args.rval().setString(rep); 6890 return true; 6891 } 6892 #endif 6893 6894 static bool SharedMemoryEnabled(JSContext* cx, unsigned argc, Value* vp) { 6895 CallArgs args = CallArgsFromVp(argc, vp); 6896 args.rval().setBoolean( 6897 cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled()); 6898 return true; 6899 } 6900 6901 static bool SharedArrayRawBufferRefcount(JSContext* cx, unsigned argc, 6902 Value* vp) { 6903 CallArgs args = CallArgsFromVp(argc, vp); 6904 if (args.length() != 1 || !args[0].isObject()) { 6905 JS_ReportErrorASCII(cx, "Expected SharedArrayBuffer object"); 6906 return false; 6907 } 6908 RootedObject obj(cx, &args[0].toObject()); 6909 if (!obj->is<SharedArrayBufferObject>()) { 6910 JS_ReportErrorASCII(cx, "Expected SharedArrayBuffer object"); 6911 return false; 6912 } 6913 args.rval().setInt32( 6914 obj->as<SharedArrayBufferObject>().rawBufferObject()->refcount()); 6915 return true; 6916 } 6917 6918 #ifdef NIGHTLY_BUILD 6919 static bool ObjectAddress(JSContext* cx, unsigned argc, Value* vp) { 6920 CallArgs args = CallArgsFromVp(argc, vp); 6921 6922 if (js::SupportDifferentialTesting()) { 6923 RootedObject callee(cx, &args.callee()); 6924 ReportUsageErrorASCII(cx, callee, 6925 "Function unavailable in differential testing mode."); 6926 return false; 6927 } 6928 6929 if (args.length() != 1) { 6930 RootedObject callee(cx, &args.callee()); 6931 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 6932 return false; 6933 } 6934 if (!args[0].isObject()) { 6935 RootedObject callee(cx, &args.callee()); 6936 ReportUsageErrorASCII(cx, callee, "Expected object"); 6937 return false; 6938 } 6939 6940 void* ptr = js::UncheckedUnwrap(&args[0].toObject(), true); 6941 char buffer[64]; 6942 SprintfLiteral(buffer, "%p", ptr); 6943 6944 return ReturnStringCopy(cx, args, buffer); 6945 } 6946 6947 static bool ScriptAddressForFunction(JSContext* cx, unsigned argc, Value* vp) { 6948 CallArgs args = CallArgsFromVp(argc, vp); 6949 6950 if (js::SupportDifferentialTesting()) { 6951 RootedObject callee(cx, &args.callee()); 6952 ReportUsageErrorASCII(cx, callee, 6953 "Function unavailable in differential testing mode."); 6954 return false; 6955 } 6956 6957 if (args.length() != 1) { 6958 RootedObject callee(cx, &args.callee()); 6959 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 6960 return false; 6961 } 6962 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) { 6963 RootedObject callee(cx, &args.callee()); 6964 ReportUsageErrorASCII(cx, callee, "Expected function"); 6965 return false; 6966 } 6967 6968 RootedFunction function(cx, &args[0].toObject().as<JSFunction>()); 6969 if (!function->hasBytecode()) { 6970 RootedObject callee(cx, &args.callee()); 6971 ReportUsageErrorASCII(cx, callee, "Expected non-lazy scripted function"); 6972 return false; 6973 } 6974 6975 void* ptr = function->nonLazyScript(); 6976 args.rval().setPrivate(ptr); 6977 6978 return true; 6979 } 6980 6981 static bool SharedAddress(JSContext* cx, unsigned argc, Value* vp) { 6982 CallArgs args = CallArgsFromVp(argc, vp); 6983 6984 if (js::SupportDifferentialTesting()) { 6985 RootedObject callee(cx, &args.callee()); 6986 ReportUsageErrorASCII(cx, callee, 6987 "Function unavailable in differential testing mode."); 6988 return false; 6989 } 6990 6991 if (args.length() != 1) { 6992 RootedObject callee(cx, &args.callee()); 6993 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 6994 return false; 6995 } 6996 if (!args[0].isObject()) { 6997 RootedObject callee(cx, &args.callee()); 6998 ReportUsageErrorASCII(cx, callee, "Expected object"); 6999 return false; 7000 } 7001 7002 RootedObject obj(cx, CheckedUnwrapStatic(&args[0].toObject())); 7003 if (!obj) { 7004 ReportAccessDenied(cx); 7005 return false; 7006 } 7007 if (!obj->is<SharedArrayBufferObject>()) { 7008 JS_ReportErrorASCII(cx, "Argument must be a SharedArrayBuffer"); 7009 return false; 7010 } 7011 char buffer[64]; 7012 uint32_t nchar = SprintfLiteral( 7013 buffer, "%p", 7014 obj->as<SharedArrayBufferObject>().dataPointerShared().unwrap( 7015 /*safeish*/)); 7016 7017 JSString* str = JS_NewStringCopyN(cx, buffer, nchar); 7018 if (!str) { 7019 return false; 7020 } 7021 7022 args.rval().setString(str); 7023 7024 return true; 7025 } 7026 #endif 7027 7028 static bool HasInvalidatedTeleporting(JSContext* cx, unsigned argc, Value* vp) { 7029 CallArgs args = CallArgsFromVp(argc, vp); 7030 7031 if (args.length() != 1 || !args[0].isObject()) { 7032 RootedObject callee(cx, &args.callee()); 7033 ReportUsageErrorASCII(cx, callee, "Expected single object argument"); 7034 return false; 7035 } 7036 7037 args.rval().setBoolean(args[0].toObject().hasInvalidatedTeleporting()); 7038 return true; 7039 } 7040 7041 static bool DumpBacktrace(JSContext* cx, unsigned argc, Value* vp) { 7042 CallArgs args = CallArgsFromVp(argc, vp); 7043 DumpBacktrace(cx); 7044 args.rval().setUndefined(); 7045 return true; 7046 } 7047 7048 static bool GetBacktrace(JSContext* cx, unsigned argc, Value* vp) { 7049 CallArgs args = CallArgsFromVp(argc, vp); 7050 7051 bool showArgs = false; 7052 bool showLocals = false; 7053 bool showThisProps = false; 7054 7055 if (args.length() > 1) { 7056 RootedObject callee(cx, &args.callee()); 7057 ReportUsageErrorASCII(cx, callee, "Too many arguments"); 7058 return false; 7059 } 7060 7061 if (args.length() == 1) { 7062 RootedObject cfg(cx, ToObject(cx, args[0])); 7063 if (!cfg) { 7064 return false; 7065 } 7066 RootedValue v(cx); 7067 7068 if (!JS_GetProperty(cx, cfg, "args", &v)) { 7069 return false; 7070 } 7071 showArgs = ToBoolean(v); 7072 7073 if (!JS_GetProperty(cx, cfg, "locals", &v)) { 7074 return false; 7075 } 7076 showLocals = ToBoolean(v); 7077 7078 if (!JS_GetProperty(cx, cfg, "thisprops", &v)) { 7079 return false; 7080 } 7081 showThisProps = ToBoolean(v); 7082 } 7083 7084 JS::UniqueChars buf = 7085 JS::FormatStackDump(cx, showArgs, showLocals, showThisProps); 7086 if (!buf) { 7087 return false; 7088 } 7089 7090 size_t len; 7091 UniqueTwoByteChars ucbuf(JS::LossyUTF8CharsToNewTwoByteCharsZ( 7092 cx, JS::UTF8Chars(buf.get(), strlen(buf.get())), 7093 &len, js::MallocArena) 7094 .get()); 7095 if (!ucbuf) { 7096 return false; 7097 } 7098 JSString* str = JS_NewUCStringCopyN(cx, ucbuf.get(), len); 7099 if (!str) { 7100 return false; 7101 } 7102 7103 args.rval().setString(str); 7104 return true; 7105 } 7106 7107 static bool ReportOutOfMemory(JSContext* cx, unsigned argc, Value* vp) { 7108 CallArgs args = CallArgsFromVp(argc, vp); 7109 JS_ReportOutOfMemory(cx); 7110 cx->clearPendingException(); 7111 args.rval().setUndefined(); 7112 return true; 7113 } 7114 7115 static bool ThrowOutOfMemory(JSContext* cx, unsigned argc, Value* vp) { 7116 JS_ReportOutOfMemory(cx); 7117 return false; 7118 } 7119 7120 static bool ReportLargeAllocationFailure(JSContext* cx, unsigned argc, 7121 Value* vp) { 7122 CallArgs args = CallArgsFromVp(argc, vp); 7123 7124 size_t bytes = JSRuntime::LARGE_ALLOCATION; 7125 if (args.length() >= 1) { 7126 if (!args[0].isInt32()) { 7127 RootedObject callee(cx, &args.callee()); 7128 ReportUsageErrorASCII(cx, callee, 7129 "First argument must be an integer if specified."); 7130 return false; 7131 } 7132 bytes = args[0].toInt32(); 7133 } 7134 7135 void* buf = cx->runtime()->onOutOfMemoryCanGC(AllocFunction::Malloc, 7136 js::MallocArena, bytes); 7137 7138 js_free(buf); 7139 args.rval().setUndefined(); 7140 return true; 7141 } 7142 7143 namespace heaptools { 7144 7145 using EdgeName = UniqueTwoByteChars; 7146 7147 // An edge to a node from its predecessor in a path through the graph. 7148 class BackEdge { 7149 // The node from which this edge starts. 7150 JS::ubi::Node predecessor_; 7151 7152 // The name of this edge. 7153 EdgeName name_; 7154 7155 public: 7156 BackEdge() : name_(nullptr) {} 7157 // Construct an initialized back edge, taking ownership of |name|. 7158 BackEdge(JS::ubi::Node predecessor, EdgeName name) 7159 : predecessor_(predecessor), name_(std::move(name)) {} 7160 BackEdge(BackEdge&& rhs) 7161 : predecessor_(rhs.predecessor_), name_(std::move(rhs.name_)) {} 7162 BackEdge& operator=(BackEdge&& rhs) { 7163 MOZ_ASSERT(&rhs != this); 7164 this->~BackEdge(); 7165 new (this) BackEdge(std::move(rhs)); 7166 return *this; 7167 } 7168 7169 EdgeName forgetName() { return std::move(name_); } 7170 JS::ubi::Node predecessor() const { return predecessor_; } 7171 7172 private: 7173 // No copy constructor or copying assignment. 7174 BackEdge(const BackEdge&) = delete; 7175 BackEdge& operator=(const BackEdge&) = delete; 7176 }; 7177 7178 // A path-finding handler class for use with JS::ubi::BreadthFirst. 7179 struct FindPathHandler { 7180 using NodeData = BackEdge; 7181 using Traversal = JS::ubi::BreadthFirst<FindPathHandler>; 7182 7183 FindPathHandler(JSContext* cx, JS::ubi::Node start, JS::ubi::Node target, 7184 MutableHandle<GCVector<Value>> nodes, Vector<EdgeName>& edges) 7185 : cx(cx), 7186 start(start), 7187 target(target), 7188 foundPath(false), 7189 nodes(nodes), 7190 edges(edges) {} 7191 7192 bool operator()(Traversal& traversal, JS::ubi::Node origin, 7193 const JS::ubi::Edge& edge, BackEdge* backEdge, bool first) { 7194 // We take care of each node the first time we visit it, so there's 7195 // nothing to be done on subsequent visits. 7196 if (!first) { 7197 return true; 7198 } 7199 7200 // Record how we reached this node. This is the last edge on a 7201 // shortest path to this node. 7202 EdgeName edgeName = 7203 DuplicateStringToArena(js::StringBufferArena, cx, edge.name.get()); 7204 if (!edgeName) { 7205 return false; 7206 } 7207 *backEdge = BackEdge(origin, std::move(edgeName)); 7208 7209 // Have we reached our final target node? 7210 if (edge.referent == target) { 7211 // Record the path that got us here, which must be a shortest path. 7212 if (!recordPath(traversal, backEdge)) { 7213 return false; 7214 } 7215 foundPath = true; 7216 traversal.stop(); 7217 } 7218 7219 return true; 7220 } 7221 7222 // We've found a path to our target. Walk the backlinks to produce the 7223 // (reversed) path, saving the path in |nodes| and |edges|. |nodes| is 7224 // rooted, so it can hold the path's nodes as we leave the scope of 7225 // the AutoCheckCannotGC. Note that nodes are added to |visited| after we 7226 // return from operator() so we have to pass the target BackEdge* to this 7227 // function. 7228 bool recordPath(Traversal& traversal, BackEdge* targetBackEdge) { 7229 JS::ubi::Node here = target; 7230 7231 do { 7232 BackEdge* backEdge = targetBackEdge; 7233 if (here != target) { 7234 Traversal::NodeMap::Ptr p = traversal.visited.lookup(here); 7235 MOZ_ASSERT(p); 7236 backEdge = &p->value(); 7237 } 7238 JS::ubi::Node predecessor = backEdge->predecessor(); 7239 if (!nodes.append(predecessor.exposeToJS()) || 7240 !edges.append(backEdge->forgetName())) { 7241 return false; 7242 } 7243 here = predecessor; 7244 } while (here != start); 7245 7246 return true; 7247 } 7248 7249 JSContext* cx; 7250 7251 // The node we're starting from. 7252 JS::ubi::Node start; 7253 7254 // The node we're looking for. 7255 JS::ubi::Node target; 7256 7257 // True if we found a path to target, false if we didn't. 7258 bool foundPath; 7259 7260 // The nodes and edges of the path --- should we find one. The path is 7261 // stored in reverse order, because that's how it's easiest for us to 7262 // construct it: 7263 // - edges[i] is the name of the edge from nodes[i] to nodes[i-1]. 7264 // - edges[0] is the name of the edge from nodes[0] to the target. 7265 // - The last node, nodes[n-1], is the start node. 7266 MutableHandle<GCVector<Value>> nodes; 7267 Vector<EdgeName>& edges; 7268 }; 7269 7270 } // namespace heaptools 7271 7272 static bool FindPath(JSContext* cx, unsigned argc, Value* vp) { 7273 CallArgs args = CallArgsFromVp(argc, vp); 7274 if (!args.requireAtLeast(cx, "findPath", 2)) { 7275 return false; 7276 } 7277 7278 // We don't ToString non-objects given as 'start' or 'target', because this 7279 // test is all about object identity, and ToString doesn't preserve that. 7280 // Non-GCThing endpoints don't make much sense. 7281 if (!args[0].isObject() && !args[0].isString() && !args[0].isSymbol()) { 7282 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0], 7283 nullptr, "not an object, string, or symbol"); 7284 return false; 7285 } 7286 7287 if (!args[1].isObject() && !args[1].isString() && !args[1].isSymbol()) { 7288 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0], 7289 nullptr, "not an object, string, or symbol"); 7290 return false; 7291 } 7292 7293 Rooted<GCVector<Value>> nodes(cx, GCVector<Value>(cx)); 7294 Vector<heaptools::EdgeName> edges(cx); 7295 7296 { 7297 // We can't tolerate the GC moving things around while we're searching 7298 // the heap. Check that nothing we do causes a GC. 7299 JS::AutoCheckCannotGC autoCannotGC; 7300 7301 JS::ubi::Node start(args[0]), target(args[1]); 7302 7303 heaptools::FindPathHandler handler(cx, start, target, &nodes, edges); 7304 heaptools::FindPathHandler::Traversal traversal(cx, handler, autoCannotGC); 7305 if (!traversal.addStart(start)) { 7306 ReportOutOfMemory(cx); 7307 return false; 7308 } 7309 7310 if (!traversal.traverse()) { 7311 if (!cx->isExceptionPending()) { 7312 ReportOutOfMemory(cx); 7313 } 7314 return false; 7315 } 7316 7317 if (!handler.foundPath) { 7318 // We didn't find any paths from the start to the target. 7319 args.rval().setUndefined(); 7320 return true; 7321 } 7322 } 7323 7324 // |nodes| and |edges| contain the path from |start| to |target|, reversed. 7325 // Construct a JavaScript array describing the path from the start to the 7326 // target. Each element has the form: 7327 // 7328 // { 7329 // node: <object or string or symbol>, 7330 // edge: <string describing outgoing edge from node> 7331 // } 7332 // 7333 // or, if the node is some internal thing that isn't a proper JavaScript 7334 // value: 7335 // 7336 // { node: undefined, edge: <string> } 7337 size_t length = nodes.length(); 7338 Rooted<ArrayObject*> result(cx, NewDenseFullyAllocatedArray(cx, length)); 7339 if (!result) { 7340 return false; 7341 } 7342 result->ensureDenseInitializedLength(0, length); 7343 7344 // Walk |nodes| and |edges| in the stored order, and construct the result 7345 // array in start-to-target order. 7346 for (size_t i = 0; i < length; i++) { 7347 // Build an object describing the node and edge. 7348 RootedObject obj(cx, NewPlainObject(cx)); 7349 if (!obj) { 7350 return false; 7351 } 7352 7353 // Only define the "node" property if we're not fuzzing, to prevent the 7354 // fuzzers from messing with internal objects that we don't want to expose 7355 // to arbitrary JS. 7356 if (!fuzzingSafe) { 7357 RootedValue wrapped(cx, nodes[i]); 7358 if (!cx->compartment()->wrap(cx, &wrapped)) { 7359 return false; 7360 } 7361 7362 if (!JS_DefineProperty(cx, obj, "node", wrapped, JSPROP_ENUMERATE)) { 7363 return false; 7364 } 7365 } 7366 7367 heaptools::EdgeName edgeName = std::move(edges[i]); 7368 7369 size_t edgeNameLength = js_strlen(edgeName.get()); 7370 RootedString edgeStr( 7371 cx, NewString<CanGC>(cx, std::move(edgeName), edgeNameLength)); 7372 if (!edgeStr) { 7373 return false; 7374 } 7375 7376 if (!JS_DefineProperty(cx, obj, "edge", edgeStr, JSPROP_ENUMERATE)) { 7377 return false; 7378 } 7379 7380 result->setDenseElement(length - i - 1, ObjectValue(*obj)); 7381 } 7382 7383 args.rval().setObject(*result); 7384 return true; 7385 } 7386 7387 static bool ShortestPaths(JSContext* cx, unsigned argc, Value* vp) { 7388 CallArgs args = CallArgsFromVp(argc, vp); 7389 if (!args.requireAtLeast(cx, "shortestPaths", 1)) { 7390 return false; 7391 } 7392 7393 if (!args[0].isObject() || !args[0].toObject().is<ArrayObject>()) { 7394 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0], 7395 nullptr, "not an array object"); 7396 return false; 7397 } 7398 7399 Rooted<ArrayObject*> objs(cx, &args[0].toObject().as<ArrayObject>()); 7400 7401 RootedValue start(cx, NullValue()); 7402 int32_t maxNumPaths = 3; 7403 7404 if (!args.get(1).isUndefined()) { 7405 if (!args[1].isObject()) { 7406 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[1], 7407 nullptr, "not an options object"); 7408 return false; 7409 } 7410 7411 RootedObject options(cx, &args[1].toObject()); 7412 bool exists; 7413 if (!JS_HasProperty(cx, options, "start", &exists)) { 7414 return false; 7415 } 7416 if (exists) { 7417 if (!JS_GetProperty(cx, options, "start", &start)) { 7418 return false; 7419 } 7420 7421 // Non-GCThing endpoints don't make much sense. 7422 if (!start.isGCThing()) { 7423 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, start, 7424 nullptr, "not a GC thing"); 7425 return false; 7426 } 7427 } 7428 7429 RootedValue v(cx, Int32Value(maxNumPaths)); 7430 if (!JS_HasProperty(cx, options, "maxNumPaths", &exists)) { 7431 return false; 7432 } 7433 if (exists) { 7434 if (!JS_GetProperty(cx, options, "maxNumPaths", &v)) { 7435 return false; 7436 } 7437 if (!JS::ToInt32(cx, v, &maxNumPaths)) { 7438 return false; 7439 } 7440 } 7441 if (maxNumPaths <= 0) { 7442 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, v, 7443 nullptr, "not greater than 0"); 7444 return false; 7445 } 7446 } 7447 7448 // Ensure we have at least one target. 7449 size_t length = objs->getDenseInitializedLength(); 7450 if (length == 0) { 7451 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0], 7452 nullptr, 7453 "not a dense array object with one or more elements"); 7454 return false; 7455 } 7456 7457 for (size_t i = 0; i < length; i++) { 7458 RootedValue el(cx, objs->getDenseElement(i)); 7459 if (!el.isGCThing()) { 7460 JS_ReportErrorASCII(cx, "Each target must be a GC thing"); 7461 return false; 7462 } 7463 } 7464 7465 // We accumulate the results into a GC-stable form, due to the fact that the 7466 // JS::ubi::ShortestPaths lifetime (when operating on the live heap graph) 7467 // is bounded within an AutoCheckCannotGC. 7468 Rooted<GCVector<GCVector<GCVector<Value>>>> values( 7469 cx, GCVector<GCVector<GCVector<Value>>>(cx)); 7470 Vector<Vector<Vector<JS::ubi::EdgeName>>> names(cx); 7471 7472 { 7473 JS::ubi::Node root; 7474 7475 JS::ubi::RootList rootList(cx, true); 7476 if (start.isNull()) { 7477 auto [ok, nogc] = rootList.init(); 7478 (void)nogc; // Old compilers get anxious about nogc being unused. 7479 if (!ok) { 7480 ReportOutOfMemory(cx); 7481 return false; 7482 } 7483 root = JS::ubi::Node(&rootList); 7484 } else { 7485 root = JS::ubi::Node(start); 7486 } 7487 JS::AutoCheckCannotGC noGC(cx); 7488 7489 JS::ubi::NodeSet targets; 7490 7491 for (size_t i = 0; i < length; i++) { 7492 RootedValue val(cx, objs->getDenseElement(i)); 7493 JS::ubi::Node node(val); 7494 if (!targets.put(node)) { 7495 ReportOutOfMemory(cx); 7496 return false; 7497 } 7498 } 7499 7500 auto maybeShortestPaths = JS::ubi::ShortestPaths::Create( 7501 cx, noGC, maxNumPaths, root, std::move(targets)); 7502 if (maybeShortestPaths.isNothing()) { 7503 ReportOutOfMemory(cx); 7504 return false; 7505 } 7506 auto& shortestPaths = *maybeShortestPaths; 7507 7508 for (size_t i = 0; i < length; i++) { 7509 if (!values.append(GCVector<GCVector<Value>>(cx)) || 7510 !names.append(Vector<Vector<JS::ubi::EdgeName>>(cx))) { 7511 return false; 7512 } 7513 7514 RootedValue val(cx, objs->getDenseElement(i)); 7515 JS::ubi::Node target(val); 7516 7517 bool ok = shortestPaths.forEachPath(target, [&](JS::ubi::Path& path) { 7518 Rooted<GCVector<Value>> pathVals(cx, GCVector<Value>(cx)); 7519 Vector<JS::ubi::EdgeName> pathNames(cx); 7520 7521 for (auto& part : path) { 7522 if (!pathVals.append(part->predecessor().exposeToJS()) || 7523 !pathNames.append(std::move(part->name()))) { 7524 return false; 7525 } 7526 } 7527 7528 return values.back().append(std::move(pathVals.get())) && 7529 names.back().append(std::move(pathNames)); 7530 }); 7531 7532 if (!ok) { 7533 return false; 7534 } 7535 } 7536 } 7537 7538 MOZ_ASSERT(values.length() == names.length()); 7539 MOZ_ASSERT(values.length() == length); 7540 7541 Rooted<ArrayObject*> results(cx, NewDenseFullyAllocatedArray(cx, length)); 7542 if (!results) { 7543 return false; 7544 } 7545 results->ensureDenseInitializedLength(0, length); 7546 7547 for (size_t i = 0; i < length; i++) { 7548 size_t numPaths = values[i].length(); 7549 MOZ_ASSERT(names[i].length() == numPaths); 7550 7551 Rooted<ArrayObject*> pathsArray(cx, 7552 NewDenseFullyAllocatedArray(cx, numPaths)); 7553 if (!pathsArray) { 7554 return false; 7555 } 7556 pathsArray->ensureDenseInitializedLength(0, numPaths); 7557 7558 for (size_t j = 0; j < numPaths; j++) { 7559 size_t pathLength = values[i][j].length(); 7560 MOZ_ASSERT(names[i][j].length() == pathLength); 7561 7562 Rooted<ArrayObject*> path(cx, 7563 NewDenseFullyAllocatedArray(cx, pathLength)); 7564 if (!path) { 7565 return false; 7566 } 7567 path->ensureDenseInitializedLength(0, pathLength); 7568 7569 for (size_t k = 0; k < pathLength; k++) { 7570 Rooted<PlainObject*> part(cx, NewPlainObject(cx)); 7571 if (!part) { 7572 return false; 7573 } 7574 7575 RootedValue predecessor(cx, values[i][j][k]); 7576 if (!cx->compartment()->wrap(cx, &predecessor) || 7577 !JS_DefineProperty(cx, part, "predecessor", predecessor, 7578 JSPROP_ENUMERATE)) { 7579 return false; 7580 } 7581 7582 if (names[i][j][k]) { 7583 RootedString edge(cx, 7584 NewStringCopyZ<CanGC>(cx, names[i][j][k].get())); 7585 if (!edge || 7586 !JS_DefineProperty(cx, part, "edge", edge, JSPROP_ENUMERATE)) { 7587 return false; 7588 } 7589 } 7590 7591 path->setDenseElement(k, ObjectValue(*part)); 7592 } 7593 7594 pathsArray->setDenseElement(j, ObjectValue(*path)); 7595 } 7596 7597 results->setDenseElement(i, ObjectValue(*pathsArray)); 7598 } 7599 7600 args.rval().setObject(*results); 7601 return true; 7602 } 7603 7604 static bool EvalReturningScope(JSContext* cx, unsigned argc, Value* vp) { 7605 CallArgs args = CallArgsFromVp(argc, vp); 7606 if (!args.requireAtLeast(cx, "evalReturningScope", 1)) { 7607 return false; 7608 } 7609 7610 RootedString str(cx, ToString(cx, args[0])); 7611 if (!str) { 7612 return false; 7613 } 7614 7615 JS::AutoFilename filename; 7616 uint32_t lineno; 7617 7618 JS::DescribeScriptedCaller(&filename, cx, &lineno); 7619 7620 // CompileOption should be created in the target global's realm. 7621 RootedObject global(cx); 7622 Maybe<JS::CompileOptions> maybeOptions; 7623 if (args.hasDefined(1)) { 7624 global = ToObject(cx, args[1]); 7625 if (!global) { 7626 return false; 7627 } 7628 7629 global = CheckedUnwrapDynamic(global, cx, /* stopAtWindowProxy = */ false); 7630 if (!global) { 7631 JS_ReportErrorASCII(cx, "Permission denied to access global"); 7632 return false; 7633 } 7634 if (!global->is<GlobalObject>()) { 7635 JS_ReportErrorASCII(cx, "Argument must be a global object"); 7636 return false; 7637 } 7638 7639 JSAutoRealm ar(cx, global); 7640 maybeOptions.emplace(cx); 7641 } else { 7642 global = JS::CurrentGlobalOrNull(cx); 7643 maybeOptions.emplace(cx); 7644 } 7645 7646 CompileOptions& options = maybeOptions.ref(); 7647 options.setFileAndLine(filename.get(), lineno); 7648 options.setNoScriptRval(true); 7649 options.setNonSyntacticScope(true); 7650 7651 AutoStableStringChars linearChars(cx); 7652 if (!linearChars.initTwoByte(cx, str)) { 7653 return false; 7654 } 7655 JS::SourceText<char16_t> srcBuf; 7656 if (!srcBuf.initMaybeBorrowed(cx, linearChars)) { 7657 return false; 7658 } 7659 7660 RootedObject varObj(cx); 7661 7662 { 7663 // ExecuteInFrameScriptEnvironment requires the script be in the same 7664 // realm as the global. The script compilation should be done after 7665 // switching globals. 7666 AutoRealm ar(cx, global); 7667 7668 RootedScript script(cx, JS::Compile(cx, options, srcBuf)); 7669 if (!script) { 7670 return false; 7671 } 7672 7673 JS::RootedObject obj(cx, JS_NewPlainObject(cx)); 7674 if (!obj) { 7675 return false; 7676 } 7677 7678 RootedObject lexicalScope(cx); 7679 if (!js::ExecuteInFrameScriptEnvironment(cx, obj, script, &lexicalScope)) { 7680 return false; 7681 } 7682 7683 varObj = lexicalScope->enclosingEnvironment()->enclosingEnvironment(); 7684 MOZ_ASSERT(varObj->is<NonSyntacticVariablesObject>()); 7685 } 7686 7687 RootedValue varObjVal(cx, ObjectValue(*varObj)); 7688 if (!cx->compartment()->wrap(cx, &varObjVal)) { 7689 return false; 7690 } 7691 7692 args.rval().set(varObjVal); 7693 return true; 7694 } 7695 7696 static bool ByteSize(JSContext* cx, unsigned argc, Value* vp) { 7697 CallArgs args = CallArgsFromVp(argc, vp); 7698 mozilla::MallocSizeOf mallocSizeOf = cx->runtime()->debuggerMallocSizeOf; 7699 7700 { 7701 // We can't tolerate the GC moving things around while we're using a 7702 // ubi::Node. Check that nothing we do causes a GC. 7703 JS::AutoCheckCannotGC autoCannotGC; 7704 7705 JS::ubi::Node node = args.get(0); 7706 if (node) { 7707 args.rval().setNumber(uint32_t(node.size(mallocSizeOf))); 7708 } else { 7709 args.rval().setUndefined(); 7710 } 7711 } 7712 return true; 7713 } 7714 7715 static bool ByteSizeOfScript(JSContext* cx, unsigned argc, Value* vp) { 7716 CallArgs args = CallArgsFromVp(argc, vp); 7717 if (!args.requireAtLeast(cx, "byteSizeOfScript", 1)) { 7718 return false; 7719 } 7720 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) { 7721 JS_ReportErrorASCII(cx, "Argument must be a Function object"); 7722 return false; 7723 } 7724 7725 RootedFunction fun(cx, &args[0].toObject().as<JSFunction>()); 7726 if (fun->isNativeFun()) { 7727 JS_ReportErrorASCII(cx, "Argument must be a scripted function"); 7728 return false; 7729 } 7730 7731 RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun)); 7732 if (!script) { 7733 return false; 7734 } 7735 7736 mozilla::MallocSizeOf mallocSizeOf = cx->runtime()->debuggerMallocSizeOf; 7737 7738 { 7739 // We can't tolerate the GC moving things around while we're using a 7740 // ubi::Node. Check that nothing we do causes a GC. 7741 JS::AutoCheckCannotGC autoCannotGC; 7742 7743 JS::ubi::Node node = script; 7744 if (node) { 7745 args.rval().setNumber(uint32_t(node.size(mallocSizeOf))); 7746 } else { 7747 args.rval().setUndefined(); 7748 } 7749 } 7750 return true; 7751 } 7752 7753 static bool SetImmutablePrototype(JSContext* cx, unsigned argc, Value* vp) { 7754 CallArgs args = CallArgsFromVp(argc, vp); 7755 if (!args.get(0).isObject()) { 7756 JS_ReportErrorASCII(cx, "setImmutablePrototype: object expected"); 7757 return false; 7758 } 7759 7760 RootedObject obj(cx, &args[0].toObject()); 7761 7762 bool succeeded; 7763 if (!js::SetImmutablePrototype(cx, obj, &succeeded)) { 7764 return false; 7765 } 7766 7767 args.rval().setBoolean(succeeded); 7768 return true; 7769 } 7770 7771 #if defined(DEBUG) || defined(JS_JITSPEW) 7772 static bool DumpStringRepresentation(JSContext* cx, unsigned argc, Value* vp) { 7773 CallArgs args = CallArgsFromVp(argc, vp); 7774 7775 RootedString str(cx, ToString(cx, args.get(0))); 7776 if (!str) { 7777 return false; 7778 } 7779 7780 Fprinter out(stderr); 7781 str->dumpRepresentation(out); 7782 7783 args.rval().setUndefined(); 7784 return true; 7785 } 7786 7787 static bool GetStringRepresentation(JSContext* cx, unsigned argc, Value* vp) { 7788 CallArgs args = CallArgsFromVp(argc, vp); 7789 7790 RootedString str(cx, ToString(cx, args.get(0))); 7791 if (!str) { 7792 return false; 7793 } 7794 7795 JSSprinter out(cx); 7796 if (!out.init()) { 7797 return false; 7798 } 7799 str->dumpRepresentation(out); 7800 7801 JSString* rep = out.release(cx); 7802 if (!rep) { 7803 return false; 7804 } 7805 7806 args.rval().setString(rep); 7807 return true; 7808 } 7809 #endif 7810 7811 static bool ParseCompileOptionsForModule(JSContext* cx, 7812 JS::CompileOptions& options, 7813 JS::Handle<JSObject*> opts, 7814 bool& isModule) { 7815 JS::Rooted<JS::Value> v(cx); 7816 7817 if (!JS_GetProperty(cx, opts, "module", &v)) { 7818 return false; 7819 } 7820 if (!v.isUndefined() && JS::ToBoolean(v)) { 7821 options.setModule(); 7822 isModule = true; 7823 7824 if (!ValidateModuleCompileOptions(cx, options)) { 7825 return false; 7826 } 7827 } else { 7828 isModule = false; 7829 } 7830 7831 return true; 7832 } 7833 7834 static bool ParseCompileOptionsForInstantiate(JSContext* cx, 7835 JS::CompileOptions& options, 7836 JS::Handle<JSObject*> opts, 7837 bool& prepareForInstantiate) { 7838 JS::Rooted<JS::Value> v(cx); 7839 7840 if (!JS_GetProperty(cx, opts, "prepareForInstantiate", &v)) { 7841 return false; 7842 } 7843 if (!v.isUndefined()) { 7844 prepareForInstantiate = JS::ToBoolean(v); 7845 } else { 7846 prepareForInstantiate = false; 7847 } 7848 7849 return true; 7850 } 7851 7852 static bool CompileToStencil(JSContext* cx, uint32_t argc, Value* vp) { 7853 CallArgs args = CallArgsFromVp(argc, vp); 7854 7855 if (!args.requireAtLeast(cx, "compileToStencil", 1)) { 7856 return false; 7857 } 7858 if (!args[0].isString()) { 7859 const char* typeName = InformalValueTypeName(args[0]); 7860 JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName); 7861 return false; 7862 } 7863 7864 RootedString src(cx, ToString<CanGC>(cx, args[0])); 7865 if (!src) { 7866 return false; 7867 } 7868 7869 /* Linearize the string to obtain a char16_t* range. */ 7870 AutoStableStringChars linearChars(cx); 7871 if (!linearChars.initTwoByte(cx, src)) { 7872 return false; 7873 } 7874 JS::SourceText<char16_t> srcBuf; 7875 if (!srcBuf.initMaybeBorrowed(cx, linearChars)) { 7876 return false; 7877 } 7878 7879 CompileOptions options(cx); 7880 options.setFile("<compileToStencil>"); 7881 7882 RootedString displayURL(cx); 7883 RootedString sourceMapURL(cx); 7884 UniqueChars fileNameBytes; 7885 bool isModule = false; 7886 bool prepareForInstantiate = false; 7887 if (args.length() == 2) { 7888 if (!args[1].isObject()) { 7889 JS_ReportErrorASCII( 7890 cx, "compileToStencil: The 2nd argument must be an object"); 7891 return false; 7892 } 7893 7894 RootedObject opts(cx, &args[1].toObject()); 7895 7896 if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) { 7897 return false; 7898 } 7899 if (!ParseCompileOptionsForModule(cx, options, opts, isModule)) { 7900 return false; 7901 } 7902 if (!ParseCompileOptionsForInstantiate(cx, options, opts, 7903 prepareForInstantiate)) { 7904 return false; 7905 } 7906 if (!js::ParseSourceOptions(cx, opts, &displayURL, &sourceMapURL)) { 7907 return false; 7908 } 7909 } 7910 7911 RefPtr<JS::Stencil> stencil; 7912 if (isModule) { 7913 stencil = JS::CompileModuleScriptToStencil(cx, options, srcBuf); 7914 } else { 7915 stencil = JS::CompileGlobalScriptToStencil(cx, options, srcBuf); 7916 } 7917 if (!stencil) { 7918 return false; 7919 } 7920 7921 JS::InstantiationStorage storage; 7922 { 7923 AutoReportFrontendContext fc(cx); 7924 if (!SetSourceOptions(cx, &fc, stencil->getInitial()->source, displayURL, 7925 sourceMapURL)) { 7926 return false; 7927 } 7928 7929 if (prepareForInstantiate) { 7930 if (!JS::PrepareForInstantiate(&fc, *stencil, storage)) { 7931 return false; 7932 } 7933 } 7934 } 7935 7936 Rooted<js::StencilObject*> stencilObj( 7937 cx, js::StencilObject::create(cx, std::move(stencil))); 7938 if (!stencilObj) { 7939 return false; 7940 } 7941 7942 args.rval().setObject(*stencilObj); 7943 return true; 7944 } 7945 7946 static bool EvalStencil(JSContext* cx, uint32_t argc, Value* vp) { 7947 CallArgs args = CallArgsFromVp(argc, vp); 7948 7949 if (!args.requireAtLeast(cx, "evalStencil", 1)) { 7950 return false; 7951 } 7952 7953 /* Prepare the input byte array. */ 7954 if (!args[0].isObject()) { 7955 JS_ReportErrorASCII(cx, "evalStencil: Stencil object expected"); 7956 return false; 7957 } 7958 Rooted<js::StencilObject*> stencilObj( 7959 cx, args[0].toObject().maybeUnwrapIf<js::StencilObject>()); 7960 if (!stencilObj) { 7961 JS_ReportErrorASCII(cx, "evalStencil: Stencil object expected"); 7962 return false; 7963 } 7964 7965 if (stencilObj->stencil()->getInitial()->isModule()) { 7966 JS_ReportErrorASCII(cx, 7967 "evalStencil: Module stencil cannot be evaluated. Use " 7968 "instantiateModuleStencil instead"); 7969 return false; 7970 } 7971 7972 CompileOptions options(cx); 7973 UniqueChars fileNameBytes; 7974 Rooted<JS::Value> privateValue(cx); 7975 Rooted<JSString*> elementAttributeName(cx); 7976 if (args.length() == 2) { 7977 if (!args[1].isObject()) { 7978 JS_ReportErrorASCII(cx, 7979 "evalStencil: The 2nd argument must be an object"); 7980 return false; 7981 } 7982 7983 RootedObject opts(cx, &args[1].toObject()); 7984 7985 if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) { 7986 return false; 7987 } 7988 if (!js::ParseDebugMetadata(cx, opts, &privateValue, 7989 &elementAttributeName)) { 7990 return false; 7991 } 7992 } 7993 7994 bool useDebugMetadata = !privateValue.isUndefined() || elementAttributeName; 7995 7996 JS::InstantiateOptions instantiateOptions(options); 7997 if (useDebugMetadata) { 7998 instantiateOptions.hideScriptFromDebugger = true; 7999 } 8000 8001 if (!js::ValidateLazinessOfStencilAndGlobal(cx, stencilObj->stencil())) { 8002 return false; 8003 } 8004 8005 RootedScript script(cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, 8006 stencilObj->stencil())); 8007 if (!script) { 8008 return false; 8009 } 8010 8011 if (useDebugMetadata) { 8012 instantiateOptions.hideScriptFromDebugger = false; 8013 if (!JS::UpdateDebugMetadata(cx, script, instantiateOptions, privateValue, 8014 elementAttributeName, nullptr, nullptr)) { 8015 return false; 8016 } 8017 } 8018 8019 RootedValue retVal(cx); 8020 if (!JS_ExecuteScript(cx, script, &retVal)) { 8021 return false; 8022 } 8023 8024 args.rval().set(retVal); 8025 return true; 8026 } 8027 8028 static bool CompileToStencilXDR(JSContext* cx, uint32_t argc, Value* vp) { 8029 CallArgs args = CallArgsFromVp(argc, vp); 8030 8031 if (!args.requireAtLeast(cx, "compileToStencilXDR", 1)) { 8032 return false; 8033 } 8034 8035 RootedString src(cx, ToString<CanGC>(cx, args[0])); 8036 if (!src) { 8037 return false; 8038 } 8039 8040 /* Linearize the string to obtain a char16_t* range. */ 8041 AutoStableStringChars linearChars(cx); 8042 if (!linearChars.initTwoByte(cx, src)) { 8043 return false; 8044 } 8045 JS::SourceText<char16_t> srcBuf; 8046 if (!srcBuf.initMaybeBorrowed(cx, linearChars)) { 8047 return false; 8048 } 8049 8050 CompileOptions options(cx); 8051 options.setFile("<compileToStencilXDR>"); 8052 8053 RootedString displayURL(cx); 8054 RootedString sourceMapURL(cx); 8055 UniqueChars fileNameBytes; 8056 bool isModule = false; 8057 if (args.length() == 2) { 8058 if (!args[1].isObject()) { 8059 JS_ReportErrorASCII( 8060 cx, "compileToStencilXDR: The 2nd argument must be an object"); 8061 return false; 8062 } 8063 8064 RootedObject opts(cx, &args[1].toObject()); 8065 8066 if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) { 8067 return false; 8068 } 8069 if (!ParseCompileOptionsForModule(cx, options, opts, isModule)) { 8070 return false; 8071 } 8072 if (!js::ParseSourceOptions(cx, opts, &displayURL, &sourceMapURL)) { 8073 return false; 8074 } 8075 } 8076 8077 RefPtr<JS::Stencil> stencil; 8078 if (isModule) { 8079 stencil = JS::CompileModuleScriptToStencil(cx, options, srcBuf); 8080 } else { 8081 stencil = JS::CompileGlobalScriptToStencil(cx, options, srcBuf); 8082 } 8083 if (!stencil) { 8084 return false; 8085 } 8086 8087 { 8088 AutoReportFrontendContext fc(cx); 8089 if (!SetSourceOptions(cx, &fc, stencil->getInitial()->source, displayURL, 8090 sourceMapURL)) { 8091 return false; 8092 } 8093 } 8094 8095 /* Serialize the stencil to XDR. */ 8096 JS::TranscodeBuffer xdrBytes; 8097 auto result = JS::EncodeStencil(cx, stencil, xdrBytes); 8098 if (result == JS::TranscodeResult::Throw) { 8099 return false; 8100 } 8101 if (JS::IsTranscodeFailureResult(result)) { 8102 JS_ReportErrorASCII(cx, "Encoding failure"); 8103 return false; 8104 } 8105 8106 Rooted<StencilXDRBufferObject*> xdrObj( 8107 cx, 8108 StencilXDRBufferObject::create(cx, xdrBytes.begin(), xdrBytes.length())); 8109 if (!xdrObj) { 8110 return false; 8111 } 8112 8113 args.rval().setObject(*xdrObj); 8114 return true; 8115 } 8116 8117 static bool EvalStencilXDR(JSContext* cx, uint32_t argc, Value* vp) { 8118 CallArgs args = CallArgsFromVp(argc, vp); 8119 8120 if (!args.requireAtLeast(cx, "evalStencilXDR", 1)) { 8121 return false; 8122 } 8123 8124 /* Prepare the input byte array. */ 8125 if (!args[0].isObject()) { 8126 JS_ReportErrorASCII(cx, "evalStencilXDR: Stencil XDR object expected"); 8127 return false; 8128 } 8129 Rooted<StencilXDRBufferObject*> xdrObj( 8130 cx, args[0].toObject().maybeUnwrapIf<StencilXDRBufferObject>()); 8131 if (!xdrObj) { 8132 JS_ReportErrorASCII(cx, "evalStencilXDR: Stencil XDR object expected"); 8133 return false; 8134 } 8135 MOZ_ASSERT(xdrObj->hasBuffer()); 8136 8137 CompileOptions options(cx); 8138 UniqueChars fileNameBytes; 8139 Rooted<JS::Value> privateValue(cx); 8140 Rooted<JSString*> elementAttributeName(cx); 8141 if (args.length() == 2) { 8142 if (!args[1].isObject()) { 8143 JS_ReportErrorASCII(cx, 8144 "evalStencilXDR: The 2nd argument must be an object"); 8145 return false; 8146 } 8147 8148 RootedObject opts(cx, &args[1].toObject()); 8149 8150 if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) { 8151 return false; 8152 } 8153 if (!js::ParseDebugMetadata(cx, opts, &privateValue, 8154 &elementAttributeName)) { 8155 return false; 8156 } 8157 } 8158 8159 JS::TranscodeRange xdrRange(xdrObj->buffer(), xdrObj->bufferLength()); 8160 JS::DecodeOptions decodeOptions(options); 8161 RefPtr<JS::Stencil> stencil; 8162 auto result = 8163 JS::DecodeStencil(cx, decodeOptions, xdrRange, getter_AddRefs(stencil)); 8164 if (result == JS::TranscodeResult::Throw) { 8165 return false; 8166 } 8167 if (JS::IsTranscodeFailureResult(result)) { 8168 JS_ReportErrorASCII(cx, "Decoding failure"); 8169 return false; 8170 } 8171 8172 if (stencil->getInitial()->isModule()) { 8173 JS_ReportErrorASCII(cx, 8174 "evalStencilXDR: Module stencil cannot be evaluated. " 8175 "Use instantiateModuleStencilXDR instead"); 8176 return false; 8177 } 8178 8179 if (!js::ValidateLazinessOfStencilAndGlobal(cx, stencil.get())) { 8180 return false; 8181 } 8182 8183 bool useDebugMetadata = !privateValue.isUndefined() || elementAttributeName; 8184 8185 JS::InstantiateOptions instantiateOptions(options); 8186 if (useDebugMetadata) { 8187 instantiateOptions.hideScriptFromDebugger = true; 8188 } 8189 RootedScript script( 8190 cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil.get())); 8191 if (!script) { 8192 return false; 8193 } 8194 8195 if (useDebugMetadata) { 8196 instantiateOptions.hideScriptFromDebugger = false; 8197 if (!JS::UpdateDebugMetadata(cx, script, instantiateOptions, privateValue, 8198 elementAttributeName, nullptr, nullptr)) { 8199 return false; 8200 } 8201 } 8202 8203 RootedValue retVal(cx); 8204 if (!JS_ExecuteScript(cx, script, &retVal)) { 8205 return false; 8206 } 8207 8208 args.rval().set(retVal); 8209 return true; 8210 } 8211 8212 static bool GetExceptionInfo(JSContext* cx, uint32_t argc, Value* vp) { 8213 CallArgs args = CallArgsFromVp(argc, vp); 8214 8215 if (!args.requireAtLeast(cx, "getExceptionInfo", 1)) { 8216 return false; 8217 } 8218 8219 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) { 8220 JS_ReportErrorASCII(cx, "getExceptionInfo: expected function argument"); 8221 return false; 8222 } 8223 8224 RootedValue rval(cx); 8225 if (JS_CallFunctionValue(cx, nullptr, args[0], JS::HandleValueArray::empty(), 8226 &rval)) { 8227 // Function didn't throw. 8228 args.rval().setNull(); 8229 return true; 8230 } 8231 8232 // We currently don't support interrupts or forced returns. 8233 if (!cx->isExceptionPending()) { 8234 JS_ReportErrorASCII(cx, "getExceptionInfo: unsupported exception status"); 8235 return false; 8236 } 8237 8238 RootedValue excVal(cx); 8239 Rooted<SavedFrame*> stack(cx); 8240 if (!GetAndClearExceptionAndStack(cx, &excVal, &stack)) { 8241 return false; 8242 } 8243 8244 RootedValue stackVal(cx); 8245 if (stack) { 8246 RootedString stackString(cx); 8247 if (!BuildStackString(cx, cx->realm()->principals(), stack, &stackString)) { 8248 return false; 8249 } 8250 stackVal.setString(stackString); 8251 } else { 8252 stackVal.setNull(); 8253 } 8254 8255 RootedObject obj(cx, NewPlainObject(cx)); 8256 if (!obj) { 8257 return false; 8258 } 8259 8260 if (!JS_DefineProperty(cx, obj, "exception", excVal, JSPROP_ENUMERATE)) { 8261 return false; 8262 } 8263 8264 if (!JS_DefineProperty(cx, obj, "stack", stackVal, JSPROP_ENUMERATE)) { 8265 return false; 8266 } 8267 8268 args.rval().setObject(*obj); 8269 return true; 8270 } 8271 8272 class AllocationMarkerObject : public NativeObject { 8273 public: 8274 static const JSClass class_; 8275 }; 8276 8277 const JSClass AllocationMarkerObject::class_ = { 8278 "AllocationMarker", 8279 }; 8280 8281 static bool AllocationMarker(JSContext* cx, unsigned argc, Value* vp) { 8282 CallArgs args = CallArgsFromVp(argc, vp); 8283 8284 bool allocateInsideNursery = true; 8285 if (args.length() > 0 && args[0].isObject()) { 8286 RootedObject options(cx, &args[0].toObject()); 8287 8288 RootedValue nurseryVal(cx); 8289 if (!JS_GetProperty(cx, options, "nursery", &nurseryVal)) { 8290 return false; 8291 } 8292 allocateInsideNursery = ToBoolean(nurseryVal); 8293 } 8294 8295 JSObject* obj = 8296 allocateInsideNursery 8297 ? NewObjectWithGivenProto<AllocationMarkerObject>(cx, nullptr) 8298 : NewTenuredObjectWithGivenProto<AllocationMarkerObject>(cx, nullptr); 8299 if (!obj) { 8300 return false; 8301 } 8302 8303 args.rval().setObject(*obj); 8304 return true; 8305 } 8306 8307 namespace gcCallback { 8308 8309 struct MajorGC { 8310 int32_t depth; 8311 int32_t phases; 8312 }; 8313 8314 static void majorGC(JSContext* cx, JSGCStatus status, JS::GCReason reason, 8315 void* data) { 8316 auto info = static_cast<MajorGC*>(data); 8317 if (!(info->phases & (1 << status))) { 8318 return; 8319 } 8320 8321 if (info->depth > 0) { 8322 info->depth--; 8323 JS::PrepareForFullGC(cx); 8324 JS::NonIncrementalGC(cx, JS::GCOptions::Normal, JS::GCReason::API); 8325 info->depth++; 8326 } 8327 } 8328 8329 struct MinorGC { 8330 int32_t phases; 8331 bool active; 8332 }; 8333 8334 static void minorGC(JSContext* cx, JSGCStatus status, JS::GCReason reason, 8335 void* data) { 8336 auto info = static_cast<MinorGC*>(data); 8337 if (!(info->phases & (1 << status))) { 8338 return; 8339 } 8340 8341 if (info->active) { 8342 info->active = false; 8343 if (cx->zone() && !cx->zone()->isAtomsZone()) { 8344 cx->runtime()->gc.evictNursery(JS::GCReason::DEBUG_GC); 8345 } 8346 info->active = true; 8347 } 8348 } 8349 8350 // Process global, should really be runtime-local. 8351 static MajorGC majorGCInfo; 8352 static MinorGC minorGCInfo; 8353 8354 static void enterNullRealm(JSContext* cx, JSGCStatus status, 8355 JS::GCReason reason, void* data) { 8356 JSAutoNullableRealm enterRealm(cx, nullptr); 8357 } 8358 8359 } /* namespace gcCallback */ 8360 8361 static bool SetGCCallback(JSContext* cx, unsigned argc, Value* vp) { 8362 CallArgs args = CallArgsFromVp(argc, vp); 8363 8364 if (args.length() != 1) { 8365 JS_ReportErrorASCII(cx, "Wrong number of arguments"); 8366 return false; 8367 } 8368 8369 RootedObject opts(cx, ToObject(cx, args[0])); 8370 if (!opts) { 8371 return false; 8372 } 8373 8374 RootedValue v(cx); 8375 if (!JS_GetProperty(cx, opts, "action", &v)) { 8376 return false; 8377 } 8378 8379 JSString* str = JS::ToString(cx, v); 8380 if (!str) { 8381 return false; 8382 } 8383 Rooted<JSLinearString*> action(cx, str->ensureLinear(cx)); 8384 if (!action) { 8385 return false; 8386 } 8387 8388 int32_t phases = 0; 8389 if (StringEqualsLiteral(action, "minorGC") || 8390 StringEqualsLiteral(action, "majorGC")) { 8391 if (!JS_GetProperty(cx, opts, "phases", &v)) { 8392 return false; 8393 } 8394 if (v.isUndefined()) { 8395 phases = (1 << JSGC_END); 8396 } else { 8397 JSString* str = JS::ToString(cx, v); 8398 if (!str) { 8399 return false; 8400 } 8401 JSLinearString* phasesStr = str->ensureLinear(cx); 8402 if (!phasesStr) { 8403 return false; 8404 } 8405 8406 if (StringEqualsLiteral(phasesStr, "begin")) { 8407 phases = (1 << JSGC_BEGIN); 8408 } else if (StringEqualsLiteral(phasesStr, "end")) { 8409 phases = (1 << JSGC_END); 8410 } else if (StringEqualsLiteral(phasesStr, "both")) { 8411 phases = (1 << JSGC_BEGIN) | (1 << JSGC_END); 8412 } else { 8413 JS_ReportErrorASCII(cx, "Invalid callback phase"); 8414 return false; 8415 } 8416 } 8417 } 8418 8419 if (StringEqualsLiteral(action, "minorGC")) { 8420 gcCallback::minorGCInfo.phases = phases; 8421 gcCallback::minorGCInfo.active = true; 8422 JS_SetGCCallback(cx, gcCallback::minorGC, &gcCallback::minorGCInfo); 8423 } else if (StringEqualsLiteral(action, "majorGC")) { 8424 if (!JS_GetProperty(cx, opts, "depth", &v)) { 8425 return false; 8426 } 8427 int32_t depth = 1; 8428 if (!v.isUndefined()) { 8429 if (!ToInt32(cx, v, &depth)) { 8430 return false; 8431 } 8432 } 8433 if (depth < 0) { 8434 JS_ReportErrorASCII(cx, "Nesting depth cannot be negative"); 8435 return false; 8436 } 8437 if (depth + gcstats::MAX_PHASE_NESTING > 8438 gcstats::Statistics::MAX_SUSPENDED_PHASES) { 8439 JS_ReportErrorASCII(cx, "Nesting depth too large, would overflow"); 8440 return false; 8441 } 8442 8443 gcCallback::majorGCInfo.phases = phases; 8444 gcCallback::majorGCInfo.depth = depth; 8445 JS_SetGCCallback(cx, gcCallback::majorGC, &gcCallback::majorGCInfo); 8446 } else if (StringEqualsLiteral(action, "enterNullRealm")) { 8447 JS_SetGCCallback(cx, gcCallback::enterNullRealm, nullptr); 8448 } else { 8449 JS_ReportErrorASCII(cx, "Unknown GC callback action"); 8450 return false; 8451 } 8452 8453 args.rval().setUndefined(); 8454 return true; 8455 } 8456 8457 #ifdef DEBUG 8458 static bool EnqueueMark(JSContext* cx, unsigned argc, Value* vp) { 8459 CallArgs args = CallArgsFromVp(argc, vp); 8460 gc::GCRuntime* gc = &cx->runtime()->gc; 8461 8462 if (gc->isIncrementalGCInProgress() && 8463 gc->hasZealMode(gc::ZealMode::IncrementalMarkingValidator)) { 8464 JS_ReportErrorASCII( 8465 cx, 8466 "Can't add to test mark queue during incremental GC if marking " 8467 "validation is enabled as this can cause failures"); 8468 return false; 8469 } 8470 8471 if (args.get(0).isString()) { 8472 RootedString val(cx, args[0].toString()); 8473 if (!val->ensureLinear(cx)) { 8474 return false; 8475 } 8476 if (!gc->appendTestMarkQueue(StringValue(val))) { 8477 JS_ReportOutOfMemory(cx); 8478 return false; 8479 } 8480 } else if (args.get(0).isObject()) { 8481 if (!gc->appendTestMarkQueue(args[0])) { 8482 JS_ReportOutOfMemory(cx); 8483 return false; 8484 } 8485 } else { 8486 JS_ReportErrorASCII(cx, "Argument must be a string or object"); 8487 return false; 8488 } 8489 8490 args.rval().setUndefined(); 8491 return true; 8492 } 8493 8494 static bool GetMarkQueue(JSContext* cx, unsigned argc, Value* vp) { 8495 CallArgs args = CallArgsFromVp(argc, vp); 8496 8497 const auto& queue = cx->runtime()->gc.getTestMarkQueue(); 8498 8499 RootedObject result(cx, JS::NewArrayObject(cx, queue.length())); 8500 if (!result) { 8501 return false; 8502 } 8503 for (size_t i = 0; i < queue.length(); i++) { 8504 RootedValue val(cx, queue[i]); 8505 if (!JS_WrapValue(cx, &val)) { 8506 return false; 8507 } 8508 if (!JS_SetElement(cx, result, i, val)) { 8509 return false; 8510 } 8511 } 8512 8513 args.rval().setObject(*result); 8514 return true; 8515 } 8516 8517 static bool ClearMarkQueue(JSContext* cx, unsigned argc, Value* vp) { 8518 CallArgs args = CallArgsFromVp(argc, vp); 8519 8520 cx->runtime()->gc.clearTestMarkQueue(); 8521 args.rval().setUndefined(); 8522 return true; 8523 } 8524 #endif // DEBUG 8525 8526 static bool NurseryStringsEnabled(JSContext* cx, unsigned argc, Value* vp) { 8527 CallArgs args = CallArgsFromVp(argc, vp); 8528 args.rval().setBoolean(cx->zone()->allocNurseryStrings()); 8529 return true; 8530 } 8531 8532 static bool IsNurseryAllocated(JSContext* cx, unsigned argc, Value* vp) { 8533 CallArgs args = CallArgsFromVp(argc, vp); 8534 if (!args.get(0).isGCThing()) { 8535 JS_ReportErrorASCII( 8536 cx, "The function takes one argument, which must be a GC thing"); 8537 return false; 8538 } 8539 8540 args.rval().setBoolean(IsInsideNursery(args[0].toGCThing())); 8541 return true; 8542 } 8543 8544 static bool NumAllocSitesPretenured(JSContext* cx, unsigned argc, Value* vp) { 8545 CallArgs args = CallArgsFromVp(argc, vp); 8546 args.rval().setInt32(cx->realm()->numAllocSitesPretenured); 8547 return true; 8548 } 8549 8550 static bool GetLcovInfo(JSContext* cx, unsigned argc, Value* vp) { 8551 CallArgs args = CallArgsFromVp(argc, vp); 8552 8553 if (args.length() > 1) { 8554 JS_ReportErrorASCII(cx, "Wrong number of arguments"); 8555 return false; 8556 } 8557 8558 if (!coverage::IsLCovEnabled()) { 8559 JS_ReportErrorASCII(cx, "Coverage not enabled for process."); 8560 return false; 8561 } 8562 8563 RootedObject global(cx); 8564 if (args.hasDefined(0)) { 8565 global = ToObject(cx, args[0]); 8566 if (!global) { 8567 JS_ReportErrorASCII(cx, "Permission denied to access global"); 8568 return false; 8569 } 8570 global = CheckedUnwrapDynamic(global, cx, /* stopAtWindowProxy = */ false); 8571 if (!global) { 8572 ReportAccessDenied(cx); 8573 return false; 8574 } 8575 if (!global->is<GlobalObject>()) { 8576 JS_ReportErrorASCII(cx, "Argument must be a global object"); 8577 return false; 8578 } 8579 } else { 8580 global = JS::CurrentGlobalOrNull(cx); 8581 } 8582 8583 size_t length = 0; 8584 UniqueChars content; 8585 { 8586 AutoRealm ar(cx, global); 8587 content = js::GetCodeCoverageSummary(cx, &length); 8588 } 8589 8590 if (!content) { 8591 return false; 8592 } 8593 8594 JSString* str = 8595 JS_NewStringCopyUTF8N(cx, JS::UTF8Chars(content.get(), length)); 8596 if (!str) { 8597 return false; 8598 } 8599 8600 args.rval().setString(str); 8601 return true; 8602 } 8603 8604 #ifdef DEBUG 8605 static bool SetRNGState(JSContext* cx, unsigned argc, Value* vp) { 8606 CallArgs args = CallArgsFromVp(argc, vp); 8607 if (!args.requireAtLeast(cx, "SetRNGState", 2)) { 8608 return false; 8609 } 8610 8611 double d0; 8612 if (!ToNumber(cx, args[0], &d0)) { 8613 return false; 8614 } 8615 8616 double d1; 8617 if (!ToNumber(cx, args[1], &d1)) { 8618 return false; 8619 } 8620 8621 uint64_t seed0 = static_cast<uint64_t>(d0); 8622 uint64_t seed1 = static_cast<uint64_t>(d1); 8623 8624 if (seed0 == 0 && seed1 == 0) { 8625 JS_ReportErrorASCII(cx, "RNG requires non-zero seed"); 8626 return false; 8627 } 8628 8629 cx->realm()->getOrCreateRandomNumberGenerator().setState(seed0, seed1); 8630 8631 args.rval().setUndefined(); 8632 return true; 8633 } 8634 #endif 8635 8636 static bool GetTimeZone(JSContext* cx, unsigned argc, Value* vp) { 8637 CallArgs args = CallArgsFromVp(argc, vp); 8638 RootedObject callee(cx, &args.callee()); 8639 8640 if (args.length() != 0) { 8641 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 8642 return false; 8643 } 8644 8645 #ifndef __wasi__ 8646 auto getTimeZone = [](std::time_t* now) -> const char* { 8647 std::tm local{}; 8648 # if defined(_WIN32) 8649 _tzset(); 8650 if (localtime_s(&local, now) == 0) { 8651 return _tzname[local.tm_isdst > 0]; 8652 } 8653 # else 8654 tzset(); 8655 # if defined(HAVE_LOCALTIME_R) 8656 if (localtime_r(now, &local)) { 8657 # else 8658 std::tm* localtm = std::localtime(now); 8659 if (localtm) { 8660 *local = *localtm; 8661 # endif /* HAVE_LOCALTIME_R */ 8662 8663 # if defined(HAVE_TM_ZONE_TM_GMTOFF) 8664 return local.tm_zone; 8665 # else 8666 return tzname[local.tm_isdst > 0]; 8667 # endif /* HAVE_TM_ZONE_TM_GMTOFF */ 8668 } 8669 # endif /* _WIN32 */ 8670 return nullptr; 8671 }; 8672 8673 std::time_t now = std::time(nullptr); 8674 if (now != static_cast<std::time_t>(-1)) { 8675 if (const char* tz = getTimeZone(&now)) { 8676 return ReturnStringCopy(cx, args, tz); 8677 } 8678 } 8679 #endif /* __wasi__ */ 8680 args.rval().setUndefined(); 8681 return true; 8682 } 8683 8684 static bool SetTimeZone(JSContext* cx, unsigned argc, Value* vp) { 8685 CallArgs args = CallArgsFromVp(argc, vp); 8686 RootedObject callee(cx, &args.callee()); 8687 8688 if (args.length() != 1) { 8689 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 8690 return false; 8691 } 8692 8693 if (!args[0].isString() && !args[0].isUndefined()) { 8694 ReportUsageErrorASCII(cx, callee, 8695 "First argument should be a string or undefined"); 8696 return false; 8697 } 8698 8699 #ifndef __wasi__ 8700 auto setTimeZone = [](const char* value) { 8701 # if defined(_WIN32) 8702 return _putenv_s("TZ", value) == 0; 8703 # else 8704 return setenv("TZ", value, true) == 0; 8705 # endif /* _WIN32 */ 8706 }; 8707 8708 auto unsetTimeZone = []() { 8709 # if defined(_WIN32) 8710 return _putenv_s("TZ", "") == 0; 8711 # else 8712 return unsetenv("TZ") == 0; 8713 # endif /* _WIN32 */ 8714 }; 8715 8716 if (args[0].isString() && !args[0].toString()->empty()) { 8717 Rooted<JSString*> str(cx, args[0].toString()); 8718 if (!str) { 8719 return false; 8720 } 8721 8722 UniqueChars timeZone = 8723 StringToTimeZone(cx, callee, str, AllowTimeZoneLink::Yes); 8724 if (!timeZone) { 8725 return false; 8726 } 8727 8728 if (!setTimeZone(timeZone.get())) { 8729 JS_ReportErrorASCII(cx, "Failed to set 'TZ' environment variable"); 8730 return false; 8731 } 8732 } else { 8733 if (!unsetTimeZone()) { 8734 JS_ReportErrorASCII(cx, "Failed to unset 'TZ' environment variable"); 8735 return false; 8736 } 8737 } 8738 8739 # if defined(_WIN32) 8740 _tzset(); 8741 # else 8742 tzset(); 8743 # endif /* _WIN32 */ 8744 8745 JS::ResetTimeZone(); 8746 8747 #endif /* __wasi__ */ 8748 args.rval().setUndefined(); 8749 return true; 8750 } 8751 8752 static bool GetRealmTimeZone(JSContext* cx, unsigned argc, Value* vp) { 8753 CallArgs args = CallArgsFromVp(argc, vp); 8754 RootedObject callee(cx, &args.callee()); 8755 8756 if (args.length() != 0) { 8757 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 8758 return false; 8759 } 8760 8761 #ifdef JS_HAS_INTL_API 8762 auto* str = cx->global()->globalIntlData().defaultTimeZone(cx); 8763 if (!str) { 8764 return false; 8765 } 8766 8767 args.rval().setString(str); 8768 #else 8769 // Realm time zones require Intl support. 8770 args.rval().setString(cx->emptyString()); 8771 #endif 8772 8773 return true; 8774 } 8775 8776 static bool SetRealmTimeZone(JSContext* cx, unsigned argc, Value* vp) { 8777 CallArgs args = CallArgsFromVp(argc, vp); 8778 RootedObject callee(cx, &args.callee()); 8779 8780 if (args.length() != 1) { 8781 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 8782 return false; 8783 } 8784 8785 if (!args[0].isString() && !args[0].isUndefined()) { 8786 ReportUsageErrorASCII(cx, callee, 8787 "First argument should be a string or undefined"); 8788 return false; 8789 } 8790 8791 if (args[0].isString() && !args[0].toString()->empty()) { 8792 Rooted<JSString*> str(cx, args[0].toString()); 8793 if (!str) { 8794 return false; 8795 } 8796 8797 auto timeZone = StringToTimeZone(cx, callee, str, AllowTimeZoneLink::No); 8798 if (!timeZone) { 8799 return false; 8800 } 8801 8802 cx->realm()->setTimeZoneOverride(timeZone.get()); 8803 } else { 8804 // Reset to use the system default time zone. 8805 cx->realm()->setTimeZoneOverride(nullptr); 8806 } 8807 8808 args.rval().setUndefined(); 8809 return true; 8810 } 8811 8812 static bool GetCoreCount(JSContext* cx, unsigned argc, Value* vp) { 8813 CallArgs args = CallArgsFromVp(argc, vp); 8814 RootedObject callee(cx, &args.callee()); 8815 8816 if (args.length() != 0) { 8817 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 8818 return false; 8819 } 8820 8821 args.rval().setInt32(GetCPUCount()); 8822 return true; 8823 } 8824 8825 static bool GetDefaultLocale(JSContext* cx, unsigned argc, Value* vp) { 8826 CallArgs args = CallArgsFromVp(argc, vp); 8827 RootedObject callee(cx, &args.callee()); 8828 8829 if (args.length() != 0) { 8830 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 8831 return false; 8832 } 8833 8834 UniqueChars locale = JS_GetDefaultLocale(cx); 8835 if (!locale) { 8836 return false; 8837 } 8838 8839 return ReturnStringCopy(cx, args, locale.get()); 8840 } 8841 8842 static bool SetDefaultLocale(JSContext* cx, unsigned argc, Value* vp) { 8843 CallArgs args = CallArgsFromVp(argc, vp); 8844 RootedObject callee(cx, &args.callee()); 8845 8846 if (args.length() != 1) { 8847 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 8848 return false; 8849 } 8850 8851 if (!args[0].isString() && !args[0].isUndefined()) { 8852 ReportUsageErrorASCII(cx, callee, 8853 "First argument should be a string or undefined"); 8854 return false; 8855 } 8856 8857 if (args[0].isString() && !args[0].toString()->empty()) { 8858 RootedString str(cx, args[0].toString()); 8859 UniqueChars locale = StringToLocale(cx, callee, str); 8860 if (!locale) { 8861 return false; 8862 } 8863 8864 if (!JS_SetDefaultLocale(cx->runtime(), locale.get())) { 8865 ReportOutOfMemory(cx); 8866 return false; 8867 } 8868 } else { 8869 JS_ResetDefaultLocale(cx->runtime()); 8870 } 8871 8872 args.rval().setUndefined(); 8873 return true; 8874 } 8875 8876 static bool GetRealmLocale(JSContext* cx, unsigned argc, Value* vp) { 8877 CallArgs args = CallArgsFromVp(argc, vp); 8878 RootedObject callee(cx, &args.callee()); 8879 8880 if (args.length() != 0) { 8881 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 8882 return false; 8883 } 8884 8885 #ifdef JS_HAS_INTL_API 8886 auto* str = cx->global()->globalIntlData().defaultLocale(cx); 8887 if (!str) { 8888 return false; 8889 } 8890 8891 args.rval().setString(str); 8892 #else 8893 // Realm locales require Intl support. 8894 args.rval().setString(cx->emptyString()); 8895 #endif 8896 8897 return true; 8898 } 8899 8900 static bool SetRealmLocale(JSContext* cx, unsigned argc, Value* vp) { 8901 CallArgs args = CallArgsFromVp(argc, vp); 8902 RootedObject callee(cx, &args.callee()); 8903 8904 if (args.length() != 1) { 8905 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 8906 return false; 8907 } 8908 8909 if (!args[0].isString() && !args[0].isUndefined()) { 8910 ReportUsageErrorASCII(cx, callee, 8911 "First argument should be a string or undefined"); 8912 return false; 8913 } 8914 8915 if (args[0].isString() && !args[0].toString()->empty()) { 8916 Rooted<JSString*> str(cx, args[0].toString()); 8917 if (!str) { 8918 return false; 8919 } 8920 8921 auto locale = StringToLocale(cx, callee, str); 8922 if (!locale) { 8923 return false; 8924 } 8925 8926 cx->realm()->setLocaleOverride(locale.get()); 8927 } else { 8928 // Reset to use the system default locale. 8929 cx->realm()->setLocaleOverride(nullptr); 8930 } 8931 8932 args.rval().setUndefined(); 8933 return true; 8934 } 8935 8936 #ifdef AFLFUZZ 8937 static bool AflLoop(JSContext* cx, unsigned argc, Value* vp) { 8938 CallArgs args = CallArgsFromVp(argc, vp); 8939 8940 uint32_t max_cnt; 8941 if (!ToUint32(cx, args.get(0), &max_cnt)) { 8942 return false; 8943 } 8944 8945 args.rval().setBoolean(!!__AFL_LOOP(max_cnt)); 8946 return true; 8947 } 8948 #endif 8949 8950 static bool MonotonicNow(JSContext* cx, unsigned argc, Value* vp) { 8951 CallArgs args = CallArgsFromVp(argc, vp); 8952 double now; 8953 8954 // The std::chrono symbols are too new to be present in STL on all platforms we 8955 // care about, so use raw POSIX clock APIs when it might be necessary. 8956 #if defined(XP_UNIX) && !defined(XP_DARWIN) 8957 auto ComputeNow = [](const timespec& ts) { 8958 return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; 8959 }; 8960 8961 timespec ts; 8962 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { 8963 // Use a monotonic clock if available. 8964 now = ComputeNow(ts); 8965 } else { 8966 // Use a realtime clock as fallback. 8967 if (clock_gettime(CLOCK_REALTIME, &ts) != 0) { 8968 // Fail if no clock is available. 8969 JS_ReportErrorASCII(cx, "can't retrieve system clock"); 8970 return false; 8971 } 8972 8973 now = ComputeNow(ts); 8974 8975 // Manually enforce atomicity on a non-monotonic clock. 8976 { 8977 static mozilla::Atomic<bool, mozilla::ReleaseAcquire> spinLock; 8978 while (!spinLock.compareExchange(false, true)) { 8979 continue; 8980 } 8981 8982 static double lastNow = -FLT_MAX; 8983 now = lastNow = std::max(now, lastNow); 8984 8985 spinLock = false; 8986 } 8987 } 8988 #else 8989 using std::chrono::duration_cast; 8990 using std::chrono::milliseconds; 8991 using std::chrono::steady_clock; 8992 now = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()) 8993 .count(); 8994 #endif // XP_UNIX && !XP_DARWIN 8995 8996 args.rval().setNumber(now); 8997 return true; 8998 } 8999 9000 static bool TimeSinceCreation(JSContext* cx, unsigned argc, Value* vp) { 9001 CallArgs args = CallArgsFromVp(argc, vp); 9002 double when = 9003 (mozilla::TimeStamp::Now() - mozilla::TimeStamp::ProcessCreation()) 9004 .ToMilliseconds(); 9005 args.rval().setNumber(when); 9006 return true; 9007 } 9008 9009 static bool GetInnerMostEnvironmentObject(JSContext* cx, unsigned argc, 9010 Value* vp) { 9011 CallArgs args = CallArgsFromVp(argc, vp); 9012 9013 FrameIter iter(cx); 9014 if (iter.done()) { 9015 args.rval().setNull(); 9016 return true; 9017 } 9018 9019 args.rval().setObjectOrNull(iter.environmentChain(cx)); 9020 return true; 9021 } 9022 9023 static bool GetEnclosingEnvironmentObject(JSContext* cx, unsigned argc, 9024 Value* vp) { 9025 CallArgs args = CallArgsFromVp(argc, vp); 9026 if (!args.requireAtLeast(cx, "getEnclosingEnvironmentObject", 1)) { 9027 return false; 9028 } 9029 9030 if (!args[0].isObject()) { 9031 args.rval().setUndefined(); 9032 return true; 9033 } 9034 9035 JSObject* envObj = &args[0].toObject(); 9036 9037 if (envObj->is<EnvironmentObject>()) { 9038 EnvironmentObject* env = &envObj->as<EnvironmentObject>(); 9039 args.rval().setObject(env->enclosingEnvironment()); 9040 return true; 9041 } 9042 9043 if (envObj->is<DebugEnvironmentProxy>()) { 9044 DebugEnvironmentProxy* envProxy = &envObj->as<DebugEnvironmentProxy>(); 9045 args.rval().setObject(envProxy->enclosingEnvironment()); 9046 return true; 9047 } 9048 9049 args.rval().setNull(); 9050 return true; 9051 } 9052 9053 static bool GetEnvironmentObjectType(JSContext* cx, unsigned argc, Value* vp) { 9054 CallArgs args = CallArgsFromVp(argc, vp); 9055 if (!args.requireAtLeast(cx, "getEnvironmentObjectType", 1)) { 9056 return false; 9057 } 9058 9059 if (!args[0].isObject()) { 9060 args.rval().setUndefined(); 9061 return true; 9062 } 9063 9064 JSObject* envObj = &args[0].toObject(); 9065 9066 if (envObj->is<EnvironmentObject>()) { 9067 EnvironmentObject* env = &envObj->as<EnvironmentObject>(); 9068 return ReturnStringCopy(cx, args, env->typeString()); 9069 } 9070 if (envObj->is<DebugEnvironmentProxy>()) { 9071 DebugEnvironmentProxy* envProxy = &envObj->as<DebugEnvironmentProxy>(); 9072 EnvironmentObject* env = &envProxy->environment(); 9073 char buf[256] = {'\0'}; 9074 SprintfLiteral(buf, "[DebugProxy] %s", env->typeString()); 9075 return ReturnStringCopy(cx, args, buf); 9076 } 9077 9078 args.rval().setUndefined(); 9079 return true; 9080 } 9081 9082 static bool AssertRealmFuseInvariants(JSContext* cx, unsigned argc, Value* vp) { 9083 CallArgs args = CallArgsFromVp(argc, vp); 9084 // Note: This will crash if any invariant isn't held, so it's sufficient to 9085 // simply return true always. 9086 cx->realm()->realmFuses.assertInvariants(cx); 9087 args.rval().setUndefined(); 9088 return true; 9089 } 9090 9091 static bool AssertRuntimeFuseInvariants(JSContext* cx, unsigned argc, 9092 Value* vp) { 9093 CallArgs args = CallArgsFromVp(argc, vp); 9094 // Note: This will crash if any invariant isn't held, so it's sufficient to 9095 // simply return true always. 9096 cx->runtime()->runtimeFuses.ref().assertInvariants(cx); 9097 args.rval().setUndefined(); 9098 return true; 9099 } 9100 9101 static bool GetFuseState(JSContext* cx, unsigned argc, Value* vp) { 9102 CallArgs args = CallArgsFromVp(argc, vp); 9103 9104 cx->realm()->realmFuses.assertInvariants(cx); 9105 9106 cx->runtime()->runtimeFuses.ref().assertInvariants(cx); 9107 9108 RootedObject returnObj(cx, JS_NewPlainObject(cx)); 9109 if (!returnObj) { 9110 return false; 9111 } 9112 9113 RootedObject fuseObj(cx); 9114 RootedValue intactValue(cx); 9115 9116 #define REALM_FUSE(Name, LowerName) \ 9117 fuseObj = JS_NewPlainObject(cx); \ 9118 if (!fuseObj) { \ 9119 return false; \ 9120 } \ 9121 intactValue.setBoolean(cx->realm()->realmFuses.LowerName.intact()); \ 9122 if (!JS_DefineProperty(cx, fuseObj, "intact", intactValue, \ 9123 JSPROP_ENUMERATE)) { \ 9124 return false; \ 9125 } \ 9126 if (!JS_DefineProperty(cx, returnObj, #Name, fuseObj, JSPROP_ENUMERATE)) { \ 9127 return false; \ 9128 } 9129 9130 FOR_EACH_REALM_FUSE(REALM_FUSE) 9131 #undef REALM_FUSE 9132 9133 #define RUNTIME_FUSE(Name, LowerName) \ 9134 fuseObj = JS_NewPlainObject(cx); \ 9135 if (!fuseObj) { \ 9136 return false; \ 9137 } \ 9138 intactValue.setBoolean( \ 9139 cx->runtime()->runtimeFuses.ref().LowerName.intact()); \ 9140 if (!JS_DefineProperty(cx, fuseObj, "intact", intactValue, \ 9141 JSPROP_ENUMERATE)) { \ 9142 return false; \ 9143 } \ 9144 if (!JS_DefineProperty(cx, returnObj, #Name, fuseObj, JSPROP_ENUMERATE)) { \ 9145 return false; \ 9146 } 9147 9148 FOR_EACH_RUNTIME_FUSE(RUNTIME_FUSE) 9149 #undef RUNTIME_FUSE 9150 9151 args.rval().setObject(*returnObj); 9152 return true; 9153 } 9154 9155 static bool PopAllFusesInRealm(JSContext* cx, unsigned argc, Value* vp) { 9156 CallArgs args = CallArgsFromVp(argc, vp); 9157 9158 MOZ_ASSERT(cx->realm()); 9159 9160 RealmFuses& realmFuses = cx->realm()->realmFuses; 9161 9162 #define FUSE(Name, LowerName) realmFuses.LowerName.popFuse(cx, realmFuses); 9163 FOR_EACH_REALM_FUSE(FUSE) 9164 #undef FUSE 9165 9166 args.rval().setUndefined(); 9167 return true; 9168 } 9169 9170 static bool PopAllFusesInRuntime(JSContext* cx, unsigned argc, Value* vp) { 9171 CallArgs args = CallArgsFromVp(argc, vp); 9172 9173 RuntimeFuses& runtimeFuses = cx->runtime()->runtimeFuses.ref(); 9174 9175 #define FUSE(Name, LowerName) runtimeFuses.LowerName.popFuse(cx); 9176 FOR_EACH_RUNTIME_FUSE(FUSE) 9177 #undef FUSE 9178 9179 args.rval().setUndefined(); 9180 return true; 9181 } 9182 9183 static bool GetAllPrefNames(JSContext* cx, unsigned argc, Value* vp) { 9184 CallArgs args = CallArgsFromVp(argc, vp); 9185 9186 RootedValueVector values(cx); 9187 9188 auto addPref = [cx, &values](const char* name) { 9189 JSString* s = JS_NewStringCopyZ(cx, name); 9190 if (!s) { 9191 return false; 9192 } 9193 return values.append(StringValue(s)); 9194 }; 9195 9196 #define ADD_NAME(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF) \ 9197 if (!addPref(NAME)) { \ 9198 return false; \ 9199 } 9200 FOR_EACH_JS_PREF(ADD_NAME) 9201 #undef ADD_NAME 9202 9203 ArrayObject* arr = NewDenseCopiedArray(cx, values.length(), values.begin()); 9204 if (!arr) { 9205 return false; 9206 } 9207 9208 args.rval().setObject(*arr); 9209 return true; 9210 } 9211 9212 static bool GetPrefValue(JSContext* cx, unsigned argc, Value* vp) { 9213 CallArgs args = CallArgsFromVp(argc, vp); 9214 if (!args.requireAtLeast(cx, "getPrefValue", 1)) { 9215 return false; 9216 } 9217 9218 if (!args[0].isString()) { 9219 JS_ReportErrorASCII(cx, "expected string argument"); 9220 return false; 9221 } 9222 9223 Rooted<JSLinearString*> name(cx, args[0].toString()->ensureLinear(cx)); 9224 if (!name) { 9225 return false; 9226 } 9227 9228 auto setReturnValue = [&args](auto value) { 9229 using T = decltype(value); 9230 if constexpr (std::is_same_v<T, bool>) { 9231 args.rval().setBoolean(value); 9232 } else { 9233 static_assert(std::is_same_v<T, int32_t> || std::is_same_v<T, uint32_t>); 9234 args.rval().setNumber(value); 9235 } 9236 }; 9237 9238 // Search for a matching pref and return its value. 9239 #define CHECK_PREF(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF) \ 9240 if (StringEqualsLiteral(name, NAME)) { \ 9241 setReturnValue(JS::Prefs::CPP_NAME()); \ 9242 return true; \ 9243 } 9244 FOR_EACH_JS_PREF(CHECK_PREF) 9245 #undef CHECK_PREF 9246 9247 JS_ReportErrorASCII(cx, "invalid pref name"); 9248 return false; 9249 } 9250 9251 static bool GetErrorNotes(JSContext* cx, unsigned argc, Value* vp) { 9252 CallArgs args = CallArgsFromVp(argc, vp); 9253 if (!args.requireAtLeast(cx, "getErrorNotes", 1)) { 9254 return false; 9255 } 9256 9257 if (!args[0].isObject() || !args[0].toObject().is<ErrorObject>()) { 9258 args.rval().setNull(); 9259 return true; 9260 } 9261 9262 JSErrorReport* report = args[0].toObject().as<ErrorObject>().getErrorReport(); 9263 if (!report) { 9264 args.rval().setNull(); 9265 return true; 9266 } 9267 9268 RootedObject notesArray(cx, CreateErrorNotesArray(cx, report)); 9269 if (!notesArray) { 9270 return false; 9271 } 9272 9273 args.rval().setObject(*notesArray); 9274 return true; 9275 } 9276 9277 static bool IsConstructor(JSContext* cx, unsigned argc, Value* vp) { 9278 CallArgs args = CallArgsFromVp(argc, vp); 9279 if (args.length() < 1) { 9280 args.rval().setBoolean(false); 9281 } else { 9282 args.rval().setBoolean(IsConstructor(args[0])); 9283 } 9284 return true; 9285 } 9286 9287 static bool SetTimeResolution(JSContext* cx, unsigned argc, Value* vp) { 9288 CallArgs args = CallArgsFromVp(argc, vp); 9289 RootedObject callee(cx, &args.callee()); 9290 9291 if (!args.requireAtLeast(cx, "setTimeResolution", 2)) { 9292 return false; 9293 } 9294 9295 if (!args[0].isInt32()) { 9296 ReportUsageErrorASCII(cx, callee, "First argument must be an Int32."); 9297 return false; 9298 } 9299 int32_t resolution = args[0].toInt32(); 9300 9301 if (!args[1].isBoolean()) { 9302 ReportUsageErrorASCII(cx, callee, "Second argument must be a Boolean"); 9303 return false; 9304 } 9305 bool jitter = args[1].toBoolean(); 9306 9307 JS::SetTimeResolutionUsec(resolution, jitter); 9308 9309 args.rval().setUndefined(); 9310 return true; 9311 } 9312 9313 static bool ScriptedCallerGlobal(JSContext* cx, unsigned argc, Value* vp) { 9314 CallArgs args = CallArgsFromVp(argc, vp); 9315 9316 RootedObject obj(cx, JS::GetScriptedCallerGlobal(cx)); 9317 if (!obj) { 9318 args.rval().setNull(); 9319 return true; 9320 } 9321 9322 obj = ToWindowProxyIfWindow(obj); 9323 9324 if (!cx->compartment()->wrap(cx, &obj)) { 9325 return false; 9326 } 9327 9328 args.rval().setObject(*obj); 9329 return true; 9330 } 9331 9332 static bool ObjectGlobal(JSContext* cx, unsigned argc, Value* vp) { 9333 CallArgs args = CallArgsFromVp(argc, vp); 9334 RootedObject callee(cx, &args.callee()); 9335 9336 if (!args.get(0).isObject()) { 9337 ReportUsageErrorASCII(cx, callee, "Argument must be an object"); 9338 return false; 9339 } 9340 9341 RootedObject obj(cx, &args[0].toObject()); 9342 if (IsCrossCompartmentWrapper(obj)) { 9343 args.rval().setNull(); 9344 return true; 9345 } 9346 9347 obj = ToWindowProxyIfWindow(&obj->nonCCWGlobal()); 9348 9349 args.rval().setObject(*obj); 9350 return true; 9351 } 9352 9353 static bool IsSameCompartment(JSContext* cx, unsigned argc, Value* vp) { 9354 CallArgs args = CallArgsFromVp(argc, vp); 9355 RootedObject callee(cx, &args.callee()); 9356 9357 if (!args.get(0).isObject() || !args.get(1).isObject()) { 9358 ReportUsageErrorASCII(cx, callee, "Both arguments must be objects"); 9359 return false; 9360 } 9361 9362 RootedObject obj1(cx, UncheckedUnwrap(&args[0].toObject())); 9363 RootedObject obj2(cx, UncheckedUnwrap(&args[1].toObject())); 9364 9365 args.rval().setBoolean(obj1->compartment() == obj2->compartment()); 9366 return true; 9367 } 9368 9369 static bool FirstGlobalInCompartment(JSContext* cx, unsigned argc, Value* vp) { 9370 CallArgs args = CallArgsFromVp(argc, vp); 9371 RootedObject callee(cx, &args.callee()); 9372 9373 if (!args.get(0).isObject()) { 9374 ReportUsageErrorASCII(cx, callee, "Argument must be an object"); 9375 return false; 9376 } 9377 9378 RootedObject obj(cx, UncheckedUnwrap(&args[0].toObject())); 9379 obj = ToWindowProxyIfWindow(GetFirstGlobalInCompartment(obj->compartment())); 9380 9381 if (!cx->compartment()->wrap(cx, &obj)) { 9382 return false; 9383 } 9384 9385 args.rval().setObject(*obj); 9386 return true; 9387 } 9388 9389 static bool AssertCorrectRealm(JSContext* cx, unsigned argc, Value* vp) { 9390 CallArgs args = CallArgsFromVp(argc, vp); 9391 MOZ_RELEASE_ASSERT(cx->realm() == args.callee().as<JSFunction>().realm()); 9392 args.rval().setUndefined(); 9393 return true; 9394 } 9395 9396 static bool GlobalLexicals(JSContext* cx, unsigned argc, Value* vp) { 9397 CallArgs args = CallArgsFromVp(argc, vp); 9398 9399 Rooted<GlobalLexicalEnvironmentObject*> globalLexical( 9400 cx, &cx->global()->lexicalEnvironment()); 9401 9402 RootedIdVector props(cx); 9403 if (!GetPropertyKeys(cx, globalLexical, JSITER_HIDDEN, &props)) { 9404 return false; 9405 } 9406 9407 RootedObject res(cx, JS_NewPlainObject(cx)); 9408 if (!res) { 9409 return false; 9410 } 9411 9412 RootedValue val(cx); 9413 for (size_t i = 0; i < props.length(); i++) { 9414 HandleId id = props[i]; 9415 if (!JS_GetPropertyById(cx, globalLexical, id, &val)) { 9416 return false; 9417 } 9418 if (val.isMagic(JS_UNINITIALIZED_LEXICAL)) { 9419 continue; 9420 } 9421 if (!JS_DefinePropertyById(cx, res, id, val, JSPROP_ENUMERATE)) { 9422 return false; 9423 } 9424 } 9425 9426 args.rval().setObject(*res); 9427 return true; 9428 } 9429 9430 static bool EncodeAsUtf8InBuffer(JSContext* cx, unsigned argc, Value* vp) { 9431 CallArgs args = CallArgsFromVp(argc, vp); 9432 if (!args.requireAtLeast(cx, "encodeAsUtf8InBuffer", 2)) { 9433 return false; 9434 } 9435 9436 RootedObject callee(cx, &args.callee()); 9437 9438 if (!args[0].isString()) { 9439 ReportUsageErrorASCII(cx, callee, "First argument must be a String"); 9440 return false; 9441 } 9442 9443 // Create the amounts array early so that the raw pointer into Uint8Array 9444 // data has as short a lifetime as possible 9445 Rooted<ArrayObject*> array(cx, NewDenseFullyAllocatedArray(cx, 2)); 9446 if (!array) { 9447 return false; 9448 } 9449 array->ensureDenseInitializedLength(0, 2); 9450 9451 JSObject* obj = args[1].isObject() ? &args[1].toObject() : nullptr; 9452 Rooted<JS::Uint8Array> view(cx, JS::Uint8Array::unwrap(obj)); 9453 if (!view) { 9454 ReportUsageErrorASCII(cx, callee, "Second argument must be a Uint8Array"); 9455 return false; 9456 } 9457 9458 mozilla::Span<uint8_t> span; 9459 bool isSharedMemory = false; 9460 { 9461 // The hazard analysis does not track the data pointer, so it can neither 9462 // tell that `data` is dead if ReportUsageErrorASCII is called, nor that 9463 // its live range ends at the call to AsWritableChars(). Construct a 9464 // temporary scope to hide from the analysis. This should really be replaced 9465 // with a safer mechanism. 9466 JS::AutoCheckCannotGC nogc(cx); 9467 if (!view.isDetached()) { 9468 span = view.get().getData(&isSharedMemory, nogc); 9469 } 9470 } 9471 9472 if (isSharedMemory || // exclude views of SharedArrayBuffers 9473 !span.data()) { // exclude views of detached ArrayBuffers 9474 ReportUsageErrorASCII( 9475 cx, callee, 9476 "Second argument must be an unshared, non-detached Uint8Array"); 9477 return false; 9478 } 9479 9480 Maybe<std::tuple<size_t, size_t>> amounts = 9481 JS_EncodeStringToUTF8BufferPartial(cx, args[0].toString(), 9482 AsWritableChars(span)); 9483 if (!amounts) { 9484 ReportOutOfMemory(cx); 9485 return false; 9486 } 9487 9488 auto [unitsRead, bytesWritten] = *amounts; 9489 9490 array->initDenseElement(0, Int32Value(AssertedCast<int32_t>(unitsRead))); 9491 array->initDenseElement(1, Int32Value(AssertedCast<int32_t>(bytesWritten))); 9492 9493 args.rval().setObject(*array); 9494 return true; 9495 } 9496 9497 JSScript* js::TestingFunctionArgumentToScript( 9498 JSContext* cx, HandleValue v, JSFunction** funp /* = nullptr */) { 9499 if (v.isString()) { 9500 // To convert a string to a script, compile it. Parse it as an ES6 Program. 9501 Rooted<JSString*> str(cx, v.toString()); 9502 AutoStableStringChars linearChars(cx); 9503 if (!linearChars.initTwoByte(cx, str)) { 9504 return nullptr; 9505 } 9506 SourceText<char16_t> source; 9507 if (!source.initMaybeBorrowed(cx, linearChars)) { 9508 return nullptr; 9509 } 9510 9511 CompileOptions options(cx); 9512 return JS::Compile(cx, options, source); 9513 } 9514 9515 RootedFunction fun(cx, JS_ValueToFunction(cx, v)); 9516 if (!fun) { 9517 return nullptr; 9518 } 9519 9520 if (!fun->isInterpreted()) { 9521 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 9522 JSMSG_TESTING_SCRIPTS_ONLY); 9523 return nullptr; 9524 } 9525 9526 JSScript* script = JSFunction::getOrCreateScript(cx, fun); 9527 if (!script) { 9528 return nullptr; 9529 } 9530 9531 if (funp) { 9532 *funp = fun; 9533 } 9534 9535 return script; 9536 } 9537 9538 static bool BaselineCompile(JSContext* cx, unsigned argc, Value* vp) { 9539 CallArgs args = CallArgsFromVp(argc, vp); 9540 RootedObject callee(cx, &args.callee()); 9541 9542 RootedScript script(cx); 9543 if (args.length() == 0) { 9544 NonBuiltinScriptFrameIter iter(cx); 9545 if (iter.done()) { 9546 ReportUsageErrorASCII(cx, callee, 9547 "no script argument and no script caller"); 9548 return false; 9549 } 9550 script = iter.script(); 9551 } else { 9552 script = TestingFunctionArgumentToScript(cx, args[0]); 9553 if (!script) { 9554 return false; 9555 } 9556 } 9557 9558 bool forceDebug = false; 9559 if (args.length() > 1) { 9560 if (args.length() > 2) { 9561 ReportUsageErrorASCII(cx, callee, "too many arguments"); 9562 return false; 9563 } 9564 if (!args[1].isBoolean() && !args[1].isUndefined()) { 9565 ReportUsageErrorASCII( 9566 cx, callee, "forceDebugInstrumentation argument should be boolean"); 9567 return false; 9568 } 9569 forceDebug = ToBoolean(args[1]); 9570 } 9571 9572 const char* returnedStr = nullptr; 9573 do { 9574 // In order to check for differential behaviour, baselineCompile should have 9575 // the same output whether --no-baseline is used or not. 9576 if (js::SupportDifferentialTesting()) { 9577 returnedStr = "skipped (differential testing)"; 9578 break; 9579 } 9580 9581 AutoRealm ar(cx, script); 9582 if (script->isBaselineCompilingOffThread()) { 9583 CancelOffThreadBaselineCompile(script); 9584 } 9585 9586 if (script->hasBaselineScript()) { 9587 if (forceDebug && !script->baselineScript()->hasDebugInstrumentation()) { 9588 // There isn't an easy way to do this for a script that might be on 9589 // stack right now. See 9590 // js::jit::RecompileOnStackBaselineScriptsForDebugMode. 9591 ReportUsageErrorASCII( 9592 cx, callee, "unsupported case: recompiling script for debug mode"); 9593 return false; 9594 } 9595 9596 args.rval().setUndefined(); 9597 return true; 9598 } 9599 9600 if (!jit::IsBaselineJitEnabled(cx)) { 9601 returnedStr = "baseline disabled"; 9602 break; 9603 } 9604 if (!script->canBaselineCompile()) { 9605 returnedStr = "can't compile"; 9606 break; 9607 } 9608 if (!cx->zone()->ensureJitZoneExists(cx)) { 9609 return false; 9610 } 9611 9612 jit::BaselineOptions options = { 9613 jit::BaselineOption::ForceMainThreadCompilation}; 9614 if (forceDebug) { 9615 options.setFlag(jit::BaselineOption::ForceDebugInstrumentation); 9616 } 9617 jit::MethodStatus status = jit::BaselineCompile(cx, script, options); 9618 switch (status) { 9619 case jit::Method_Error: 9620 return false; 9621 case jit::Method_CantCompile: 9622 returnedStr = "can't compile"; 9623 break; 9624 case jit::Method_Skipped: 9625 returnedStr = "skipped"; 9626 break; 9627 case jit::Method_Compiled: 9628 args.rval().setUndefined(); 9629 } 9630 } while (false); 9631 9632 if (returnedStr) { 9633 return ReturnStringCopy(cx, args, returnedStr); 9634 } 9635 9636 return true; 9637 } 9638 9639 static bool SetBaselineHint(JSContext* cx, unsigned argc, Value* vp) { 9640 CallArgs args = CallArgsFromVp(argc, vp); 9641 RootedObject callee(cx, &args.callee()); 9642 9643 if (args.length() != 1) { 9644 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 9645 return false; 9646 } 9647 9648 RootedScript script(cx, TestingFunctionArgumentToScript(cx, args[0])); 9649 if (!script) { 9650 return false; 9651 } 9652 9653 if (!cx->runtime()->jitRuntime() || 9654 !cx->runtime()->jitRuntime()->hasJitHintsMap()) { 9655 args.rval().setUndefined(); 9656 return true; 9657 } 9658 9659 jit::JitHintsMap* jitHints = cx->runtime()->jitRuntime()->getJitHintsMap(); 9660 jitHints->setEagerBaselineHint(script); 9661 9662 args.rval().setUndefined(); 9663 return true; 9664 } 9665 9666 static bool HasBaselineHint(JSContext* cx, unsigned argc, Value* vp) { 9667 CallArgs args = CallArgsFromVp(argc, vp); 9668 RootedObject callee(cx, &args.callee()); 9669 9670 if (args.length() != 1) { 9671 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 9672 return false; 9673 } 9674 9675 RootedScript script(cx, TestingFunctionArgumentToScript(cx, args[0])); 9676 if (!script) { 9677 return false; 9678 } 9679 9680 if (!cx->runtime()->jitRuntime() || 9681 !cx->runtime()->jitRuntime()->hasJitHintsMap()) { 9682 args.rval().setBoolean(false); 9683 return true; 9684 } 9685 9686 jit::JitHintsMap* jitHints = cx->runtime()->jitRuntime()->getJitHintsMap(); 9687 bool hasHint = jitHints->mightHaveEagerBaselineHint(script); 9688 9689 args.rval().setBoolean(hasHint); 9690 return true; 9691 } 9692 9693 static bool ClearKeptObjects(JSContext* cx, unsigned argc, Value* vp) { 9694 CallArgs args = CallArgsFromVp(argc, vp); 9695 JS::ClearKeptObjects(cx); 9696 args.rval().setUndefined(); 9697 return true; 9698 } 9699 9700 static bool NumberToDouble(JSContext* cx, unsigned argc, Value* vp) { 9701 CallArgs args = CallArgsFromVp(argc, vp); 9702 if (!args.requireAtLeast(cx, "numberToDouble", 1)) { 9703 return false; 9704 } 9705 9706 if (!args[0].isNumber()) { 9707 RootedObject callee(cx, &args.callee()); 9708 ReportUsageErrorASCII(cx, callee, "argument must be a number"); 9709 return false; 9710 } 9711 9712 args.rval().setDouble(args[0].toNumber()); 9713 return true; 9714 } 9715 9716 static bool GetICUOptions(JSContext* cx, unsigned argc, Value* vp) { 9717 CallArgs args = CallArgsFromVp(argc, vp); 9718 9719 RootedObject info(cx, JS_NewPlainObject(cx)); 9720 if (!info) { 9721 return false; 9722 } 9723 9724 #ifdef JS_HAS_INTL_API 9725 RootedString str(cx); 9726 9727 str = NewStringCopy<CanGC>(cx, mozilla::intl::ICU4CLibrary::GetVersion()); 9728 if (!str || !JS_DefineProperty(cx, info, "version", str, JSPROP_ENUMERATE)) { 9729 return false; 9730 } 9731 9732 str = NewStringCopy<CanGC>(cx, mozilla::intl::String::GetUnicodeVersion()); 9733 if (!str || !JS_DefineProperty(cx, info, "unicode", str, JSPROP_ENUMERATE)) { 9734 return false; 9735 } 9736 9737 str = NewStringCopyZ<CanGC>(cx, mozilla::intl::Locale::GetDefaultLocale()); 9738 if (!str || !JS_DefineProperty(cx, info, "locale", str, JSPROP_ENUMERATE)) { 9739 return false; 9740 } 9741 9742 auto tzdataVersion = mozilla::intl::TimeZone::GetTZDataVersion(); 9743 if (tzdataVersion.isErr()) { 9744 intl::ReportInternalError(cx, tzdataVersion.unwrapErr()); 9745 return false; 9746 } 9747 9748 str = NewStringCopy<CanGC>(cx, tzdataVersion.unwrap()); 9749 if (!str || !JS_DefineProperty(cx, info, "tzdata", str, JSPROP_ENUMERATE)) { 9750 return false; 9751 } 9752 9753 TimeZoneIdentifierVector timeZoneId; 9754 if (!DateTimeInfo::timeZoneId(nullptr, timeZoneId)) { 9755 ReportOutOfMemory(cx); 9756 return false; 9757 } 9758 9759 str = NewStringCopy<CanGC>( 9760 cx, static_cast<mozilla::Span<const char>>(timeZoneId)); 9761 if (!str || !JS_DefineProperty(cx, info, "timezone", str, JSPROP_ENUMERATE)) { 9762 return false; 9763 } 9764 9765 intl::FormatBuffer<char16_t, intl::INITIAL_CHAR_BUFFER_SIZE> buf(cx); 9766 if (auto ok = mozilla::intl::TimeZone::GetHostTimeZone(buf); ok.isErr()) { 9767 intl::ReportInternalError(cx, ok.unwrapErr()); 9768 return false; 9769 } 9770 9771 str = buf.toString(cx); 9772 if (!str || 9773 !JS_DefineProperty(cx, info, "host-timezone", str, JSPROP_ENUMERATE)) { 9774 return false; 9775 } 9776 #endif 9777 9778 args.rval().setObject(*info); 9779 return true; 9780 } 9781 9782 static bool GetAvailableLocalesOf(JSContext* cx, unsigned argc, Value* vp) { 9783 CallArgs args = CallArgsFromVp(argc, vp); 9784 RootedObject callee(cx, &args.callee()); 9785 9786 if (!args.requireAtLeast(cx, "getAvailableLocalesOf", 1)) { 9787 return false; 9788 } 9789 9790 HandleValue arg = args[0]; 9791 if (!arg.isString()) { 9792 ReportUsageErrorASCII(cx, callee, "First argument must be a string"); 9793 return false; 9794 } 9795 9796 ArrayObject* result; 9797 #ifdef JS_HAS_INTL_API 9798 using AvailableLocaleKind = js::intl::AvailableLocaleKind; 9799 9800 AvailableLocaleKind kind; 9801 { 9802 JSLinearString* typeStr = arg.toString()->ensureLinear(cx); 9803 if (!typeStr) { 9804 return false; 9805 } 9806 9807 if (StringEqualsLiteral(typeStr, "Collator")) { 9808 kind = AvailableLocaleKind::Collator; 9809 } else if (StringEqualsLiteral(typeStr, "DateTimeFormat")) { 9810 kind = AvailableLocaleKind::DateTimeFormat; 9811 } else if (StringEqualsLiteral(typeStr, "DisplayNames")) { 9812 kind = AvailableLocaleKind::DisplayNames; 9813 } else if (StringEqualsLiteral(typeStr, "DurationFormat")) { 9814 kind = AvailableLocaleKind::DurationFormat; 9815 } else if (StringEqualsLiteral(typeStr, "ListFormat")) { 9816 kind = AvailableLocaleKind::ListFormat; 9817 } else if (StringEqualsLiteral(typeStr, "NumberFormat")) { 9818 kind = AvailableLocaleKind::NumberFormat; 9819 } else if (StringEqualsLiteral(typeStr, "PluralRules")) { 9820 kind = AvailableLocaleKind::PluralRules; 9821 } else if (StringEqualsLiteral(typeStr, "RelativeTimeFormat")) { 9822 kind = AvailableLocaleKind::RelativeTimeFormat; 9823 } else if (StringEqualsLiteral(typeStr, "Segmenter")) { 9824 kind = AvailableLocaleKind::Segmenter; 9825 } else { 9826 ReportUsageErrorASCII(cx, callee, "Unsupported Intl constructor name"); 9827 return false; 9828 } 9829 } 9830 9831 intl::SharedIntlData& sharedIntlData = cx->runtime()->sharedIntlData.ref(); 9832 result = sharedIntlData.availableLocalesOf(cx, kind); 9833 #else 9834 result = NewDenseEmptyArray(cx); 9835 #endif 9836 if (!result) { 9837 return false; 9838 } 9839 9840 args.rval().setObject(*result); 9841 return true; 9842 } 9843 9844 static bool IsSmallFunction(JSContext* cx, unsigned argc, Value* vp) { 9845 CallArgs args = CallArgsFromVp(argc, vp); 9846 RootedObject callee(cx, &args.callee()); 9847 9848 if (!args.requireAtLeast(cx, "IsSmallFunction", 1)) { 9849 return false; 9850 } 9851 9852 HandleValue arg = args[0]; 9853 if (!arg.isObject() || !arg.toObject().is<JSFunction>()) { 9854 ReportUsageErrorASCII(cx, callee, "First argument must be a function"); 9855 return false; 9856 } 9857 9858 RootedFunction fun(cx, &args[0].toObject().as<JSFunction>()); 9859 if (!fun->isInterpreted()) { 9860 ReportUsageErrorASCII(cx, callee, 9861 "First argument must be an interpreted function"); 9862 return false; 9863 } 9864 9865 JSScript* script = JSFunction::getOrCreateScript(cx, fun); 9866 if (!script) { 9867 return false; 9868 } 9869 9870 args.rval().setBoolean(jit::JitOptions.isSmallFunction(script)); 9871 return true; 9872 } 9873 9874 static bool PCCountProfiling_Start(JSContext* cx, unsigned argc, Value* vp) { 9875 CallArgs args = CallArgsFromVp(argc, vp); 9876 9877 JS::StartPCCountProfiling(cx); 9878 9879 args.rval().setUndefined(); 9880 return true; 9881 } 9882 9883 static bool PCCountProfiling_Stop(JSContext* cx, unsigned argc, Value* vp) { 9884 CallArgs args = CallArgsFromVp(argc, vp); 9885 9886 JS::StopPCCountProfiling(cx); 9887 9888 args.rval().setUndefined(); 9889 return true; 9890 } 9891 9892 static bool PCCountProfiling_Purge(JSContext* cx, unsigned argc, Value* vp) { 9893 CallArgs args = CallArgsFromVp(argc, vp); 9894 9895 JS::PurgePCCounts(cx); 9896 9897 args.rval().setUndefined(); 9898 return true; 9899 } 9900 9901 static bool PCCountProfiling_ScriptCount(JSContext* cx, unsigned argc, 9902 Value* vp) { 9903 CallArgs args = CallArgsFromVp(argc, vp); 9904 9905 size_t length = JS::GetPCCountScriptCount(cx); 9906 9907 args.rval().setNumber(double(length)); 9908 return true; 9909 } 9910 9911 static bool PCCountProfiling_ScriptSummary(JSContext* cx, unsigned argc, 9912 Value* vp) { 9913 CallArgs args = CallArgsFromVp(argc, vp); 9914 if (!args.requireAtLeast(cx, "summary", 1)) { 9915 return false; 9916 } 9917 9918 uint32_t index; 9919 if (!JS::ToUint32(cx, args[0], &index)) { 9920 return false; 9921 } 9922 9923 JSString* str = JS::GetPCCountScriptSummary(cx, index); 9924 if (!str) { 9925 return false; 9926 } 9927 9928 args.rval().setString(str); 9929 return true; 9930 } 9931 9932 static bool PCCountProfiling_ScriptContents(JSContext* cx, unsigned argc, 9933 Value* vp) { 9934 CallArgs args = CallArgsFromVp(argc, vp); 9935 if (!args.requireAtLeast(cx, "contents", 1)) { 9936 return false; 9937 } 9938 9939 uint32_t index; 9940 if (!JS::ToUint32(cx, args[0], &index)) { 9941 return false; 9942 } 9943 9944 JSString* str = JS::GetPCCountScriptContents(cx, index); 9945 if (!str) { 9946 return false; 9947 } 9948 9949 args.rval().setString(str); 9950 return true; 9951 } 9952 9953 static bool NukeCCW(JSContext* cx, unsigned argc, Value* vp) { 9954 CallArgs args = CallArgsFromVp(argc, vp); 9955 9956 if (args.length() != 1 || !args[0].isObject() || 9957 !IsCrossCompartmentWrapper(&args[0].toObject())) { 9958 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INVALID_ARGS, 9959 "nukeCCW"); 9960 return false; 9961 } 9962 9963 NukeCrossCompartmentWrapper(cx, &args[0].toObject()); 9964 args.rval().setUndefined(); 9965 return true; 9966 } 9967 9968 static bool IsCCW(JSContext* cx, unsigned argc, Value* vp) { 9969 CallArgs args = CallArgsFromVp(argc, vp); 9970 9971 if (args.length() != 1 || !args[0].isObject()) { 9972 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INVALID_ARGS, 9973 "IsCCW"); 9974 return false; 9975 } 9976 9977 args.rval().setBoolean(IsCrossCompartmentWrapper(&args[0].toObject())); 9978 return true; 9979 } 9980 9981 static bool FdLibM_Pow(JSContext* cx, unsigned argc, Value* vp) { 9982 CallArgs args = CallArgsFromVp(argc, vp); 9983 9984 double x; 9985 if (!JS::ToNumber(cx, args.get(0), &x)) { 9986 return false; 9987 } 9988 9989 double y; 9990 if (!JS::ToNumber(cx, args.get(1), &y)) { 9991 return false; 9992 } 9993 9994 // Because C99 and ECMA specify different behavior for pow(), we need to wrap 9995 // the fdlibm call to make it ECMA compliant. 9996 if (!std::isfinite(y) && (x == 1.0 || x == -1.0)) { 9997 args.rval().setNaN(); 9998 } else { 9999 args.rval().setDouble(fdlibm_pow(x, y)); 10000 } 10001 return true; 10002 } 10003 10004 static bool HadOutOfMemory(JSContext* cx, unsigned argc, Value* vp) { 10005 CallArgs args = CallArgsFromVp(argc, vp); 10006 args.rval().setBoolean(cx->runtime()->hadOutOfMemory); 10007 return true; 10008 } 10009 10010 static bool WaitForDone(JSContext* cx, unsigned argc, Value* vp) { 10011 CallArgs args = CallArgsFromVp(argc, vp); 10012 if (args.length() != 1) { 10013 JS_ReportErrorASCII(cx, "The function takes exactly one argument."); 10014 return false; 10015 } 10016 10017 if (!args[0].isObject()) { 10018 JS_ReportErrorASCII(cx, "The first argument should be an object."); 10019 return false; 10020 } 10021 10022 RootedObject obj(cx, &args[0].toObject()); 10023 RootedValue val(cx); 10024 if (!JS_GetProperty(cx, obj, "done", &val)) { 10025 return false; 10026 } 10027 10028 args.rval().setUndefined(); 10029 10030 while (!val.isBoolean() || !val.toBoolean()) { 10031 js::RunJobs(cx); 10032 if (!JS_GetProperty(cx, obj, "done", &val)) { 10033 return false; 10034 } 10035 } 10036 return true; 10037 } 10038 10039 static bool TestingFunc_SupportDifferentialTesting(JSContext* cx, unsigned argc, 10040 Value* vp) { 10041 CallArgs args = CallArgsFromVp(argc, vp); 10042 args.rval().setBoolean(SupportDifferentialTesting()); 10043 return true; 10044 } 10045 10046 static bool GetLastOOMStackTrace(JSContext* cx, unsigned argc, Value* vp) { 10047 CallArgs args = CallArgsFromVp(argc, vp); 10048 10049 if (!cx->hasOOMStackTrace()) { 10050 args.rval().setNull(); 10051 return true; 10052 } 10053 10054 const char* stackTrace = cx->getOOMStackTrace(); 10055 MOZ_ASSERT(stackTrace); 10056 10057 // The stackTrace persists, so we can clear the flag here in case copying the 10058 // string fails 10059 cx->unsetOOMStackTrace(); 10060 10061 JSString* str = JS_NewStringCopyZ(cx, stackTrace); 10062 if (!str) { 10063 return false; 10064 } 10065 10066 args.rval().setString(str); 10067 return true; 10068 } 10069 10070 // clang-format off 10071 static const JSFunctionSpecWithHelp TestingFunctions[] = { 10072 JS_FN_HELP("gc", ::GC, 0, 0, 10073 "gc([obj] | 'zone' [, ('shrinking' | 'last-ditch') ])", 10074 " Run the garbage collector.\n" 10075 " The first parameter describes which zones to collect: if an object is\n" 10076 " given, GC only its zone. If 'zone' is given, GC any zones that were\n" 10077 " scheduled via schedulegc.\n" 10078 " The second parameter is optional and may be 'shrinking' to perform a\n" 10079 " shrinking GC or 'last-ditch' for a shrinking, last-ditch GC."), 10080 10081 JS_FN_HELP("minorgc", ::MinorGC, 0, 0, 10082 "minorgc([aboutToOverflow])", 10083 " Run a minor collector on the Nursery. When aboutToOverflow is true, marks\n" 10084 " the store buffer as about-to-overflow before collecting."), 10085 10086 JS_FN_HELP("maybegc", ::MaybeGC, 0, 0, 10087 "maybegc()", 10088 " Hint to the engine that now is an ok time to run the garbage collector.\n"), 10089 10090 JS_FN_HELP("gcparam", GCParameter, 2, 0, 10091 "gcparam(name [, value])", 10092 " Wrapper for JS_[GS]etGCParameter. The name is one of:" GC_PARAMETER_ARGS_LIST), 10093 10094 JS_FN_HELP("finishBackgroundFree", FinishBackgroundFree, 0, 0, 10095 "finishBackgroundFree()", 10096 " Wait for the GC's background free task to finish.\n"), 10097 10098 JS_FN_HELP("hasDisassembler", HasDisassembler, 0, 0, 10099 "hasDisassembler()", 10100 " Return true if a disassembler is present (for disnative and wasmDis)."), 10101 10102 JS_FN_HELP("disnative", DisassembleNative, 2, 0, 10103 "disnative(fun,[path])", 10104 " Disassemble a function into its native code. Optionally write the native code bytes to a file on disk.\n"), 10105 10106 JS_FN_HELP("disblic", DisassembleBaselineICs, 1, 0, 10107 "disblic(fun)", 10108 " Disassemble the baseline ICs for a function into native code.\n"), 10109 10110 JS_FN_HELP("relazifyFunctions", RelazifyFunctions, 0, 0, 10111 "relazifyFunctions(...)", 10112 " Perform a GC and allow relazification of functions. Accepts the same\n" 10113 " arguments as gc()."), 10114 10115 JS_FN_HELP("getBuildConfiguration", GetBuildConfiguration, 1, 0, 10116 "getBuildConfiguration([option])", 10117 " Query the options SpiderMonkey was built with, or return an object\n" 10118 " with the options if no argument is given."), 10119 10120 JS_FN_HELP("getRealmConfiguration", GetRealmConfiguration, 1, 0, 10121 "getRealmConfiguration([option])", 10122 " Query the runtime options SpiderMonkey is running with, or return an\n." 10123 " object with the options if no argument is given."), 10124 10125 JS_FN_HELP("isLcovEnabled", ::IsLCovEnabled, 0, 0, 10126 "isLcovEnabled()", 10127 " Return true if JS LCov support is enabled."), 10128 10129 JS_FN_HELP("trialInline", TrialInline, 0, 0, 10130 "trialInline()", 10131 " Perform trial-inlining for the caller's frame if it's a BaselineFrame."), 10132 10133 JS_FN_HELP("hasChild", HasChild, 0, 0, 10134 "hasChild(parent, child)", 10135 " Return true if |child| is a child of |parent|, as determined by a call to\n" 10136 " TraceChildren"), 10137 10138 JS_FN_HELP("setSavedStacksRNGState", SetSavedStacksRNGState, 1, 0, 10139 "setSavedStacksRNGState(seed)", 10140 " Set this compartment's SavedStacks' RNG state.\n"), 10141 10142 JS_FN_HELP("getSavedFrameCount", GetSavedFrameCount, 0, 0, 10143 "getSavedFrameCount()", 10144 " Return the number of SavedFrame instances stored in this compartment's\n" 10145 " SavedStacks cache."), 10146 10147 JS_FN_HELP("clearSavedFrames", ClearSavedFrames, 0, 0, 10148 "clearSavedFrames()", 10149 " Empty the current compartment's cache of SavedFrame objects, so that\n" 10150 " subsequent stack captures allocate fresh objects to represent frames.\n" 10151 " Clear the current stack's LiveSavedFrameCaches."), 10152 10153 JS_FN_HELP("saveStack", SaveStack, 0, 0, 10154 "saveStack([maxDepth [, compartment]])", 10155 " Capture a stack. If 'maxDepth' is given, capture at most 'maxDepth' number\n" 10156 " of frames. If 'compartment' is given, allocate the js::SavedFrame instances\n" 10157 " with the given object's compartment."), 10158 10159 JS_FN_HELP("captureFirstSubsumedFrame", CaptureFirstSubsumedFrame, 1, 0, 10160 "saveStack(object [, shouldIgnoreSelfHosted = true]])", 10161 " Capture a stack back to the first frame whose principals are subsumed by the\n" 10162 " object's compartment's principals. If 'shouldIgnoreSelfHosted' is given,\n" 10163 " control whether self-hosted frames are considered when checking principals."), 10164 10165 JS_FN_HELP("callFunctionFromNativeFrame", CallFunctionFromNativeFrame, 1, 0, 10166 "callFunctionFromNativeFrame(function)", 10167 " Call 'function' with a (C++-)native frame on stack.\n" 10168 " Required for testing that SaveStack properly handles native frames."), 10169 10170 JS_FN_HELP("callFunctionWithAsyncStack", CallFunctionWithAsyncStack, 0, 0, 10171 "callFunctionWithAsyncStack(function, stack, asyncCause)", 10172 " Call 'function', using the provided stack as the async stack responsible\n" 10173 " for the call, and propagate its return value or the exception it throws.\n" 10174 " The function is called with no arguments, and 'this' is 'undefined'. The\n" 10175 " specified |asyncCause| is attached to the provided stack frame."), 10176 10177 JS_FN_HELP("enableTrackAllocations", EnableTrackAllocations, 0, 0, 10178 "enableTrackAllocations()", 10179 " Start capturing the JS stack at every allocation. Note that this sets an\n" 10180 " object metadata callback that will override any other object metadata\n" 10181 " callback that may be set."), 10182 10183 JS_FN_HELP("disableTrackAllocations", DisableTrackAllocations, 0, 0, 10184 "disableTrackAllocations()", 10185 " Stop capturing the JS stack at every allocation."), 10186 10187 JS_FN_HELP("setTestFilenameValidationCallback", SetTestFilenameValidationCallback, 0, 0, 10188 "setTestFilenameValidationCallback()", 10189 " Set the filename validation callback to a callback that accepts only\n" 10190 " filenames starting with 'safe' or (only in system realms) 'system'."), 10191 10192 JS_FN_HELP("newObjectWithAddPropertyHook", NewObjectWithAddPropertyHook, 0, 0, 10193 "newObjectWithAddPropertyHook()", 10194 " Returns a new object with an addProperty JSClass hook. This hook\n" 10195 " increments the value of the _propertiesAdded data property on the object\n" 10196 " when a new property is added."), 10197 10198 JS_FN_HELP("newObjectWithCallHook", NewObjectWithCallHook, 0, 0, 10199 "newObjectWithCallHook()", 10200 " Returns a new object with call/construct JSClass hooks. These hooks return\n" 10201 " a new object that contains the Values supplied by the caller."), 10202 10203 JS_FN_HELP("newObjectWithManyReservedSlots", NewObjectWithManyReservedSlots, 0, 0, 10204 "newObjectWithManyReservedSlots()", 10205 " Returns a new object with many reserved slots. The slots are initialized to int32\n" 10206 " values. checkObjectWithManyReservedSlots can be used to check the slots still\n" 10207 " hold these values."), 10208 10209 JS_FN_HELP("checkObjectWithManyReservedSlots", CheckObjectWithManyReservedSlots, 1, 0, 10210 "checkObjectWithManyReservedSlots(obj)", 10211 " Checks the reserved slots set by newObjectWithManyReservedSlots still hold the expected\n" 10212 " values."), 10213 10214 JS_FN_HELP("addObjectFuse", AddObjectFuse, 1, 0, 10215 "addObjectFuse(object)", 10216 " Mark 'object' as having an ObjectFuse."), 10217 10218 JS_FN_HELP("getObjectFuseState", GetObjectFuseState, 1, 0, 10219 "getObjectFuseState(object)", 10220 " Returns a new object that contains information about the ObjectFuse of 'object'."), 10221 10222 JS_FN_HELP("getWatchtowerLog", GetWatchtowerLog, 0, 0, 10223 "getWatchtowerLog()", 10224 " Returns the Watchtower log recording object changes for objects for which\n" 10225 " addWatchtowerTarget was called. The internal log is cleared. The return\n" 10226 " value is an array of plain objects with the following properties:\n" 10227 " - kind: a string describing the kind of mutation, for example \"add-prop\"\n" 10228 " - object: the object being mutated\n" 10229 " - extra: an extra value, for example the name of the property being added"), 10230 10231 JS_FN_HELP("addWatchtowerTarget", AddWatchtowerTarget, 1, 0, 10232 "addWatchtowerTarget(object)", 10233 " Invoke the watchtower callback for changes to this object."), 10234 10235 JS_FN_HELP("newString", NewString, 2, 0, 10236 "newString(str[, options])", 10237 " Copies str's chars and returns a new string. Valid options:\n" 10238 " \n" 10239 " - tenured: allocate directly into the tenured heap.\n" 10240 " \n" 10241 " - twoByte: create a \"two byte\" string, not a latin1 string, regardless of the\n" 10242 " input string's characters. Latin1 will be used by default if possible\n" 10243 " (again regardless of the input string.)\n" 10244 " \n" 10245 " - newStringBuffer: create a new string that uses a refcounted StringBuffer for\n" 10246 " the characters.\n" 10247 " \n" 10248 " - shareStringBuffer: create a new string that shares str's StringBuffer.\n" 10249 " \n" 10250 " - external: create an external string. External strings are always twoByte and\n" 10251 " tenured.\n" 10252 " \n" 10253 " - maybeExternal: create an external string, unless the data fits within an\n" 10254 " inline string. Inline strings may be nursery-allocated.\n" 10255 " \n" 10256 " - capacity: create an extensible string with the given capacity"), 10257 10258 JS_FN_HELP("newDependentString", NewDependentString, 2, 0, 10259 "newDependentString(str, indexStart[, indexEnd] [, options])", 10260 " Essentially the same as str.substring() but insist on\n" 10261 " creating a dependent string and failing if not. Also has options to\n" 10262 " control the heap the string object is allocated into:\n" 10263 " \n" 10264 " - tenured: if true, allocate in the tenured heap or throw. If false,\n" 10265 " allocate in the nursery or throw.\n" 10266 " - suppress-contraction: prevent the optimization of using a base's base rather\n" 10267 " than creating a chain of dependent string bases."), 10268 10269 JS_FN_HELP("ensureLinearString", EnsureLinearString, 1, 0, 10270 "ensureLinearString(str)", 10271 " Ensures str is a linear (non-rope) string and returns it."), 10272 10273 JS_FN_HELP("representativeStringArray", RepresentativeStringArray, 0, 0, 10274 "representativeStringArray()", 10275 " Returns an array of strings that represent the various internal string\n" 10276 " types and character encodings."), 10277 10278 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT) 10279 10280 JS_FN_HELP("oomThreadTypes", OOMThreadTypes, 0, 0, 10281 "oomThreadTypes()", 10282 " Get the number of thread types that can be used as an argument for\n" 10283 " oomAfterAllocations() and oomAtAllocation()."), 10284 10285 JS_FN_HELP("oomAfterAllocations", OOMAfterAllocations, 2, 0, 10286 "oomAfterAllocations(count [,threadType])", 10287 " After 'count' js_malloc memory allocations, fail every following allocation\n" 10288 " (return nullptr). The optional thread type limits the effect to the\n" 10289 " specified type of helper thread."), 10290 10291 JS_FN_HELP("oomAtAllocation", OOMAtAllocation, 2, 0, 10292 "oomAtAllocation(count [,threadType])", 10293 " After 'count' js_malloc memory allocations, fail the next allocation\n" 10294 " (return nullptr). The optional thread type limits the effect to the\n" 10295 " specified type of helper thread."), 10296 10297 JS_FN_HELP("resetOOMFailure", ResetOOMFailure, 0, 0, 10298 "resetOOMFailure()", 10299 " Remove the allocation failure scheduled by either oomAfterAllocations() or\n" 10300 " oomAtAllocation() and return whether any allocation had been caused to fail."), 10301 10302 JS_FN_HELP("oomTest", OOMTest, 0, 0, 10303 "oomTest(function, [expectExceptionOnFailure = true | options])", 10304 " Test that the passed function behaves correctly under OOM conditions by\n" 10305 " repeatedly executing it and simulating allocation failure at successive\n" 10306 " allocations until the function completes without seeing a failure.\n" 10307 " By default this tests that an exception is raised if execution fails, but\n" 10308 " this can be disabled by passing false as the optional second parameter.\n" 10309 " This is also disabled when --fuzzing-safe is specified.\n" 10310 " Alternatively an object can be passed to set the following options:\n" 10311 " expectExceptionOnFailure: bool - as described above.\n" 10312 " keepFailing: bool - continue to fail after first simulated failure.\n" 10313 "\n" 10314 " WARNING: By design, oomTest assumes the test-function follows the same\n" 10315 " code path each time it is called, right up to the point where OOM occurs.\n" 10316 " If on iteration 70 it finishes and caches a unit of work that saves 65\n" 10317 " allocations the next time we run, then the subsequent 65 allocation\n" 10318 " points will go untested.\n" 10319 "\n" 10320 " Things in this category include lazy parsing and baseline compilation,\n" 10321 " so it is very easy to accidentally write an oomTest that only tests one\n" 10322 " or the other of those, and not the functionality you meant to test!\n" 10323 " To avoid lazy parsing, call the test function once first before passing\n" 10324 " it to oomTest. The jits can be disabled via the test harness.\n"), 10325 10326 JS_FN_HELP("stackTest", StackTest, 0, 0, 10327 "stackTest(function, [expectExceptionOnFailure = true])", 10328 " This function behaves exactly like oomTest with the difference that\n" 10329 " instead of simulating regular OOM conditions, it simulates the engine\n" 10330 " running out of stack space (failing recursion check).\n" 10331 "\n" 10332 " See the WARNING in help('oomTest').\n"), 10333 10334 JS_FN_HELP("interruptTest", InterruptTest, 0, 0, 10335 "interruptTest(function)", 10336 " This function simulates interrupts similar to how oomTest simulates OOM conditions." 10337 "\n" 10338 " See the WARNING in help('oomTest').\n"), 10339 10340 #endif // defined(DEBUG) || defined(JS_OOM_BREAKPOINT) 10341 10342 JS_FN_HELP("newRope", NewRope, 3, 0, 10343 "newRope(left, right[, options])", 10344 " Creates a rope with the given left/right strings.\n" 10345 " Available options:\n" 10346 " nursery: bool - force the string to be created in/out of the nursery, if possible.\n"), 10347 10348 JS_FN_HELP("isRope", IsRope, 1, 0, 10349 "isRope(str)", 10350 " Returns true if the parameter is a rope"), 10351 10352 JS_FN_HELP("settlePromiseNow", SettlePromiseNow, 1, 0, 10353 "settlePromiseNow(promise)", 10354 " 'Settle' a 'promise' immediately. This just marks the promise as resolved\n" 10355 " with a value of `undefined` and causes the firing of any onPromiseSettled\n" 10356 " hooks set on Debugger instances that are observing the given promise's\n" 10357 " global as a debuggee."), 10358 JS_FN_HELP("getWaitForAllPromise", GetWaitForAllPromise, 1, 0, 10359 "getWaitForAllPromise(densePromisesArray)", 10360 " Calls the 'GetWaitForAllPromise' JSAPI function and returns the result\n" 10361 " Promise."), 10362 JS_FN_HELP("resolvePromise", ResolvePromise, 2, 0, 10363 "resolvePromise(promise, resolution)", 10364 " Resolve a Promise by calling the JSAPI function JS::ResolvePromise."), 10365 JS_FN_HELP("rejectPromise", RejectPromise, 2, 0, 10366 "rejectPromise(promise, reason)", 10367 " Reject a Promise by calling the JSAPI function JS::RejectPromise."), 10368 10369 JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver, 0, 0, 10370 "makeFinalizeObserver()", 10371 " Get a special object whose finalization increases the counter returned\n" 10372 " by the finalizeCount function."), 10373 10374 JS_FN_HELP("finalizeCount", FinalizeCount, 0, 0, 10375 "finalizeCount()", 10376 " Return the current value of the finalization counter that is incremented\n" 10377 " each time an object returned by the makeFinalizeObserver is finalized."), 10378 10379 JS_FN_HELP("resetFinalizeCount", ResetFinalizeCount, 0, 0, 10380 "resetFinalizeCount()", 10381 " Reset the value returned by finalizeCount()."), 10382 10383 JS_FN_HELP("gcPreserveCode", GCPreserveCode, 0, 0, 10384 "gcPreserveCode()", 10385 " Preserve JIT code during garbage collections."), 10386 10387 #ifdef JS_GC_ZEAL 10388 JS_FN_HELP("gczeal", GCZeal, 2, 0, 10389 "gczeal([mode, [frequency]])", 10390 gc::ZealModeHelpText), 10391 10392 JS_FN_HELP("unsetgczeal", UnsetGCZeal, 2, 0, 10393 "unsetgczeal(mode)", 10394 " Turn off the mode or modes given that were set with gczeal() or JS_GC_ZEAL\n" 10395 " and don't finish any ongoing collection that may be happening."), 10396 10397 JS_FN_HELP("schedulegc", ScheduleGC, 1, 0, 10398 "schedulegc([num])", 10399 " If num is given, schedule a GC after num allocations.\n" 10400 " Returns the number of allocations before the next trigger."), 10401 10402 JS_FN_HELP("selectforgc", SelectForGC, 0, 0, 10403 "selectforgc(obj1, obj2, ...)", 10404 " Schedule the given objects to be marked in the next GC slice."), 10405 10406 JS_FN_HELP("verifyprebarriers", VerifyPreBarriers, 0, 0, 10407 "verifyprebarriers()", 10408 " Start or end a run of the pre-write barrier verifier."), 10409 10410 JS_FN_HELP("verifypostbarriers", VerifyPostBarriers, 0, 0, 10411 "verifypostbarriers()", 10412 " Enable or disable the post-write barrier verifier."), 10413 10414 JS_FN_HELP("currentgc", CurrentGC, 0, 0, 10415 "currentgc()", 10416 " Report various information about the currently running incremental GC,\n" 10417 " if one is running."), 10418 10419 JS_FN_HELP("deterministicgc", DeterministicGC, 1, 0, 10420 "deterministicgc(true|false)", 10421 " If true, only allow determinstic GCs to run."), 10422 10423 JS_FN_HELP("dumpGCArenaInfo", DumpGCArenaInfo, 0, 0, 10424 "dumpGCArenaInfo()", 10425 " Prints information about the different GC things and how they are arranged\n" 10426 " in arenas.\n"), 10427 10428 JS_FN_HELP("setMarkStackLimit", SetMarkStackLimit, 1, 0, 10429 "markStackLimit(limit)", 10430 " Sets a limit on the number of words used for the mark stack. Used to test OOM" 10431 " handling during marking.\n"), 10432 10433 #endif 10434 10435 JS_FN_HELP("gcstate", GCState, 0, 0, 10436 "gcstate([obj])", 10437 " Report the global GC state, or the GC state for the zone containing |obj|."), 10438 10439 JS_FN_HELP("schedulezone", ScheduleZoneForGC, 1, 0, 10440 "schedulezone([obj | string])", 10441 " If obj is given, schedule a GC of obj's zone.\n" 10442 " If string is given, schedule a GC of the string's zone if possible."), 10443 10444 JS_FN_HELP("startgc", StartGC, 1, 0, 10445 "startgc([n [, 'shrinking']])", 10446 " Start an incremental GC and run a slice that processes about n objects.\n" 10447 " If 'shrinking' is passesd as the optional second argument, perform a\n" 10448 " shrinking GC rather than a normal GC. If no zones have been selected with\n" 10449 " schedulezone(), a full GC will be performed."), 10450 10451 JS_FN_HELP("finishgc", FinishGC, 0, 0, 10452 "finishgc()", 10453 " Finish an in-progress incremental GC, if none is running then do nothing."), 10454 10455 JS_FN_HELP("gcslice", GCSlice, 1, 0, 10456 "gcslice([n [, options]])", 10457 " Start or continue an an incremental GC, running a slice that processes\n" 10458 " about n objects. Takes an optional options object, which may contain the\n" 10459 " following properties:\n" 10460 " dontStart: do not start a new incremental GC if one is not already\n" 10461 " running"), 10462 10463 JS_FN_HELP("abortgc", AbortGC, 1, 0, 10464 "abortgc()", 10465 " Abort the current incremental GC."), 10466 10467 JS_FN_HELP("isAtomMarked", IsAtomMarked, 2, 0, 10468 "isAtomMarked(obj, atom)", 10469 " Return whether |atom| is marked relative to the zone containing |obj|."), 10470 10471 JS_FN_HELP("getAtomMarkIndex", GetAtomMarkIndex, 1, 0, 10472 "getAtomMarkIndex(atom)", 10473 " Return the atom marking bitmap's index for |atom|."), 10474 10475 JS_FN_HELP("getAtomMarkColor", GetAtomMarkColor, 2, 0, 10476 "getAtomMarkColor(obj, index)", 10477 " Return the atom marking bitmap's mark color for |index| relative to the zone containing |obj|."), 10478 10479 JS_FN_HELP("setMallocMaxDirtyPageModifier", SetMallocMaxDirtyPageModifier, 1, 0, 10480 "setMallocMaxDirtyPageModifier(value)", 10481 " Change the maximum size of jemalloc's page cache. The value should be between\n" 10482 " -5 and 16 (inclusive). See moz_set_max_dirty_page_modifier.\n"), 10483 10484 JS_FN_HELP("fullcompartmentchecks", FullCompartmentChecks, 1, 0, 10485 "fullcompartmentchecks(true|false)", 10486 " If true, check for compartment mismatches before every GC."), 10487 10488 JS_FN_HELP("nondeterministicGetWeakMapSize", NondeterministicGetWeakMapSize, 1, 0, 10489 "nondeterministicGetWeakMapSize(weakmap)", 10490 " Returns the number of entries in the given WeakMap."), 10491 10492 JS_FN_HELP("nondeterministicGetWeakMapKeys", NondeterministicGetWeakMapKeys, 1, 0, 10493 "nondeterministicGetWeakMapKeys(weakmap)", 10494 " Return an array of the keys in the given WeakMap."), 10495 10496 JS_FN_HELP("grayBitsValid", GrayBitsValid, 0, 0, 10497 "grayBitsValid()", 10498 " Return whether the gray bits state is valid."), 10499 10500 JS_FN_HELP("setGrayBitsInvalid", SetGrayBitsInvalid, 0, 0, 10501 "setGrayBitsInvalid()", 10502 " Set the gray bits state to invalid."), 10503 10504 JS_FN_HELP("internalConst", InternalConst, 1, 0, 10505 "internalConst(name)", 10506 " Query an internal constant for the engine. See InternalConst source for\n" 10507 " the list of constant names."), 10508 10509 JS_FN_HELP("isProxy", IsProxy, 1, 0, 10510 "isProxy(obj)", 10511 " If true, obj is a proxy of some sort"), 10512 10513 JS_FN_HELP("dumpHeap", DumpHeap, 1, 0, 10514 "dumpHeap([filename])", 10515 " Dump reachable and unreachable objects to the named file, or to stdout. Objects\n" 10516 " in the nursery are ignored, so if you wish to include them, consider calling\n" 10517 " minorgc() first."), 10518 10519 JS_FN_HELP("terminate", Terminate, 0, 0, 10520 "terminate()", 10521 " Terminate JavaScript execution, as if we had run out of\n" 10522 " memory or been terminated by the slow script dialog."), 10523 10524 JS_FN_HELP("readGeckoProfilingStack", ReadGeckoProfilingStack, 0, 0, 10525 "readGeckoProfilingStack()", 10526 " Reads the JIT/Wasm stack using ProfilingFrameIterator. Skips non-JIT/Wasm frames."), 10527 10528 JS_FN_HELP("readGeckoInterpProfilingStack", ReadGeckoInterpProfilingStack, 0, 0, 10529 "readGeckoInterpProfilingStack()", 10530 " Reads the C++ interpreter profiling stack. Skips JIT/Wasm frames."), 10531 10532 JS_FN_HELP("enableOsiPointRegisterChecks", EnableOsiPointRegisterChecks, 0, 0, 10533 "enableOsiPointRegisterChecks()", 10534 " Emit extra code to verify live regs at the start of a VM call are not\n" 10535 " modified before its OsiPoint."), 10536 10537 JS_FN_HELP("displayName", DisplayName, 1, 0, 10538 "displayName(fn)", 10539 " Gets the display name for a function, which can possibly be a guessed or\n" 10540 " inferred name based on where the function was defined. This can be\n" 10541 " different from the 'name' property on the function."), 10542 10543 JS_FN_HELP("isAsmJSCompilationAvailable", IsAsmJSCompilationAvailable, 0, 0, 10544 "isAsmJSCompilationAvailable", 10545 " Returns whether asm.js compilation is currently available or whether it is disabled\n" 10546 " (e.g., by the debugger)."), 10547 10548 JS_FN_HELP("getJitCompilerOptions", GetJitCompilerOptions, 0, 0, 10549 "getJitCompilerOptions()", 10550 " Return an object describing some of the JIT compiler options.\n"), 10551 10552 JS_FN_HELP("isAsmJSModule", IsAsmJSModule, 1, 0, 10553 "isAsmJSModule(fn)", 10554 " Returns whether the given value is a function containing \"use asm\" that has been\n" 10555 " validated according to the asm.js spec."), 10556 10557 JS_FN_HELP("isAsmJSFunction", IsAsmJSFunction, 1, 0, 10558 "isAsmJSFunction(fn)", 10559 " Returns whether the given value is a nested function in an asm.js module that has been\n" 10560 " both compile- and link-time validated."), 10561 10562 JS_FN_HELP("isAvxPresent", IsAvxPresent, 0, 0, 10563 "isAvxPresent([minVersion])", 10564 " Returns whether AVX is present and enabled. If minVersion specified,\n" 10565 " use 1 - to check if AVX is enabled (default), 2 - if AVX2 is enabled."), 10566 10567 JS_FN_HELP("wasmIsSupported", WasmIsSupported, 0, 0, 10568 "wasmIsSupported()", 10569 " Returns a boolean indicating whether WebAssembly is supported on the current device."), 10570 10571 JS_FN_HELP("wasmIsSupportedByHardware", WasmIsSupportedByHardware, 0, 0, 10572 "wasmIsSupportedByHardware()", 10573 " Returns a boolean indicating whether WebAssembly is supported on the current hardware (regardless of whether we've enabled support)."), 10574 10575 JS_FN_HELP("wasmDebuggingEnabled", WasmDebuggingEnabled, 0, 0, 10576 "wasmDebuggingEnabled()", 10577 " Returns a boolean indicating whether WebAssembly debugging is supported on the current device;\n" 10578 " returns false also if WebAssembly is not supported"), 10579 10580 JS_FN_HELP("wasmStreamingEnabled", WasmStreamingEnabled, 0, 0, 10581 "wasmStreamingEnabled()", 10582 " Returns a boolean indicating whether WebAssembly caching is supported by the runtime."), 10583 10584 JS_FN_HELP("wasmCachingEnabled", WasmCachingEnabled, 0, 0, 10585 "wasmCachingEnabled()", 10586 " Returns a boolean indicating whether WebAssembly caching is supported by the runtime."), 10587 10588 JS_FN_HELP("wasmHugeMemorySupported", WasmHugeMemorySupported, 0, 0, 10589 "wasmHugeMemorySupported()", 10590 " Returns a boolean indicating whether WebAssembly supports using a large" 10591 " virtual memory reservation in order to elide bounds checks on this platform."), 10592 10593 JS_FN_HELP("wasmMaxMemoryPages", WasmMaxMemoryPages, 1, 0, 10594 "wasmMaxMemoryPages(addressType)", 10595 " Returns an int with the maximum number of pages that can be allocated to a memory." 10596 " This is an implementation artifact that does depend on the address type, the hardware," 10597 " the operating system, the build configuration, and flags. The result is constant for" 10598 " a given combination of those; there is no guarantee that that size allocation will" 10599 " always succeed, only that it can succeed in principle. The addressType is a string," 10600 " 'i32' or 'i64'."), 10601 10602 #define WASM_FEATURE(NAME, ...) \ 10603 JS_FN_HELP("wasm" #NAME "Enabled", Wasm##NAME##Enabled, 0, 0, \ 10604 "wasm" #NAME "Enabled()", \ 10605 " Returns a boolean indicating whether the WebAssembly " #NAME " proposal is enabled."), 10606 JS_FOR_WASM_FEATURES(WASM_FEATURE) 10607 #undef WASM_FEATURE 10608 10609 JS_FN_HELP("wasmThreadsEnabled", WasmThreadsEnabled, 0, 0, 10610 "wasmThreadsEnabled()", 10611 " Returns a boolean indicating whether the WebAssembly threads proposal is\n" 10612 " supported on the current device."), 10613 10614 JS_FN_HELP("wasmSimdEnabled", WasmSimdEnabled, 0, 0, 10615 "wasmSimdEnabled()", 10616 " Returns a boolean indicating whether WebAssembly SIMD proposal is\n" 10617 " supported by the current device."), 10618 10619 #if defined(ENABLE_WASM_SIMD) && defined(DEBUG) 10620 JS_FN_HELP("wasmSimdAnalysis", WasmSimdAnalysis, 1, 0, 10621 "wasmSimdAnalysis(...)", 10622 " Unstable API for white-box testing.\n"), 10623 #endif 10624 10625 JS_FN_HELP("wasmGlobalFromArrayBuffer", WasmGlobalFromArrayBuffer, 2, 0, 10626 "wasmGlobalFromArrayBuffer(type, arrayBuffer)", 10627 " Create a WebAssembly.Global object from a provided ArrayBuffer. The type\n" 10628 " must be POD (i32, i64, f32, f64, v128). The buffer must be the same\n" 10629 " size as the type in bytes.\n"), 10630 JS_FN_HELP("wasmGlobalExtractLane", WasmGlobalExtractLane, 3, 0, 10631 "wasmGlobalExtractLane(global, laneInterp, laneIndex)", 10632 " Extract a lane from a WebAssembly.Global object that contains a v128 value\n" 10633 " and return it as a new WebAssembly.Global object of the appropriate type.\n" 10634 " The supported laneInterp values are i32x4, i64x2, f32x4, and\n" 10635 " f64x2.\n"), 10636 JS_FN_HELP("wasmGlobalsEqual", WasmGlobalsEqual, 2, 0, 10637 "wasmGlobalsEqual(globalA, globalB)", 10638 " Compares two WebAssembly.Global objects for if their types and values are\n" 10639 " equal. Mutability is not compared. Floating point values are compared for\n" 10640 " bitwise equality, not IEEE 754 equality.\n"), 10641 JS_FN_HELP("wasmGlobalIsNaN", WasmGlobalIsNaN, 2, 0, 10642 "wasmGlobalIsNaN(global, flavor)", 10643 " Compares a floating point WebAssembly.Global object for if its value is a\n" 10644 " specific NaN flavor. Valid flavors are `arithmetic_nan` and `canonical_nan`.\n"), 10645 JS_FN_HELP("wasmGlobalToString", WasmGlobalToString, 1, 0, 10646 "wasmGlobalToString(global)", 10647 " Returns a debug representation of the contents of a WebAssembly.Global\n" 10648 " object.\n"), 10649 JS_FN_HELP("wasmLosslessInvoke", WasmLosslessInvoke, 1, 0, 10650 "wasmLosslessInvoke(wasmFunc, args...)", 10651 " Invokes the provided WebAssembly function using a modified conversion\n" 10652 " function that allows providing a param as a WebAssembly.Global and\n" 10653 " returning a result as a WebAssembly.Global.\n"), 10654 10655 JS_FN_HELP("wasmCompilersPresent", WasmCompilersPresent, 0, 0, 10656 "wasmCompilersPresent()", 10657 " Returns a string indicating the present wasm compilers: a comma-separated list\n" 10658 " of 'baseline', 'ion'. A compiler is present in the executable if it is compiled\n" 10659 " in and can generate code for the current architecture."), 10660 10661 JS_FN_HELP("wasmCompileMode", WasmCompileMode, 0, 0, 10662 "wasmCompileMode()", 10663 " Returns a string indicating the available wasm compilers: 'baseline', 'ion',\n" 10664 " 'baseline+ion', or 'none'. A compiler is available if it is present in the\n" 10665 " executable and not disabled by switches or runtime conditions. At most one\n" 10666 " baseline and one optimizing compiler can be available."), 10667 10668 JS_FN_HELP("wasmLazyTieringEnabled", WasmLazyTieringEnabled, 0, 0, 10669 "wasmLazyTieringEnabled()", 10670 " Returns a boolean indicating whether compilation will be performed\n" 10671 " using lazy tiering."), 10672 10673 JS_FN_HELP("wasmBaselineDisabledByFeatures", WasmBaselineDisabledByFeatures, 0, 0, 10674 "wasmBaselineDisabledByFeatures()", 10675 " If some feature is enabled at compile-time or run-time that prevents baseline\n" 10676 " from being used then this returns a truthy string describing the features that\n." 10677 " are disabling it. Otherwise it returns false."), 10678 10679 JS_FN_HELP("wasmIonDisabledByFeatures", WasmIonDisabledByFeatures, 0, 0, 10680 "wasmIonDisabledByFeatures()", 10681 " If some feature is enabled at compile-time or run-time that prevents Ion\n" 10682 " from being used then this returns a truthy string describing the features that\n." 10683 " are disabling it. Otherwise it returns false."), 10684 10685 JS_FN_HELP("wasmHasTier2CompilationCompleted", WasmHasTier2CompilationCompleted, 1, 0, 10686 "wasmHasTier2CompilationCompleted(module)", 10687 " Returns a boolean indicating whether a given module has finished compiled code for tier2. \n" 10688 "This will return true early if compilation isn't two-tiered. "), 10689 10690 JS_FN_HELP("wasmLoadedFromCache", WasmLoadedFromCache, 1, 0, 10691 "wasmLoadedFromCache(module)", 10692 " Returns a boolean indicating whether a given module was deserialized directly from a\n" 10693 " cache (as opposed to compiled from bytecode)."), 10694 10695 JS_FN_HELP("wasmBuiltinI8VecMul", WasmBuiltinI8VecMul, 0, 0, 10696 "wasmBuiltinI8VecMul()", 10697 " Returns a module that implements an i8 vector pairwise multiplication intrinsic."), 10698 10699 JS_FN_HELP("wasmGcReadField", WasmGcReadField, 2, 0, 10700 "wasmGcReadField(obj, index)", 10701 " Gets a field of a WebAssembly GC struct or array."), 10702 10703 JS_FN_HELP("wasmGcArrayLength", WasmGcArrayLength, 1, 0, 10704 "wasmGcArrayLength(arr)", 10705 " Gets the length of a WebAssembly GC array."), 10706 10707 #ifdef ENABLE_WASM_BRANCH_HINTING 10708 JS_FN_HELP("wasmParsedBranchHints", WasmParsedBranchHints, 1, 0, 10709 "wasmParsedBranchHints(module)", 10710 " Returns a boolean indicating whether a given module has successfully parsed a\n" 10711 " custom branch hinting section."), 10712 10713 #endif // ENABLE_WASM_BRANCH_HINTING 10714 10715 JS_FN_HELP("largeArrayBufferSupported", LargeArrayBufferSupported, 0, 0, 10716 "largeArrayBufferSupported()", 10717 " Returns true if array buffers larger than 2GB can be allocated."), 10718 10719 JS_FN_HELP("isLazyFunction", IsLazyFunction, 1, 0, 10720 "isLazyFunction(fun)", 10721 " True if fun is a lazy JSFunction."), 10722 10723 JS_FN_HELP("isRelazifiableFunction", IsRelazifiableFunction, 1, 0, 10724 "isRelazifiableFunction(fun)", 10725 " True if fun is a JSFunction with a relazifiable JSScript."), 10726 10727 JS_FN_HELP("hasSameBytecodeData", HasSameBytecodeData, 2, 0, 10728 "hasSameBytecodeData(fun1, fun2)", 10729 " True if fun1 and fun2 share the same copy of bytecode data. This will\n" 10730 " delazify the function if necessary."), 10731 10732 JS_FN_HELP("enableShellAllocationMetadataBuilder", EnableShellAllocationMetadataBuilder, 0, 0, 10733 "enableShellAllocationMetadataBuilder()", 10734 " Use ShellAllocationMetadataBuilder to supply metadata for all newly created objects."), 10735 10736 JS_FN_HELP("getAllocationMetadata", GetAllocationMetadata, 1, 0, 10737 "getAllocationMetadata(obj)", 10738 " Get the metadata for an object."), 10739 10740 JS_INLINABLE_FN_HELP("bailout", testingFunc_bailout, 0, 0, TestBailout, 10741 "bailout()", 10742 " Force a bailout out of ionmonkey (if running in ionmonkey)."), 10743 10744 JS_FN_HELP("bailAfter", testingFunc_bailAfter, 1, 0, 10745 "bailAfter(number)", 10746 " Start a counter to bail once after passing the given amount of possible bailout positions in\n" 10747 " ionmonkey.\n"), 10748 10749 JS_FN_HELP("invalidate", testingFunc_invalidate, 0, 0, 10750 "invalidate()", 10751 " Force an immediate invalidation (if running in Warp)."), 10752 10753 JS_FN_HELP("inJit", testingFunc_inJit, 0, 0, 10754 "inJit()", 10755 " Returns true when called within (jit-)compiled code. When jit compilation is disabled this\n" 10756 " function returns an error string. This function returns false in all other cases.\n" 10757 " Depending on truthiness, you should continue to wait for compilation to happen or stop execution.\n"), 10758 10759 JS_FN_HELP("inIon", testingFunc_inIon, 0, 0, 10760 "inIon()", 10761 " Returns true when called within ion. When ion is disabled or when compilation is abnormally\n" 10762 " slow to start, this function returns an error string. Otherwise, this function returns false.\n" 10763 " This behaviour ensures that a falsy value means that we are not in ion, but expect a\n" 10764 " compilation to occur in the future. Conversely, a truthy value means that we are either in\n" 10765 " ion or that there is litle or no chance of ion ever compiling the current script."), 10766 10767 JS_FN_HELP("assertJitStackInvariants", TestingFunc_assertJitStackInvariants, 0, 0, 10768 "assertJitStackInvariants()", 10769 " Iterates the Jit stack and check that stack invariants hold."), 10770 10771 JS_FN_HELP("setIonCheckGraphCoherency", SetIonCheckGraphCoherency, 1, 0, 10772 "setIonCheckGraphCoherency(bool)", 10773 " Set whether Ion should perform graph consistency (DEBUG-only) assertions. These assertions\n" 10774 " are valuable and should be generally enabled, however they can be very expensive for large\n" 10775 " (wasm) programs."), 10776 10777 JS_FN_HELP("serialize", testingFunc_serialize, 1, 0, 10778 "serialize(data, [transferables, [policy]])", 10779 " Serialize 'data' using JS_WriteStructuredClone. Returns a structured\n" 10780 " clone buffer object. 'policy' may be an options hash. Valid keys:\n" 10781 " 'SharedArrayBuffer' - either 'allow' or 'deny' (the default)\n" 10782 " to specify whether SharedArrayBuffers may be serialized.\n" 10783 " 'scope' - SameProcess, DifferentProcess, or\n" 10784 " DifferentProcessForIndexedDB. Determines how some values will be\n" 10785 " serialized. Clone buffers may only be deserialized with a compatible\n" 10786 " scope. NOTE - For DifferentProcess/DifferentProcessForIndexedDB,\n" 10787 " must also set SharedArrayBuffer:'deny' if data contains any shared memory\n" 10788 " object."), 10789 10790 JS_FN_HELP("deserialize", Deserialize, 1, 0, 10791 "deserialize(clonebuffer[, opts])", 10792 " Deserialize data generated by serialize. 'opts' may be an options hash.\n" 10793 " Valid keys:\n" 10794 " 'SharedArrayBuffer' - either 'allow' or 'deny' (the default)\n" 10795 " to specify whether SharedArrayBuffers may be serialized.\n" 10796 " 'scope', which limits the clone buffers that are considered\n" 10797 " valid. Allowed values: ''SameProcess', 'DifferentProcess',\n" 10798 " and 'DifferentProcessForIndexedDB'. So for example, a\n" 10799 " DifferentProcessForIndexedDB clone buffer may be deserialized in any scope, but\n" 10800 " a SameProcess clone buffer cannot be deserialized in a\n" 10801 " DifferentProcess scope."), 10802 10803 JS_FN_HELP("detachArrayBuffer", DetachArrayBuffer, 1, 0, 10804 "detachArrayBuffer(buffer)", 10805 " Detach the given ArrayBuffer object from its memory, i.e. as if it\n" 10806 " had been transferred to a WebWorker."), 10807 10808 JS_FN_HELP("makeSerializable", MakeSerializable, 1, 0, 10809 "makeSerializable(numeric id, [behavior])", 10810 " Make a custom serializable, transferable object. It will have a single accessor\n" 10811 " obj.log that will give a history of all operations on all such objects in the\n" 10812 " current thread as an array [id, action, id, action, ...] where the id\n" 10813 " is the number passed into this function, and the action is one of:\n" 10814 " ? - the canTransfer() hook was called.\n" 10815 " w - the write() hook was called.\n" 10816 " W - the writeTransfer() hook was called.\n" 10817 " R - the readTransfer() hook was called.\n" 10818 " r - the read() hook was called.\n" 10819 " F - the freeTransfer() hook was called.\n" 10820 " The `behavior` parameter can be used to force a failure during processing:\n" 10821 " 1 - fail during readTransfer() hook\n" 10822 " 2 - fail during read() hook\n" 10823 " Set the log to null to clear it."), 10824 10825 JS_FN_HELP("ensureNonInline", EnsureNonInline, 1, 0, 10826 "ensureNonInline(view or buffer)", 10827 " Ensure that the memory for the given ArrayBuffer or ArrayBufferView\n" 10828 " is not inline."), 10829 10830 JS_FN_HELP("pinArrayBufferOrViewLength", PinArrayBufferOrViewLength, 1, 0, 10831 "pinArrayBufferOrViewLength(view or buffer[, pin])", 10832 " Prevent or allow (if `pin` is false) changes to the length of the given\n" 10833 " ArrayBuffer or ArrayBufferView. `pin` defaults to true."), 10834 10835 JS_FN_HELP("JSONStringify", JSONStringify, 4, 0, 10836 "JSONStringify(value, behavior)", 10837 " Same as JSON.stringify(value), but allows setting behavior:\n" 10838 " Normal: the default\n" 10839 " FastOnly: throw if the fast path bails\n" 10840 " SlowOnly: skip the fast path entirely\n" 10841 " Compare: run both the fast and slow paths and compare the result. Crash if\n" 10842 " they do not match. If the fast path bails, no comparison is done."), 10843 10844 JS_FN_HELP("helperThreadCount", HelperThreadCount, 0, 0, 10845 "helperThreadCount()", 10846 " Returns the number of helper threads available for off-thread tasks."), 10847 10848 JS_FN_HELP("createShapeSnapshot", CreateShapeSnapshot, 1, 0, 10849 "createShapeSnapshot(obj)", 10850 " Returns an object containing a shape snapshot for use with\n" 10851 " checkShapeSnapshot.\n"), 10852 10853 JS_FN_HELP("checkShapeSnapshot", CheckShapeSnapshot, 2, 0, 10854 "checkShapeSnapshot(snapshot, [obj])", 10855 " Check shape invariants based on the given snapshot and optional object.\n" 10856 " If there's no object argument, the snapshot's object is used.\n"), 10857 10858 JS_FN_HELP("enableShapeConsistencyChecks", EnableShapeConsistencyChecks, 0, 0, 10859 "enableShapeConsistencyChecks()", 10860 " Enable some slow Shape assertions.\n"), 10861 10862 JS_FN_HELP("reportOutOfMemory", ReportOutOfMemory, 0, 0, 10863 "reportOutOfMemory()", 10864 " Report OOM, then clear the exception and return undefined. For crash testing."), 10865 10866 JS_FN_HELP("throwOutOfMemory", ThrowOutOfMemory, 0, 0, 10867 "throwOutOfMemory()", 10868 " Throw out of memory exception, for OOM handling testing."), 10869 10870 JS_FN_HELP("reportLargeAllocationFailure", ReportLargeAllocationFailure, 0, 0, 10871 "reportLargeAllocationFailure([bytes])", 10872 " Call the large allocation failure callback, as though a large malloc call failed,\n" 10873 " then return undefined. In Gecko, this sends a memory pressure notification, which\n" 10874 " can free up some memory."), 10875 10876 JS_FN_HELP("findPath", FindPath, 2, 0, 10877 "findPath(start, target)", 10878 " Return an array describing one of the shortest paths of GC heap edges from\n" 10879 " |start| to |target|, or |undefined| if |target| is unreachable from |start|.\n" 10880 " Each element of the array is either of the form:\n" 10881 " { node: <object or string>, edge: <string describing edge from node> }\n" 10882 " if the node is a JavaScript object or value; or of the form:\n" 10883 " { type: <string describing node>, edge: <string describing edge> }\n" 10884 " if the node is some internal thing that is not a proper JavaScript value\n" 10885 " (like a shape or a scope chain element). The destination of the i'th array\n" 10886 " element's edge is the node of the i+1'th array element; the destination of\n" 10887 " the last array element is implicitly |target|.\n"), 10888 10889 JS_FN_HELP("sharedMemoryEnabled", SharedMemoryEnabled, 0, 0, 10890 "sharedMemoryEnabled()", 10891 " Return true if SharedArrayBuffer and Atomics are enabled"), 10892 10893 JS_FN_HELP("sharedArrayRawBufferRefcount", SharedArrayRawBufferRefcount, 0, 0, 10894 "sharedArrayRawBufferRefcount(sab)", 10895 " Return the reference count of the SharedArrayRawBuffer object held by sab"), 10896 10897 #ifdef NIGHTLY_BUILD 10898 JS_FN_HELP("objectAddress", ObjectAddress, 1, 0, 10899 "objectAddress(obj)", 10900 " Return the current address of the object. For debugging only--this\n" 10901 " address may change during a moving GC."), 10902 10903 JS_FN_HELP("scriptAddressForFunction", ScriptAddressForFunction, 1, 0, 10904 "scriptAddressForFunction(fun)", 10905 " Return the current address of a function's script."), 10906 10907 JS_FN_HELP("sharedAddress", SharedAddress, 1, 0, 10908 "sharedAddress(obj)", 10909 " Return the address of the shared storage of a SharedArrayBuffer."), 10910 #endif 10911 10912 JS_FN_HELP("hasInvalidatedTeleporting", HasInvalidatedTeleporting, 1, 0, 10913 "hasInvalidatedTeleporting(obj)", 10914 " Return true if the shape teleporting optimization has been disabled for |obj|."), 10915 10916 JS_FN_HELP("evalReturningScope", EvalReturningScope, 1, 0, 10917 "evalReturningScope(scriptStr, [global])", 10918 " Evaluate the script in a new scope and return the scope.\n" 10919 " If |global| is present, clone the script to |global| before executing."), 10920 10921 JS_FN_HELP("backtrace", DumpBacktrace, 1, 0, 10922 "backtrace()", 10923 " Dump out a brief backtrace."), 10924 10925 JS_FN_HELP("getBacktrace", GetBacktrace, 1, 0, 10926 "getBacktrace([options])", 10927 " Return the current stack as a string. Takes an optional options object,\n" 10928 " which may contain any or all of the boolean properties:\n" 10929 " options.args - show arguments to each function\n" 10930 " options.locals - show local variables in each frame\n" 10931 " options.thisprops - show the properties of the 'this' object of each frame\n"), 10932 10933 JS_FN_HELP("byteSize", ByteSize, 1, 0, 10934 "byteSize(value)", 10935 " Return the size in bytes occupied by |value|, or |undefined| if value\n" 10936 " is not allocated in memory.\n"), 10937 10938 JS_FN_HELP("byteSizeOfScript", ByteSizeOfScript, 1, 0, 10939 "byteSizeOfScript(f)", 10940 " Return the size in bytes occupied by the function |f|'s JSScript.\n"), 10941 10942 JS_FN_HELP("setImmutablePrototype", SetImmutablePrototype, 1, 0, 10943 "setImmutablePrototype(obj)", 10944 " Try to make obj's [[Prototype]] immutable, such that subsequent attempts to\n" 10945 " change it will fail. Return true if obj's [[Prototype]] was successfully made\n" 10946 " immutable (or if it already was immutable), false otherwise. Throws in case\n" 10947 " of internal error, or if the operation doesn't even make sense (for example,\n" 10948 " because the object is a revoked proxy)."), 10949 10950 JS_FN_HELP("allocationMarker", AllocationMarker, 0, 0, 10951 "allocationMarker([options])", 10952 " Return a freshly allocated object whose [[Class]] name is\n" 10953 " \"AllocationMarker\". Such objects are allocated only by calls\n" 10954 " to this function, never implicitly by the system, making them\n" 10955 " suitable for use in allocation tooling tests. Takes an optional\n" 10956 " options object which may contain the following properties:\n" 10957 " * nursery: bool, whether to allocate the object in the nursery\n"), 10958 10959 JS_FN_HELP("setGCCallback", SetGCCallback, 1, 0, 10960 "setGCCallback({action:\"...\", options...})", 10961 " Set the GC callback. action may be:\n" 10962 " 'minorGC' - run a nursery collection\n" 10963 " 'majorGC' - run a major collection, nesting up to a given 'depth'\n"), 10964 10965 #ifdef DEBUG 10966 JS_FN_HELP("enqueueMark", EnqueueMark, 1, 0, 10967 "enqueueMark(obj|string)", 10968 " Add an object to the queue of objects to mark at the beginning every GC. (Note\n" 10969 " that the objects will actually be marked at the beginning of every slice, but\n" 10970 " after the first slice they will already be marked so nothing will happen.)\n" 10971 " \n" 10972 " Instead of an object, a few magic strings may be used:\n" 10973 " 'yield' - cause the current marking slice to end, as if the mark budget were\n" 10974 " exceeded.\n" 10975 " 'enter-weak-marking-mode' - divide the list into two segments. The items after\n" 10976 " this string will not be marked until we enter weak marking mode. Note that weak\n" 10977 " marking mode may be entered zero or multiple times for one GC.\n" 10978 " 'abort-weak-marking-mode' - same as above, but then abort weak marking to fall back\n" 10979 " on the old iterative marking code path.\n" 10980 " 'drain' - fully drain the mark stack before continuing.\n" 10981 " 'set-color-black' - force everything following in the mark queue to be marked black.\n" 10982 " 'set-color-gray' - continue with the regular GC until gray marking is possible, then force\n" 10983 " everything following in the mark queue to be marked gray.\n" 10984 " 'unset-color' - stop forcing the mark color."), 10985 10986 JS_FN_HELP("clearMarkQueue", ClearMarkQueue, 0, 0, 10987 "clearMarkQueue()", 10988 " Cancel the special marking of all objects enqueue with enqueueMark()."), 10989 10990 JS_FN_HELP("getMarkQueue", GetMarkQueue, 0, 0, 10991 "getMarkQueue()", 10992 " Return the current mark queue set up via enqueueMark calls. Note that all\n" 10993 " returned values will be wrapped into the current compartment, so this loses\n" 10994 " some fidelity."), 10995 #endif // DEBUG 10996 10997 JS_FN_HELP("nurseryStringsEnabled", NurseryStringsEnabled, 0, 0, 10998 "nurseryStringsEnabled()", 10999 " Return whether strings are currently allocated in the nursery for the current\n" 11000 " global\n"), 11001 11002 JS_FN_HELP("isNurseryAllocated", IsNurseryAllocated, 1, 0, 11003 "isNurseryAllocated(thing)", 11004 " Return whether a GC thing is nursery allocated.\n"), 11005 11006 JS_FN_HELP("numAllocSitesPretenured", NumAllocSitesPretenured, 0, 0, 11007 "numAllocSitesPretenured()", 11008 " Return the number of allocation sites that were pretenured for the current\n" 11009 " global\n"), 11010 11011 JS_FN_HELP("getLcovInfo", GetLcovInfo, 1, 0, 11012 "getLcovInfo(global)", 11013 " Generate LCOV tracefile for the given compartment. If no global are provided then\n" 11014 " the current global is used as the default one.\n"), 11015 11016 #ifdef DEBUG 11017 JS_FN_HELP("setRNGState", SetRNGState, 2, 0, 11018 "setRNGState(seed0, seed1)", 11019 " Set this compartment's RNG state.\n"), 11020 #endif 11021 11022 #ifdef AFLFUZZ 11023 JS_FN_HELP("aflloop", AflLoop, 1, 0, 11024 "aflloop(max_cnt)", 11025 " Call the __AFL_LOOP() runtime function (see AFL docs)\n"), 11026 #endif 11027 11028 JS_FN_HELP("monotonicNow", MonotonicNow, 0, 0, 11029 "monotonicNow()", 11030 " Return a timestamp reflecting the current elapsed system time.\n" 11031 " This is monotonically increasing.\n"), 11032 11033 JS_FN_HELP("timeSinceCreation", TimeSinceCreation, 0, 0, 11034 "TimeSinceCreation()", 11035 " Returns the time in milliseconds since process creation.\n" 11036 " This uses a clock compatible with the profiler.\n"), 11037 11038 JS_FN_HELP("isConstructor", IsConstructor, 1, 0, 11039 "isConstructor(value)", 11040 " Returns whether the value is considered IsConstructor.\n"), 11041 11042 JS_FN_HELP("getTimeZone", GetTimeZone, 0, 0, 11043 "getTimeZone()", 11044 " Get the current time zone.\n"), 11045 11046 JS_FN_HELP("getDefaultLocale", GetDefaultLocale, 0, 0, 11047 "getDefaultLocale()", 11048 " Get the current default locale.\n"), 11049 11050 JS_FN_HELP("getCoreCount", GetCoreCount, 0, 0, 11051 "getCoreCount()", 11052 " Get the number of CPU cores from the platform layer. Typically this\n" 11053 " means the number of hyperthreads on systems where that makes sense.\n"), 11054 11055 JS_FN_HELP("setTimeResolution", SetTimeResolution, 2, 0, 11056 "setTimeResolution(resolution, jitter)", 11057 " Enables time clamping and jittering. Specify a time resolution in\n" 11058 " microseconds and whether or not to jitter\n"), 11059 11060 JS_FN_HELP("scriptedCallerGlobal", ScriptedCallerGlobal, 0, 0, 11061 "scriptedCallerGlobal()", 11062 " Get the caller's global (or null). See JS::GetScriptedCallerGlobal.\n"), 11063 11064 JS_FN_HELP("objectGlobal", ObjectGlobal, 1, 0, 11065 "objectGlobal(obj)", 11066 " Returns the object's global object or null if the object is a wrapper.\n"), 11067 11068 JS_FN_HELP("isSameCompartment", IsSameCompartment, 2, 0, 11069 "isSameCompartment(obj1, obj2)", 11070 " Unwraps obj1 and obj2 and returns whether the unwrapped objects are\n" 11071 " same-compartment.\n"), 11072 11073 JS_FN_HELP("firstGlobalInCompartment", FirstGlobalInCompartment, 1, 0, 11074 "firstGlobalInCompartment(obj)", 11075 " Returns the first global in obj's compartment.\n"), 11076 11077 JS_FN_HELP("assertCorrectRealm", AssertCorrectRealm, 0, 0, 11078 "assertCorrectRealm()", 11079 " Asserts cx->realm matches callee->realm.\n"), 11080 11081 JS_FN_HELP("globalLexicals", GlobalLexicals, 0, 0, 11082 "globalLexicals()", 11083 " Returns an object containing a copy of all global lexical bindings.\n" 11084 " Example use: let x = 1; assertEq(globalLexicals().x, 1);\n"), 11085 11086 JS_FN_HELP("baselineCompile", BaselineCompile, 2, 0, 11087 "baselineCompile([fun/code], forceDebugInstrumentation=false)", 11088 " Baseline-compiles the given JS function or script.\n" 11089 " Without arguments, baseline-compiles the caller's script; but note\n" 11090 " that extra boilerplate is needed afterwards to cause the VM to start\n" 11091 " running the jitcode rather than staying in the interpreter:\n" 11092 " baselineCompile(); for (var i=0; i<1; i++) {} ...\n" 11093 " The interpreter will enter the new jitcode at the loop header unless\n" 11094 " baselineCompile returned a string or threw an error.\n"), 11095 JS_FN_HELP("setBaselineHint", SetBaselineHint, 1, 0, 11096 "setBaselineHint(fun)", 11097 " Sets a baseline JIT hint for the given function, marking it for eager\n" 11098 " baseline compilation on subsequent executions.\n"), 11099 JS_FN_HELP("hasBaselineHint", HasBaselineHint, 1, 0, 11100 "hasBaselineHint(fun)", 11101 " Returns true if the given function has a baseline JIT hint set.\n"), 11102 11103 JS_FN_HELP("encodeAsUtf8InBuffer", EncodeAsUtf8InBuffer, 2, 0, 11104 "encodeAsUtf8InBuffer(str, uint8Array)", 11105 " Encode as many whole code points from the string str into the provided\n" 11106 " Uint8Array as will completely fit in it, converting lone surrogates to\n" 11107 " REPLACEMENT CHARACTER. Return an array [r, w] where |r| is the\n" 11108 " number of 16-bit units read and |w| is the number of bytes of UTF-8\n" 11109 " written."), 11110 11111 JS_FN_HELP("clearKeptObjects", ClearKeptObjects, 0, 0, 11112 "clearKeptObjects()", 11113 "Perform the ECMAScript ClearKeptObjects operation, clearing the list of\n" 11114 "observed WeakRef targets that are kept alive until the next synchronous\n" 11115 "sequence of ECMAScript execution completes. This is used for testing\n" 11116 "WeakRefs.\n"), 11117 11118 JS_FN_HELP("numberToDouble", NumberToDouble, 1, 0, 11119 "numberToDouble(number)", 11120 " Return the input number as double-typed number."), 11121 11122 JS_FN_HELP("getICUOptions", GetICUOptions, 0, 0, 11123 "getICUOptions()", 11124 " Return an object describing the following ICU options.\n\n" 11125 " version: a string containing the ICU version number, e.g. '67.1'\n" 11126 " unicode: a string containing the Unicode version number, e.g. '13.0'\n" 11127 " locale: the ICU default locale, e.g. 'en_US'\n" 11128 " tzdata: a string containing the tzdata version number, e.g. '2020a'\n" 11129 " timezone: the ICU default time zone, e.g. 'America/Los_Angeles'\n" 11130 " host-timezone: the host time zone, e.g. 'America/Los_Angeles'"), 11131 11132 JS_FN_HELP("getAvailableLocalesOf", GetAvailableLocalesOf, 0, 0, 11133 "getAvailableLocalesOf(name)", 11134 " Return an array of all available locales for the given Intl constuctor."), 11135 11136 JS_FN_HELP("isSmallFunction", IsSmallFunction, 1, 0, 11137 "isSmallFunction(fun)", 11138 " Returns true if a scripted function is small enough to be inlinable."), 11139 11140 JS_FN_HELP("compileToStencil", CompileToStencil, 1, 0, 11141 "compileToStencil(string, [options])", 11142 " Parses the given string argument as js script, returns the stencil" 11143 " for it."), 11144 11145 JS_FN_HELP("evalStencil", EvalStencil, 1, 0, 11146 "evalStencil(stencil, [options])", 11147 " Instantiates the given stencil, and evaluates the top-level script it" 11148 " defines."), 11149 11150 JS_FN_HELP("compileToStencilXDR", CompileToStencilXDR, 1, 0, 11151 "compileToStencilXDR(string, [options])", 11152 " Parses the given string argument as js script, produces the stencil" 11153 " for it, XDR-encodes the stencil, and returns an object that contains the" 11154 " XDR buffer."), 11155 11156 JS_FN_HELP("evalStencilXDR", EvalStencilXDR, 1, 0, 11157 "evalStencilXDR(stencilXDR, [options])", 11158 " Reads the given stencil XDR object, and evaluates the top-level script it" 11159 " defines."), 11160 11161 JS_FN_HELP("getExceptionInfo", GetExceptionInfo, 1, 0, 11162 "getExceptionInfo(fun)", 11163 " Calls the given function and returns information about the exception it" 11164 " throws. Returns null if the function didn't throw an exception."), 11165 11166 JS_FN_HELP("nukeCCW", NukeCCW, 1, 0, 11167 "nukeCCW(wrapper)", 11168 " Nuke a CrossCompartmentWrapper, which turns it into a DeadProxyObject."), 11169 11170 JS_FN_HELP("assertRealmFuseInvariants", AssertRealmFuseInvariants, 0, 0, 11171 "assertRealmFuseInvariants()", 11172 " Runs the realm's fuse invariant checks -- these will crash on failure. " 11173 " Only available in fuzzing or debug builds, so usage should be guarded. "), 11174 11175 JS_FN_HELP("assertRuntimeFuseInvariants", AssertRuntimeFuseInvariants, 0, 0, 11176 "assertRuntimeFuseInvariants()", 11177 " Runs the runtime's fuse invariant checks -- these will crash on failure. " 11178 " Only available in fuzzing or debug builds, so usage should be guarded. "), 11179 11180 JS_FN_HELP("isCCW", IsCCW, 1, 0, 11181 "isCCW(object)", 11182 " Return true if an object is a CCW."), 11183 11184 JS_FN_HELP("popAllFusesInRealm", PopAllFusesInRealm, 0, 0, 11185 "popAllFusesInRealm()", 11186 " Pops all the fuses in the current realm"), 11187 11188 JS_FN_HELP("getLastOOMStackTrace", GetLastOOMStackTrace, 0, 0, 11189 "getLastOOMStackTrace()", 11190 " Returns the stack trace captured from the most recent out-of-memory exception,\n" 11191 " or null if no OOM stack trace is available. The stack trace shows the JavaScript\n" 11192 " call stack at the time the out-of-memory condition occurred."), 11193 11194 JS_FN_HELP("popAllFusesInRuntime", PopAllFusesInRuntime, 0, 0, 11195 "popAllFusesInRuntime()", 11196 " Pops all the fuses in the runtime"), 11197 11198 JS_FN_HELP("getAllPrefNames", GetAllPrefNames, 0, 0, 11199 "getAllPrefNames()", 11200 " Returns an array containing the names of all JS prefs."), 11201 11202 JS_FN_HELP("getPrefValue", GetPrefValue, 1, 0, 11203 "getPrefValue(name)", 11204 " Return the value of the JS pref with the given name."), 11205 11206 JS_FN_HELP("hadOutOfMemory", HadOutOfMemory, 0, 0, 11207 "hadOutOfMemory()", 11208 " Return the runtime's internal hadOutOfMemory flag that is set when\n" 11209 " out of memory is hit with an exception being propagated. "), 11210 11211 JS_FN_HELP("supportDifferentialTesting", TestingFunc_SupportDifferentialTesting, 0, 0, 11212 "supportDifferentialTesting()", 11213 " Return the value of JS::SupportDifferentialTesting."), 11214 11215 JS_FS_HELP_END 11216 }; 11217 // clang-format on 11218 11219 // clang-format off 11220 static const JSFunctionSpecWithHelp FuzzingUnsafeTestingFunctions[] = { 11221 JS_FN_HELP("getErrorNotes", GetErrorNotes, 1, 0, 11222 "getErrorNotes(error)", 11223 " Returns an array of error notes."), 11224 11225 JS_FN_HELP("setTimeZone", SetTimeZone, 1, 0, 11226 "setTimeZone(tzname)", 11227 " Set the 'TZ' environment variable to the given time zone and applies the new time zone.\n" 11228 " The time zone given is validated according to the current environment.\n" 11229 " An empty string or undefined resets the time zone to its default value."), 11230 11231 JS_FN_HELP("getRealmTimeZone", GetRealmTimeZone, 0, 0, 11232 "getRealmTimeZone()", 11233 " Get the time zone for the current realm.\n"), 11234 11235 JS_FN_HELP("setRealmTimeZone", SetRealmTimeZone, 1, 0, 11236 "setRealmTimeZone(tzname)", 11237 " Set the time zone for the current realm.\n" 11238 " The time zone must be a valid IANA time zone identifier.\n" 11239 " An empty string or undefined resets the realm time zone to the system default time zone."), 11240 11241 JS_FN_HELP("setDefaultLocale", SetDefaultLocale, 1, 0, 11242 "setDefaultLocale(locale)", 11243 " Set the runtime default locale to the given value.\n" 11244 " An empty string or undefined resets the runtime locale to its default value.\n" 11245 " NOTE: The input string is not fully validated, it must be a valid BCP-47 language tag."), 11246 11247 JS_FN_HELP("getRealmLocale", GetRealmLocale, 0, 0, 11248 "getRealmLocale()", 11249 " Get the locale for the current realm."), 11250 11251 JS_FN_HELP("setRealmLocale", SetRealmLocale, 1, 0, 11252 "setRealmLocale(locale)", 11253 " Set the locale for the current realm.\n" 11254 " The locale must be a valid BCP-47 locale identifier.\n" 11255 " An empty string or undefined resets the realm locale to the system default locale."), 11256 11257 JS_FN_HELP("isCollectingDelazifications", IsCollectingDelazifications, 1, 0, 11258 "isCollectingDelazifications(fun)", 11259 " True if script for the function is collecting delazifications."), 11260 11261 JS_FN_HELP("isDelazificationPopulatedFor", IsDelazificationsPopulated, 1, 0, 11262 "isDelazificationPopulatedFor(fun)", 11263 " True if fun is available in the shared stencils."), 11264 11265 JS_FN_HELP("waitForDelazificationOf", WaitForDelazificationOf, 1, 0, 11266 "waitForDelazificationOf(fun)", 11267 " Block main thread execution until the function is made available in the\n" 11268 " shared stencils. If this function isn't sharing stencils, return immediately."), 11269 11270 JS_FN_HELP("waitForDone", WaitForDone, 1, 0, 11271 "waitForDone(obj)", 11272 " Loop calling `RunJobs` until the `done` value of `obj` is true"), 11273 11274 JS_FN_HELP("getInnerMostEnvironmentObject", GetInnerMostEnvironmentObject, 0, 0, 11275 "getInnerMostEnvironmentObject()", 11276 " Return the inner-most environment object for current execution."), 11277 11278 JS_FN_HELP("getEnclosingEnvironmentObject", GetEnclosingEnvironmentObject, 1, 0, 11279 "getEnclosingEnvironmentObject(env)", 11280 " Return the enclosing environment object for given environment object."), 11281 11282 JS_FN_HELP("getEnvironmentObjectType", GetEnvironmentObjectType, 1, 0, 11283 "getEnvironmentObjectType(env)", 11284 " Return a string represents the type of given environment object."), 11285 11286 JS_FN_HELP("shortestPaths", ShortestPaths, 3, 0, 11287 "shortestPaths(targets, options)", 11288 " Return an array of arrays of shortest retaining paths. There is an array of\n" 11289 " shortest retaining paths for each object in |targets|. Each element in a path\n" 11290 " is of the form |{ predecessor, edge }|. |options| may contain:\n" 11291 " \n" 11292 " maxNumPaths: The maximum number of paths returned in each of those arrays\n" 11293 " (default 3).\n" 11294 " start: The object to start all paths from. If not given, then\n" 11295 " the starting point will be the set of GC roots."), 11296 11297 JS_FN_HELP("getFuseState", GetFuseState, 0, 0, 11298 "getFuseState()", 11299 " Return an object describing the calling realm's fuse state, " 11300 " as well as the state of any runtime fuses."), 11301 11302 #if defined(DEBUG) || defined(JS_JITSPEW) 11303 JS_FN_HELP("dumpObject", DumpObject, 1, 0, 11304 "dumpObject(obj)", 11305 " Dump an internal representation of an object."), 11306 11307 JS_FN_HELP("dumpValue", DumpValue, 1, 0, 11308 "dumpValue(v)", 11309 " Dump an internal representation of a value."), 11310 11311 JS_FN_HELP("dumpValueToString", DumpValueToString, 1, 0, 11312 "dumpValue(v)", 11313 " Return a dump of an internal representation of a value."), 11314 11315 JS_FN_HELP("dumpStringRepresentation", DumpStringRepresentation, 1, 0, 11316 "dumpStringRepresentation(str)", 11317 " Print a human-readable description of how the string |str| is represented.\n"), 11318 11319 JS_FN_HELP("stringRepresentation", GetStringRepresentation, 1, 0, 11320 "stringRepresentation(str)", 11321 " Return a human-readable description of how the string |str| is represented.\n"), 11322 #endif 11323 11324 JS_FN_HELP("wasmExtractCode", WasmExtractCode, 1, 0, 11325 "wasmExtractCode(module[, tier])", 11326 " Extracts generated machine code from WebAssembly.Module. The tier is a string,\n" 11327 " 'stable', 'best', 'baseline', or 'ion'; the default is 'stable'. If the request\n" 11328 " cannot be satisfied then null is returned. If the request is 'ion' then block\n" 11329 " until background compilation is complete."), 11330 11331 JS_FN_HELP("wasmDis", WasmDisassemble, 1, 0, 11332 "wasmDis(wasmObject[, options])\n", 11333 " Disassembles generated machine code from an exported WebAssembly function,\n" 11334 " or from all the functions defined in the module or instance, exported and not.\n" 11335 " The `options` is an object with the following optional keys:\n" 11336 " asString: boolean - if true, return a string rather than printing on stderr,\n" 11337 " the default is false.\n" 11338 " tier: string - one of 'stable', 'best', 'baseline', or 'ion'; the default is\n" 11339 " 'stable'.\n" 11340 " kinds: string - if set, and the wasmObject is a module or instance, a\n" 11341 " comma-separated list of the following keys, the default is `Function`:\n" 11342 " Function - functions defined in the module\n" 11343 " InterpEntry - C++-to-wasm stubs\n" 11344 " JitEntry - jitted-js-to-wasm stubs\n" 11345 " ImportInterpExit - wasm-to-C++ stubs\n" 11346 " ImportJitExit - wasm-to-jitted-JS stubs\n" 11347 " all - all kinds, including obscure ones\n"), 11348 11349 JS_FN_HELP("wasmModuleToText", WasmModuleToText, 1, 0, 11350 "wasmModuleToText(wasmModule[, options])\n", 11351 " Converts a compiled wasm module to the wasm text format.\n"), 11352 11353 JS_FN_HELP("wasmDumpIon", WasmDumpIon, 2, 0, 11354 "wasmDumpIon(bytecode, funcIndex, [, contents])\n", 11355 "wasmDumpIon(bytecode, funcIndex, [, contents])" 11356 " Returns a dump of compiling a function in the specified module with Ion." 11357 " The `contents` flag controls what is dumped. one of:" 11358 " `mir` | `unopt-mir`: Unoptimized MIR (the default)" 11359 " `opt-mir`: Optimized MIR" 11360 " `lir`: LIR"), 11361 11362 JS_FN_HELP("wasmFunctionTier", WasmFunctionTier, 1, 0, 11363 "wasmFunctionTier(wasmFunc)\n", 11364 " Returns the best compiled tier for a function. Either 'baseline' or 'optimized'."), 11365 11366 JS_FN_HELP("wasmMetadataAnalysis", wasmMetadataAnalysis, 1, 0, 11367 "wasmMetadataAnalysis(wasmObject)", 11368 " Prints an analysis of the size of metadata on this wasm object.\n"), 11369 11370 JS_FS_HELP_END 11371 }; 11372 // clang-format on 11373 11374 // clang-format off 11375 static const JSFunctionSpecWithHelp PCCountProfilingTestingFunctions[] = { 11376 JS_FN_HELP("start", PCCountProfiling_Start, 0, 0, 11377 "start()", 11378 " Start PC count profiling."), 11379 11380 JS_FN_HELP("stop", PCCountProfiling_Stop, 0, 0, 11381 "stop()", 11382 " Stop PC count profiling."), 11383 11384 JS_FN_HELP("purge", PCCountProfiling_Purge, 0, 0, 11385 "purge()", 11386 " Purge the collected PC count profiling data."), 11387 11388 JS_FN_HELP("count", PCCountProfiling_ScriptCount, 0, 0, 11389 "count()", 11390 " Return the number of profiled scripts."), 11391 11392 JS_FN_HELP("summary", PCCountProfiling_ScriptSummary, 1, 0, 11393 "summary(index)", 11394 " Return the PC count profiling summary for the given script index.\n" 11395 " The script index must be in the range [0, pc.count())."), 11396 11397 JS_FN_HELP("contents", PCCountProfiling_ScriptContents, 1, 0, 11398 "contents(index)", 11399 " Return the complete profiling contents for the given script index.\n" 11400 " The script index must be in the range [0, pc.count())."), 11401 11402 JS_FS_HELP_END 11403 }; 11404 // clang-format on 11405 11406 // clang-format off 11407 static const JSFunctionSpecWithHelp FdLibMTestingFunctions[] = { 11408 JS_FN_HELP("pow", FdLibM_Pow, 2, 0, 11409 "pow(x, y)", 11410 " Return x ** y."), 11411 11412 JS_FS_HELP_END 11413 }; 11414 // clang-format on 11415 11416 bool js::InitTestingFunctions() { return disasmPrinter.init(); } 11417 11418 bool js::DefineTestingFunctions(JSContext* cx, HandleObject obj, 11419 bool fuzzingSafe_, bool disableOOMFunctions_) { 11420 fuzzingSafe = fuzzingSafe_; 11421 if (EnvVarIsDefined("MOZ_FUZZING_SAFE")) { 11422 fuzzingSafe = true; 11423 } 11424 11425 disableOOMFunctions = disableOOMFunctions_; 11426 11427 if (!fuzzingSafe) { 11428 if (!JS_DefineFunctionsWithHelp(cx, obj, FuzzingUnsafeTestingFunctions)) { 11429 return false; 11430 } 11431 11432 RootedObject pccount(cx, JS_NewPlainObject(cx)); 11433 if (!pccount) { 11434 return false; 11435 } 11436 11437 if (!JS_DefineProperty(cx, obj, "pccount", pccount, 0)) { 11438 return false; 11439 } 11440 11441 if (!JS_DefineFunctionsWithHelp(cx, pccount, 11442 PCCountProfilingTestingFunctions)) { 11443 return false; 11444 } 11445 } 11446 11447 RootedObject fdlibm(cx, JS_NewPlainObject(cx)); 11448 if (!fdlibm) { 11449 return false; 11450 } 11451 11452 if (!JS_DefineProperty(cx, obj, "fdlibm", fdlibm, 0)) { 11453 return false; 11454 } 11455 11456 if (!JS_DefineFunctionsWithHelp(cx, fdlibm, FdLibMTestingFunctions)) { 11457 return false; 11458 } 11459 11460 return JS_DefineFunctionsWithHelp(cx, obj, TestingFunctions); 11461 } 11462 11463 #ifdef FUZZING_JS_FUZZILLI 11464 uint32_t js::FuzzilliHashDouble(double value) { 11465 // We shouldn't GC here as this is called directly from IC code. 11466 AutoUnsafeCallWithABI unsafe; 11467 uint64_t v = mozilla::BitwiseCast<uint64_t>(value); 11468 return static_cast<uint32_t>(v) + static_cast<uint32_t>(v >> 32); 11469 } 11470 11471 uint32_t js::FuzzilliHashBigInt(BigInt* bigInt) { 11472 // We shouldn't GC here as this is called directly from IC code. 11473 AutoUnsafeCallWithABI unsafe; 11474 return bigInt->hash(); 11475 } 11476 11477 void js::FuzzilliHashObject(JSContext* cx, JSObject* obj) { 11478 // called from IC and baseline/interpreter 11479 uint32_t hash; 11480 FuzzilliHashObjectInl(cx, obj, &hash); 11481 11482 cx->executionHashInputs += 1; 11483 cx->executionHash = mozilla::RotateLeft(cx->executionHash + hash, 1); 11484 } 11485 11486 void js::FuzzilliHashObjectInl(JSContext* cx, JSObject* obj, uint32_t* out) { 11487 *out = 0; 11488 if (!js::SupportDifferentialTesting()) { 11489 return; 11490 } 11491 11492 RootedValue v(cx); 11493 v.setObject(*obj); 11494 11495 JSAutoStructuredCloneBuffer JSCloner( 11496 JS::StructuredCloneScope::DifferentProcess, nullptr, nullptr); 11497 if (JSCloner.write(cx, v)) { 11498 JSStructuredCloneData& data = JSCloner.data(); 11499 data.ForEachDataChunk([&](const char* aData, size_t aSize) { 11500 uint32_t h = mozilla::HashBytes(aData, aSize); 11501 h = (h << 1) | 1; 11502 *out ^= h; 11503 *out *= h; 11504 return true; 11505 }); 11506 } else if (JS_IsExceptionPending(cx)) { 11507 JS_ClearPendingException(cx); 11508 } 11509 } 11510 #endif