js.cpp (451622B)
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 /* JS shell. */ 8 9 #include "mozilla/AlreadyAddRefed.h" // mozilla::already_AddRefed 10 #include "mozilla/ArrayUtils.h" 11 #include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_ASSERT_IF, MOZ_RELEASE_ASSERT, MOZ_CRASH 12 #include "mozilla/Atomics.h" 13 #include "mozilla/Attributes.h" 14 #include "mozilla/Compression.h" 15 #include "mozilla/DebugOnly.h" 16 #include "mozilla/EnumSet.h" 17 #include "mozilla/IntegerPrintfMacros.h" 18 #include "mozilla/mozalloc.h" 19 #include "mozilla/PodOperations.h" 20 #include "mozilla/RandomNum.h" 21 #include "mozilla/RefPtr.h" 22 #include "mozilla/ScopeExit.h" 23 #include "mozilla/Sprintf.h" 24 #include "mozilla/TimeStamp.h" 25 #include "mozilla/UniquePtrExtensions.h" // UniqueFreePtr 26 #include "mozilla/Utf8.h" 27 #include "mozilla/Variant.h" 28 29 #include <algorithm> 30 #include <cctype> 31 #include <chrono> 32 #ifdef XP_WIN 33 # include <direct.h> 34 # include <process.h> 35 #endif 36 #include <errno.h> 37 #include <fcntl.h> 38 #if defined(XP_WIN) 39 # include <io.h> /* for isatty() */ 40 #endif 41 #include <locale.h> 42 #if defined(MALLOC_H) 43 # include MALLOC_H /* for malloc_usable_size, malloc_size, _msize */ 44 #endif 45 #include <ctime> 46 #include <math.h> 47 #ifndef __wasi__ 48 # include <signal.h> 49 #endif 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <sys/stat.h> 54 #include <sys/types.h> 55 #include <utility> 56 #ifdef XP_UNIX 57 # ifndef __wasi__ 58 # include <sys/mman.h> 59 # include <sys/wait.h> 60 # endif 61 # include <sys/stat.h> 62 # include <unistd.h> 63 #endif 64 #ifdef XP_LINUX 65 # include <sys/prctl.h> 66 #endif 67 68 #include "jsapi.h" 69 #include "jsfriendapi.h" 70 #include "jstypes.h" 71 #include "fmt/format.h" 72 #ifndef JS_WITHOUT_NSPR 73 # include "prerror.h" 74 # include "prlink.h" 75 #endif 76 77 #include "builtin/Array.h" 78 #include "builtin/MapObject.h" 79 #include "builtin/ModuleObject.h" 80 #include "builtin/RegExp.h" 81 #include "builtin/TestingFunctions.h" 82 #include "builtin/TestingUtility.h" // js::ParseCompileOptions, js::ParseDebugMetadata, js::CreateScriptPrivate 83 #include "debugger/DebugAPI.h" 84 #include "frontend/CompilationStencil.h" 85 #include "frontend/FrontendContext.h" // AutoReportFrontendContext 86 #include "frontend/ModuleSharedContext.h" 87 #include "frontend/Parser.h" 88 #include "frontend/ScopeBindingCache.h" // js::frontend::ScopeBindingCache 89 #include "gc/GC.h" 90 #include "gc/PublicIterators.h" 91 #ifdef DEBUG 92 # include "irregexp/RegExpAPI.h" 93 #endif 94 95 #ifdef JS_SIMULATOR_ARM 96 # include "jit/arm/Simulator-arm.h" 97 #endif 98 #ifdef JS_SIMULATOR_MIPS64 99 # include "jit/mips64/Simulator-mips64.h" 100 #endif 101 #ifdef JS_SIMULATOR_LOONG64 102 # include "jit/loong64/Simulator-loong64.h" 103 #endif 104 #ifdef JS_SIMULATOR_RISCV64 105 # include "jit/riscv64/Simulator-riscv64.h" 106 #endif 107 #include "jit/BaselineCompileQueue.h" 108 #include "jit/CacheIRHealth.h" 109 #include "jit/InlinableNatives.h" 110 #include "jit/Ion.h" 111 #include "jit/JitcodeMap.h" 112 #include "jit/JitZone.h" 113 #include "jit/shared/CodeGenerator-shared.h" 114 #ifdef JS_CODEGEN_ARM64 115 # include "jit/arm64/vixl/Cpu-Features-vixl.h" 116 #endif 117 #include "js/Array.h" // JS::NewArrayObject 118 #include "js/ArrayBuffer.h" // JS::{CreateMappedArrayBufferContents,NewMappedArrayBufferWithContents,IsArrayBufferObject,GetArrayBufferLengthAndData} 119 #include "js/BuildId.h" // JS::BuildIdCharVector, JS::SetProcessBuildIdOp 120 #include "js/CallAndConstruct.h" // JS::Call, JS::IsCallable, JS_CallFunction, JS_CallFunctionValue 121 #include "js/CharacterEncoding.h" // JS::StringIsASCII 122 #include "js/CompilationAndEvaluation.h" 123 #include "js/CompileOptions.h" // JS::ReadOnlyCompileOptions, JS::CompileOptions, JS::OwningCompileOptions, JS::DecodeOptions, JS::InstantiateOptions 124 #include "js/ContextOptions.h" // JS::ContextOptions{,Ref} 125 #include "js/Debug.h" // JS::dbg::ShouldAvoidSideEffects, JS::ExecutionTrace 126 #include "js/EnvironmentChain.h" // JS::EnvironmentChain 127 #include "js/Equality.h" // JS::SameValue 128 #include "js/ErrorReport.h" // JS::PrintError 129 #include "js/Exception.h" // JS::StealPendingExceptionStack 130 #include "js/experimental/BindingAllocs.h" // JS_NewObjectWithGivenProtoAndUseAllocSite 131 #include "js/experimental/CodeCoverage.h" // js::EnableCodeCoverage 132 #include "js/experimental/CompileScript.h" // JS::NewFrontendContext, JS::DestroyFrontendContext, JS::HadFrontendErrors, JS::ConvertFrontendErrorsToRuntimeErrors, JS::CompileGlobalScriptToStencil, JS::CompileModuleScriptToStencil 133 #include "js/experimental/CTypes.h" // JS::InitCTypesClass 134 #include "js/experimental/Intl.h" // JS::AddMoz{DateTimeFormat,DisplayNames}Constructor 135 #include "js/experimental/JitInfo.h" // JSJit{Getter,Setter,Method}CallArgs, JSJitGetterInfo, JSJit{Getter,Setter}Op, JSJitInfo 136 #include "js/experimental/JSStencil.h" // JS::Stencil, JS::DecodeStencil, JS::InstantiateModuleStencil 137 #include "js/experimental/SourceHook.h" // js::{Set,Forget,}SourceHook 138 #include "js/experimental/TypedData.h" // JS_NewUint8Array 139 #include "js/friend/DumpFunctions.h" // JS::FormatStackDump 140 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* 141 #include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit 142 #include "js/friend/WindowProxy.h" // js::IsWindowProxy, js::SetWindowProxyClass, js::ToWindowProxyIfWindow, js::ToWindowIfWindowProxy 143 #include "js/GCAPI.h" // JS::AutoCheckCannotGC 144 #include "js/GCVector.h" 145 #include "js/GlobalObject.h" 146 #include "js/Initialization.h" 147 #include "js/Interrupt.h" 148 #include "js/JSON.h" 149 #include "js/MemoryCallbacks.h" 150 #include "js/MemoryFunctions.h" 151 #include "js/Modules.h" // JS::GetModulePrivate, JS::SetModule{DynamicImport,Metadata,Resolve}Hook, JS::SetModulePrivate, JS::CompileModule 152 #include "js/Object.h" // JS::GetClass, JS::GetCompartment, JS::GetReservedSlot, JS::SetReservedSlot 153 #include "js/Prefs.h" 154 #include "js/Principals.h" 155 #include "js/Printer.h" // QuoteString 156 #include "js/Printf.h" 157 #include "js/PropertyAndElement.h" // JS_DefineElement, JS_DefineFunction, JS_DefineFunctions, JS_DefineProperties, JS_DefineProperty, JS_GetElement, JS_GetProperty, JS_GetPropertyById, JS_HasProperty, JS_SetElement, JS_SetProperty, JS_SetPropertyById 158 #include "js/PropertySpec.h" 159 #include "js/Realm.h" 160 #include "js/RegExp.h" // JS::ObjectIsRegExp 161 #include "js/ScriptPrivate.h" 162 #include "js/SourceText.h" // JS::SourceText 163 #include "js/StableStringChars.h" 164 #include "js/Stack.h" 165 #include "js/StreamConsumer.h" 166 #include "js/StructuredClone.h" 167 #include "js/SweepingAPI.h" 168 #include "js/Transcoding.h" // JS::TranscodeBuffer, JS::TranscodeRange, JS::IsTranscodeFailureResult 169 #include "js/Warnings.h" // JS::SetWarningReporter 170 #include "js/WasmFeatures.h" // JS_FOR_WASM_FEATURES 171 #include "js/WasmModule.h" // JS::WasmModule 172 #include "js/Wrapper.h" 173 #include "proxy/DeadObjectProxy.h" // js::IsDeadProxyObject 174 #include "shell/jsoptparse.h" 175 #include "shell/jsshell.h" 176 #include "shell/OSObject.h" 177 #include "shell/ShellModuleObjectWrapper.h" 178 #include "shell/WasmTesting.h" 179 #include "threading/ConditionVariable.h" 180 #include "threading/ExclusiveData.h" 181 #include "threading/LockGuard.h" 182 #include "threading/Thread.h" 183 #include "util/CompleteFile.h" // js::FileContents, js::ReadCompleteFile 184 #include "util/DifferentialTesting.h" 185 #include "util/StringBuilder.h" 186 #include "util/Text.h" 187 #include "util/WindowsWrapper.h" 188 #include "vm/ArgumentsObject.h" 189 #include "vm/Compression.h" 190 #include "vm/ErrorObject.h" 191 #include "vm/ErrorReporting.h" 192 #include "vm/HelperThreads.h" 193 #include "vm/JSAtomUtils.h" // AtomizeUTF8Chars, AtomizeString, ToAtom 194 #include "vm/JSContext.h" 195 #include "vm/JSFunction.h" 196 #include "vm/JSObject.h" 197 #include "vm/JSScript.h" 198 #include "vm/Logging.h" 199 #include "vm/ModuleBuilder.h" // js::ModuleBuilder 200 #include "vm/Modules.h" 201 #include "vm/Monitor.h" 202 #include "vm/MutexIDs.h" 203 #include "vm/PromiseObject.h" // js::PromiseObject 204 #include "vm/Shape.h" 205 #include "vm/SharedArrayObject.h" 206 #include "vm/StencilObject.h" // js::StencilObject 207 #include "vm/Time.h" 208 #include "vm/ToSource.h" // js::ValueToSource 209 #include "vm/TypedArrayObject.h" 210 #include "vm/WrapperObject.h" 211 #include "wasm/WasmFeatures.h" 212 #include "wasm/WasmJS.h" 213 214 #include "gc/WeakMap-inl.h" 215 #include "vm/Compartment-inl.h" 216 #include "vm/ErrorObject-inl.h" 217 #include "vm/Interpreter-inl.h" 218 #include "vm/JSObject-inl.h" 219 #include "vm/Realm-inl.h" 220 #include "vm/Stack-inl.h" 221 222 #undef compress 223 224 using namespace js; 225 using namespace js::cli; 226 using namespace js::shell; 227 228 using JS::AutoStableStringChars; 229 using JS::CompileOptions; 230 231 using js::shell::RCFile; 232 233 using mozilla::ArrayEqual; 234 using mozilla::AsVariant; 235 using mozilla::Atomic; 236 using mozilla::MakeScopeExit; 237 using mozilla::Maybe; 238 using mozilla::Nothing; 239 using mozilla::NumberEqualsInt32; 240 using mozilla::TimeDuration; 241 using mozilla::TimeStamp; 242 using mozilla::Utf8Unit; 243 using mozilla::Variant; 244 245 bool InitOptionParser(OptionParser& op); 246 bool SetGlobalOptionsPreJSInit(const OptionParser& op); 247 bool SetGlobalOptionsPostJSInit(const OptionParser& op); 248 bool SetContextOptions(JSContext* cx, const OptionParser& op); 249 bool SetContextWasmOptions(JSContext* cx, const OptionParser& op); 250 bool SetContextJITOptions(JSContext* cx, const OptionParser& op); 251 bool SetContextGCOptions(JSContext* cx, const OptionParser& op); 252 bool InitModuleLoader(JSContext* cx, const OptionParser& op); 253 254 #ifdef FUZZING_JS_FUZZILLI 255 # define REPRL_CRFD 100 256 # define REPRL_CWFD 101 257 # define REPRL_DRFD 102 258 # define REPRL_DWFD 103 259 260 # define SHM_SIZE 0x100000 261 # define MAX_EDGES ((SHM_SIZE - 4) * 8) 262 263 struct shmem_data { 264 uint32_t num_edges; 265 unsigned char edges[]; 266 }; 267 268 struct shmem_data* __shmem; 269 270 uint32_t *__edges_start, *__edges_stop; 271 void __sanitizer_cov_reset_edgeguards() { 272 uint64_t N = 0; 273 for (uint32_t* x = __edges_start; x < __edges_stop && N < MAX_EDGES; x++) 274 *x = ++N; 275 } 276 277 extern "C" void __sanitizer_cov_trace_pc_guard_init(uint32_t* start, 278 uint32_t* stop) { 279 // Avoid duplicate initialization 280 if (start == stop || *start) return; 281 282 if (__edges_start != NULL || __edges_stop != NULL) { 283 fprintf(stderr, 284 "Coverage instrumentation is only supported for a single module\n"); 285 _exit(-1); 286 } 287 288 __edges_start = start; 289 __edges_stop = stop; 290 291 // Map the shared memory region 292 const char* shm_key = getenv("SHM_ID"); 293 if (!shm_key) { 294 puts("[COV] no shared memory bitmap available, skipping"); 295 __shmem = (struct shmem_data*)malloc(SHM_SIZE); 296 } else { 297 int fd = shm_open(shm_key, O_RDWR, S_IREAD | S_IWRITE); 298 if (fd <= -1) { 299 fprintf(stderr, "Failed to open shared memory region: %s\n", 300 strerror(errno)); 301 _exit(-1); 302 } 303 304 __shmem = (struct shmem_data*)mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, 305 MAP_SHARED, fd, 0); 306 if (__shmem == MAP_FAILED) { 307 fprintf(stderr, "Failed to mmap shared memory region\n"); 308 _exit(-1); 309 } 310 } 311 312 __sanitizer_cov_reset_edgeguards(); 313 314 __shmem->num_edges = stop - start; 315 printf("[COV] edge counters initialized. Shared memory: %s with %u edges\n", 316 shm_key, __shmem->num_edges); 317 } 318 319 extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t* guard) { 320 // There's a small race condition here: if this function executes in two 321 // threads for the same edge at the same time, the first thread might disable 322 // the edge (by setting the guard to zero) before the second thread fetches 323 // the guard value (and thus the index). However, our instrumentation ignores 324 // the first edge (see libcoverage.c) and so the race is unproblematic. 325 uint32_t index = *guard; 326 // If this function is called before coverage instrumentation is properly 327 // initialized we want to return early. 328 if (!index) return; 329 __shmem->edges[index / 8] |= 1 << (index % 8); 330 *guard = 0; 331 } 332 #endif /* FUZZING_JS_FUZZILLI */ 333 334 enum JSShellExitCode { 335 EXITCODE_RUNTIME_ERROR = 3, 336 EXITCODE_FILE_NOT_FOUND = 4, 337 EXITCODE_OUT_OF_MEMORY = 5, 338 EXITCODE_TIMEOUT = 6 339 }; 340 341 struct ShellLogModule { 342 // Since ShellLogModules have references to their levels created 343 // we can't move them. 344 ShellLogModule(ShellLogModule&&) = delete; 345 346 const char* name; 347 explicit ShellLogModule(const char* name) : name(name) {} 348 mozilla::AtomicLogLevel level; 349 }; 350 351 // If asserts related to this ever fail, simply bump this number. 352 // 353 // This is used to construct a mozilla::Array, which is used because a 354 // ShellLogModule cannot move once constructed to avoid invalidating 355 // a levelRef. 356 static const int MAX_LOG_MODULES = 64; 357 static int initialized_modules = 0; 358 mozilla::Array<mozilla::Maybe<ShellLogModule>, MAX_LOG_MODULES> logModules; 359 360 JS::OpaqueLogger GetLoggerByName(const char* name) { 361 // Check for pre-existing module 362 for (auto& logger : logModules) { 363 if (logger) { 364 if (logger->name == name) { 365 return logger.ptr(); 366 } 367 } 368 // We've seen all initialized, not there, break out. 369 if (!logger) break; 370 } 371 372 // Not found, allocate a new module. 373 MOZ_RELEASE_ASSERT(initialized_modules < MAX_LOG_MODULES - 1); 374 auto index = initialized_modules++; 375 logModules[index].emplace(name); 376 return logModules[index].ptr(); 377 } 378 379 mozilla::AtomicLogLevel& GetLevelRef(JS::OpaqueLogger logger) { 380 ShellLogModule* slm = static_cast<ShellLogModule*>(logger); 381 return slm->level; 382 } 383 384 void LogPrintVA(const JS::OpaqueLogger logger, mozilla::LogLevel level, 385 const char* fmt, va_list ap) { 386 ShellLogModule* mod = static_cast<ShellLogModule*>(logger); 387 fprintf(stderr, "[%s] ", mod->name); 388 vfprintf(stderr, fmt, ap); 389 fprintf(stderr, "\n"); 390 } 391 392 void LogPrintFmt(const JS::OpaqueLogger logger, mozilla::LogLevel level, 393 fmt::string_view fmt, fmt::format_args args) { 394 ShellLogModule* mod = static_cast<ShellLogModule*>(logger); 395 fmt::print(stderr, FMT_STRING("[{}] {}\n"), mod->name, 396 fmt::vformat(fmt, args)); 397 } 398 399 JS::LoggingInterface shellLoggingInterface = {GetLoggerByName, LogPrintVA, 400 LogPrintFmt, GetLevelRef}; 401 402 static void ToLower(const char* src, char* dest, size_t len) { 403 for (size_t c = 0; c < len; c++) { 404 dest[c] = (char)(tolower(src[c])); 405 } 406 } 407 408 // This should be run once after initialization, and may be rerun (via the 409 // mozLog() command) again later. 410 static void ParseLoggerOptions(mozilla::Range<const char> mixedCaseOpts) { 411 // Copy into a new buffer and lower case to do case insensitive matching. 412 // 413 // Done this way rather than just using strcasestr because Windows doesn't 414 // have strcasestr as part of its base C library. 415 size_t len = mixedCaseOpts.length(); 416 mozilla::UniqueFreePtr<char[]> logOpts( 417 static_cast<char*>(calloc(len + 1, 1))); 418 if (!logOpts) { 419 return; 420 } 421 422 ToLower(mixedCaseOpts.begin().get(), logOpts.get(), len); 423 logOpts.get()[len] = '\0'; 424 425 // This is a really permissive parser, but will suffice! 426 for (auto& logger : logModules) { 427 if (logger) { 428 // Lowercase the logger name for strstr 429 size_t len = strlen(logger->name); 430 mozilla::UniqueFreePtr<char[]> lowerName( 431 static_cast<char*>(calloc(len + 1, 1))); 432 ToLower(logger->name, lowerName.get(), len); 433 lowerName.get()[len] = '\0'; 434 435 int logLevel = 0; 436 if (char* needle = strstr(logOpts.get(), lowerName.get())) { 437 // If the string to enable a logger is present, but no level is provided 438 // then default to Debug level. 439 logLevel = static_cast<int>(mozilla::LogLevel::Debug); 440 441 if (char* colon = strchr(needle, ':')) { 442 // Parse character after colon as log level. 443 if (*(colon + 1)) { 444 logLevel = atoi(colon + 1); 445 } 446 } 447 448 fprintf(stderr, "[JS_LOG] Enabling Logger %s at level %d\n", 449 logger->name, logLevel); 450 } else { 451 if (logger->level != mozilla::ToLogLevel(logLevel)) { 452 fprintf(stderr, "[JS_LOG] Resetting Logger %s to level %d\n", 453 logger->name, logLevel); 454 } 455 } 456 457 logger->level = mozilla::ToLogLevel(logLevel); 458 } 459 } 460 } 461 462 static bool SetMozLog(JSContext* cx, unsigned argc, Value* vp) { 463 CallArgs args = CallArgsFromVp(argc, vp); 464 465 Rooted<JSString*> spec(cx, ToString(cx, args.get(0))); 466 if (!spec) { 467 return false; 468 } 469 470 if (!spec->hasLatin1Chars()) { 471 JS_ReportErrorASCII(cx, "invalid MOZ_LOG setting"); 472 return false; 473 } 474 475 AutoStableStringChars stable(cx); 476 if (!stable.init(cx, spec)) { 477 return false; 478 } 479 480 mozilla::Range<const Latin1Char> r = stable.latin1Range(); 481 ParseLoggerOptions({(const char*)r.begin().get(), r.length()}); 482 return true; 483 } 484 485 /* 486 * Limit the timeout to 30 minutes to prevent an overflow on platfoms 487 * that represent the time internally in microseconds using 32-bit int. 488 */ 489 static const double MAX_TIMEOUT_SECONDS = 1800.0; 490 491 // Not necessarily in sync with the browser 492 #ifdef ENABLE_SHARED_MEMORY 493 # define SHARED_MEMORY_DEFAULT 1 494 #else 495 # define SHARED_MEMORY_DEFAULT 0 496 #endif 497 498 // Fuzzing support for JS runtime fuzzing 499 #ifdef FUZZING_INTERFACES 500 # include "shell/jsrtfuzzing/jsrtfuzzing.h" 501 MOZ_RUNINIT static bool fuzzDoDebug = !!getenv("MOZ_FUZZ_DEBUG"); 502 MOZ_RUNINIT static bool fuzzHaveModule = !!getenv("FUZZER"); 503 #endif // FUZZING_INTERFACES 504 505 // Code to support GCOV code coverage measurements on standalone shell 506 #ifdef MOZ_CODE_COVERAGE 507 # if defined(__GNUC__) && !defined(__clang__) 508 extern "C" void __gcov_dump(); 509 extern "C" void __gcov_reset(); 510 511 void counters_dump(int) { __gcov_dump(); } 512 513 void counters_reset(int) { __gcov_reset(); } 514 # else 515 void counters_dump(int) { /* Do nothing */ } 516 517 void counters_reset(int) { /* Do nothing */ } 518 # endif 519 520 static void InstallCoverageSignalHandlers() { 521 # ifndef XP_WIN 522 fprintf(stderr, "[CodeCoverage] Setting handlers for process %d.\n", 523 getpid()); 524 525 struct sigaction dump_sa; 526 dump_sa.sa_handler = counters_dump; 527 dump_sa.sa_flags = SA_RESTART; 528 sigemptyset(&dump_sa.sa_mask); 529 mozilla::DebugOnly<int> r1 = sigaction(SIGUSR1, &dump_sa, nullptr); 530 MOZ_ASSERT(r1 == 0, "Failed to install GCOV SIGUSR1 handler"); 531 532 struct sigaction reset_sa; 533 reset_sa.sa_handler = counters_reset; 534 reset_sa.sa_flags = SA_RESTART; 535 sigemptyset(&reset_sa.sa_mask); 536 mozilla::DebugOnly<int> r2 = sigaction(SIGUSR2, &reset_sa, nullptr); 537 MOZ_ASSERT(r2 == 0, "Failed to install GCOV SIGUSR2 handler"); 538 # endif 539 } 540 #endif 541 542 // An off-thread parse or decode job. 543 class js::shell::OffThreadJob { 544 static constexpr size_t kCompileStackQuota = 128 * sizeof(size_t) * 1024; 545 static constexpr size_t kThreadStackQuota = 546 kCompileStackQuota + 128 * sizeof(size_t) * 1024; 547 548 enum State { 549 RUNNING, // Working; no stencil. 550 DONE, // Finished; have stencil. 551 CANCELLED // Cancelled due to error. 552 }; 553 554 public: 555 enum class Kind { 556 CompileScript, 557 CompileModule, 558 Decode, 559 }; 560 561 OffThreadJob(ShellContext* sc, Kind kind, JS::SourceText<char16_t>&& srcBuf); 562 OffThreadJob(ShellContext* sc, Kind kind, JS::TranscodeBuffer&& xdrBuf); 563 564 ~OffThreadJob(); 565 566 bool init(JSContext* cx, const JS::ReadOnlyCompileOptions& options); 567 bool dispatch(); 568 569 static void OffThreadMain(OffThreadJob* self); 570 void run(); 571 572 void cancel(); 573 void waitUntilDone(); 574 575 already_AddRefed<JS::Stencil> stealStencil(JSContext* cx); 576 577 public: 578 const int32_t id; 579 580 private: 581 Kind kind_; 582 State state_; 583 584 JS::FrontendContext* fc_ = nullptr; 585 JS::OwningCompileOptions options_; 586 587 UniquePtr<Thread> thread_; 588 589 JS::SourceText<char16_t> srcBuf_; 590 JS::TranscodeBuffer xdrBuf_; 591 592 RefPtr<JS::Stencil> stencil_; 593 594 JS::TranscodeResult transcodeResult_ = JS::TranscodeResult::Ok; 595 }; 596 597 template <typename T> 598 static OffThreadJob* NewOffThreadJob(JSContext* cx, OffThreadJob::Kind kind, 599 JS::ReadOnlyCompileOptions& options, 600 T&& source) { 601 ShellContext* sc = GetShellContext(cx); 602 if (sc->isWorker) { 603 // Off-thread compilation/decode is used by main-thread, in order to improve 604 // the responsiveness. It's not used by worker in browser, and there's not 605 // much reason to support worker here. 606 JS_ReportErrorASCII(cx, "Off-thread job is not supported in worker"); 607 return nullptr; 608 } 609 610 UniquePtr<OffThreadJob> job( 611 cx->new_<OffThreadJob>(sc, kind, std::move(source))); 612 if (!job) { 613 return nullptr; 614 } 615 616 if (!job->init(cx, options)) { 617 return nullptr; 618 } 619 620 if (!sc->offThreadJobs.append(job.get())) { 621 job->cancel(); 622 JS_ReportErrorASCII(cx, "OOM adding off-thread job"); 623 return nullptr; 624 } 625 626 return job.release(); 627 } 628 629 static OffThreadJob* GetSingleOffThreadJob(JSContext* cx) { 630 ShellContext* sc = GetShellContext(cx); 631 const auto& jobs = sc->offThreadJobs; 632 if (jobs.empty()) { 633 JS_ReportErrorASCII(cx, "No off-thread jobs are pending"); 634 return nullptr; 635 } 636 637 if (jobs.length() > 1) { 638 JS_ReportErrorASCII( 639 cx, "Multiple off-thread jobs are pending: must specify job ID"); 640 return nullptr; 641 } 642 643 return jobs[0]; 644 } 645 646 static OffThreadJob* LookupOffThreadJobByID(JSContext* cx, int32_t id) { 647 if (id <= 0) { 648 JS_ReportErrorASCII(cx, "Bad off-thread job ID"); 649 return nullptr; 650 } 651 652 ShellContext* sc = GetShellContext(cx); 653 const auto& jobs = sc->offThreadJobs; 654 if (jobs.empty()) { 655 JS_ReportErrorASCII(cx, "No off-thread jobs are pending"); 656 return nullptr; 657 } 658 659 OffThreadJob* job = nullptr; 660 for (auto someJob : jobs) { 661 if (someJob->id == id) { 662 job = someJob; 663 break; 664 } 665 } 666 667 if (!job) { 668 JS_ReportErrorASCII(cx, "Off-thread job not found"); 669 return nullptr; 670 } 671 672 return job; 673 } 674 675 static OffThreadJob* LookupOffThreadJobForArgs(JSContext* cx, 676 const CallArgs& args, 677 size_t arg) { 678 // If the optional ID argument isn't present, get the single pending job. 679 if (args.length() <= arg) { 680 return GetSingleOffThreadJob(cx); 681 } 682 683 // Lookup the job using the specified ID. 684 int32_t id = 0; 685 RootedValue value(cx, args[arg]); 686 if (!ToInt32(cx, value, &id)) { 687 return nullptr; 688 } 689 690 return LookupOffThreadJobByID(cx, id); 691 } 692 693 static void DeleteOffThreadJob(JSContext* cx, OffThreadJob* job) { 694 ShellContext* sc = GetShellContext(cx); 695 for (size_t i = 0; i < sc->offThreadJobs.length(); i++) { 696 if (sc->offThreadJobs[i] == job) { 697 sc->offThreadJobs.erase(&sc->offThreadJobs[i]); 698 js_delete(job); 699 return; 700 } 701 } 702 703 MOZ_CRASH("Off-thread job not found"); 704 } 705 706 static void CancelOffThreadJobsForRuntime(JSContext* cx) { 707 ShellContext* sc = GetShellContext(cx); 708 while (!sc->offThreadJobs.empty()) { 709 OffThreadJob* job = sc->offThreadJobs.popCopy(); 710 job->waitUntilDone(); 711 js_delete(job); 712 } 713 } 714 715 mozilla::Atomic<int32_t> gOffThreadJobSerial(1); 716 717 OffThreadJob::OffThreadJob(ShellContext* sc, Kind kind, 718 JS::SourceText<char16_t>&& srcBuf) 719 : id(gOffThreadJobSerial++), 720 kind_(kind), 721 state_(RUNNING), 722 options_(JS::OwningCompileOptions::ForFrontendContext()), 723 srcBuf_(std::move(srcBuf)) { 724 MOZ_RELEASE_ASSERT(id > 0, "Off-thread job IDs exhausted"); 725 } 726 727 OffThreadJob::OffThreadJob(ShellContext* sc, Kind kind, 728 JS::TranscodeBuffer&& xdrBuf) 729 : id(gOffThreadJobSerial++), 730 kind_(kind), 731 state_(RUNNING), 732 options_(JS::OwningCompileOptions::ForFrontendContext()), 733 xdrBuf_(std::move(xdrBuf)) { 734 MOZ_RELEASE_ASSERT(id > 0, "Off-thread job IDs exhausted"); 735 } 736 737 OffThreadJob::~OffThreadJob() { 738 if (fc_) { 739 JS::DestroyFrontendContext(fc_); 740 } 741 MOZ_ASSERT(state_ != RUNNING); 742 } 743 744 bool OffThreadJob::init(JSContext* cx, 745 const JS::ReadOnlyCompileOptions& options) { 746 fc_ = JS::NewFrontendContext(); 747 if (!fc_) { 748 ReportOutOfMemory(cx); 749 state_ = CANCELLED; 750 return false; 751 } 752 753 if (!options_.copy(cx, options)) { 754 state_ = CANCELLED; 755 return false; 756 } 757 758 return true; 759 } 760 761 bool OffThreadJob::dispatch() { 762 thread_ = 763 js::MakeUnique<Thread>(Thread::Options().setStackSize(kThreadStackQuota)); 764 if (!thread_) { 765 state_ = CANCELLED; 766 return false; 767 } 768 769 if (!thread_->init(OffThreadJob::OffThreadMain, this)) { 770 state_ = CANCELLED; 771 thread_ = nullptr; 772 return false; 773 } 774 775 return true; 776 } 777 778 /* static */ void OffThreadJob::OffThreadMain(OffThreadJob* self) { 779 self->run(); 780 } 781 782 void OffThreadJob::run() { 783 MOZ_ASSERT(state_ == RUNNING); 784 MOZ_ASSERT(!stencil_); 785 786 JS::SetNativeStackQuota(fc_, kCompileStackQuota); 787 788 switch (kind_) { 789 case Kind::CompileScript: { 790 stencil_ = JS::CompileGlobalScriptToStencil(fc_, options_, srcBuf_); 791 break; 792 } 793 case Kind::CompileModule: { 794 stencil_ = JS::CompileModuleScriptToStencil(fc_, options_, srcBuf_); 795 break; 796 } 797 case Kind::Decode: { 798 JS::DecodeOptions decodeOptions(options_); 799 JS::TranscodeRange range(xdrBuf_.begin(), xdrBuf_.length()); 800 transcodeResult_ = JS::DecodeStencil(fc_, decodeOptions, range, 801 getter_AddRefs(stencil_)); 802 break; 803 } 804 } 805 806 state_ = DONE; 807 } 808 809 void OffThreadJob::cancel() { 810 MOZ_ASSERT(state_ == RUNNING); 811 MOZ_ASSERT(!stencil_); 812 MOZ_ASSERT(!thread_, "cannot cancel after starting a thread"); 813 814 state_ = CANCELLED; 815 } 816 817 void OffThreadJob::waitUntilDone() { 818 MOZ_ASSERT(state_ != CANCELLED); 819 thread_->join(); 820 } 821 822 already_AddRefed<JS::Stencil> OffThreadJob::stealStencil(JSContext* cx) { 823 JS::FrontendContext* fc = fc_; 824 fc_ = nullptr; 825 auto destroyFrontendContext = 826 mozilla::MakeScopeExit([&]() { JS::DestroyFrontendContext(fc); }); 827 828 MOZ_ASSERT(fc); 829 830 if (JS::HadFrontendErrors(fc)) { 831 (void)JS::ConvertFrontendErrorsToRuntimeErrors(cx, fc, options_); 832 return nullptr; 833 } 834 835 if (!stencil_ && JS::IsTranscodeFailureResult(transcodeResult_)) { 836 JS_ReportErrorASCII(cx, "failed to decode cache"); 837 return nullptr; 838 } 839 840 // Report warnings. 841 if (!JS::ConvertFrontendErrorsToRuntimeErrors(cx, fc, options_)) { 842 return nullptr; 843 } 844 845 return stencil_.forget(); 846 } 847 848 struct ShellCompartmentPrivate { 849 GCPtr<ArrayObject*> blackRoot; 850 GCPtr<ArrayObject*> grayRoot; 851 }; 852 853 struct MOZ_STACK_CLASS EnvironmentPreparer 854 : public js::ScriptEnvironmentPreparer { 855 explicit EnvironmentPreparer(JSContext* cx) { 856 js::SetScriptEnvironmentPreparer(cx, this); 857 } 858 void invoke(JS::HandleObject global, Closure& closure) override; 859 }; 860 861 const char* shell::selfHostedXDRPath = nullptr; 862 bool shell::encodeSelfHostedCode = false; 863 bool shell::enableCodeCoverage = false; 864 bool shell::enableDisassemblyDumps = false; 865 bool shell::offthreadBaselineCompilation = false; 866 bool shell::offthreadIonCompilation = false; 867 JS::DelazificationOption shell::defaultDelazificationMode = 868 JS::DelazificationOption::OnDemandOnly; 869 bool shell::enableAsmJS = false; 870 bool shell::enableWasm = false; 871 bool shell::enableSharedMemory = SHARED_MEMORY_DEFAULT; 872 bool shell::enableWasmBaseline = false; 873 bool shell::enableWasmOptimizing = false; 874 bool shell::enableTestWasmAwaitTier2 = false; 875 bool shell::enableSourcePragmas = true; 876 bool shell::enableAsyncStacks = false; 877 bool shell::enableAsyncStackCaptureDebuggeeOnly = false; 878 bool shell::enableToSource = false; 879 #ifdef JS_GC_ZEAL 880 uint32_t shell::gZealBits = 0; 881 uint32_t shell::gZealFrequency = 0; 882 #endif 883 bool shell::printTiming = false; 884 RCFile* shell::gErrFile = nullptr; 885 RCFile* shell::gOutFile = nullptr; 886 bool shell::reportWarnings = true; 887 bool shell::compileOnly = false; 888 bool shell::disableOOMFunctions = false; 889 bool shell::defaultToSameCompartment = true; 890 891 #ifdef DEBUG 892 bool shell::dumpEntrainedVariables = false; 893 bool shell::OOM_printAllocationCount = false; 894 #endif 895 896 MOZ_RUNINIT UniqueChars shell::processWideModuleLoadPath; 897 898 static bool SetTimeoutValue(JSContext* cx, double t); 899 900 static void KillWatchdog(JSContext* cx); 901 902 static bool ScheduleWatchdog(JSContext* cx, double t); 903 904 static void CancelExecution(JSContext* cx); 905 906 enum class ShellGlobalKind { 907 GlobalObject, 908 WindowProxy, 909 }; 910 911 static void SetStandardRealmOptions(JS::RealmOptions& options); 912 static JSObject* NewGlobalObject(JSContext* cx, JS::RealmOptions& options, 913 JSPrincipals* principals, ShellGlobalKind kind, 914 bool immutablePrototype); 915 916 /* 917 * A toy WindowProxy class for the shell. This is intended for testing code 918 * where global |this| is a WindowProxy. All requests are forwarded to the 919 * underlying global and no navigation is supported. 920 */ 921 const JSClass ShellWindowProxyClass = 922 PROXY_CLASS_DEF("ShellWindowProxy", JSCLASS_HAS_RESERVED_SLOTS(1)); 923 924 JSObject* NewShellWindowProxy(JSContext* cx, JS::HandleObject global) { 925 MOZ_ASSERT(global->is<GlobalObject>()); 926 927 js::WrapperOptions options; 928 options.setClass(&ShellWindowProxyClass); 929 930 JSAutoRealm ar(cx, global); 931 JSObject* obj = 932 js::Wrapper::New(cx, global, &js::Wrapper::singleton, options); 933 MOZ_ASSERT_IF(obj, js::IsWindowProxy(obj)); 934 return obj; 935 } 936 937 /* 938 * A toy principals type for the shell. 939 * 940 * In the shell, a principal is simply a 32-bit mask: P subsumes Q if the 941 * set bits in P are a superset of those in Q. Thus, the principal 0 is 942 * subsumed by everything, and the principal ~0 subsumes everything. 943 * 944 * As a special case, a null pointer as a principal is treated like 0xffff. 945 * 946 * The 'newGlobal' function takes an option indicating which principal the 947 * new global should have; 'evaluate' does for the new code. 948 */ 949 class ShellPrincipals final : public JSPrincipals { 950 uint32_t bits; 951 952 static uint32_t getBits(JSPrincipals* p) { 953 if (!p) { 954 return 0xffff; 955 } 956 return static_cast<ShellPrincipals*>(p)->bits; 957 } 958 959 public: 960 explicit ShellPrincipals(uint32_t bits, int32_t refcount = 0) : bits(bits) { 961 this->refcount = refcount; 962 } 963 964 bool write(JSContext* cx, JSStructuredCloneWriter* writer) override { 965 // The shell doesn't have a read principals hook, so it doesn't really 966 // matter what we write here, but we have to write something so the 967 // fuzzer is happy. 968 return JS_WriteUint32Pair(writer, bits, 0); 969 } 970 971 bool isSystemOrAddonPrincipal() override { return true; } 972 973 static void destroy(JSPrincipals* principals) { 974 MOZ_ASSERT(principals != &fullyTrusted); 975 MOZ_ASSERT(principals->refcount == 0); 976 js_delete(static_cast<const ShellPrincipals*>(principals)); 977 } 978 979 static bool subsumes(JSPrincipals* first, JSPrincipals* second) { 980 uint32_t firstBits = getBits(first); 981 uint32_t secondBits = getBits(second); 982 return (firstBits | secondBits) == firstBits; 983 } 984 985 static JSSecurityCallbacks securityCallbacks; 986 987 // Fully-trusted principals singleton. 988 static ShellPrincipals fullyTrusted; 989 }; 990 991 // Global CSP state for the shell. When true, CSP restrictions are enforced. 992 static bool gCSPEnabled = false; 993 994 static bool ContentSecurityPolicyAllows( 995 JSContext* cx, JS::RuntimeCode kind, JS::Handle<JSString*> codeString, 996 JS::CompilationType compilationType, 997 JS::Handle<JS::StackGCVector<JSString*>> parameterStrings, 998 JS::Handle<JSString*> bodyString, 999 JS::Handle<JS::StackGCVector<JS::Value>> parameterArgs, 1000 JS::Handle<JS::Value> bodyArg, bool* outCanCompileStrings) { 1001 // If CSP is enabled, block string compilation. 1002 *outCanCompileStrings = !gCSPEnabled; 1003 return true; 1004 } 1005 1006 JSSecurityCallbacks ShellPrincipals::securityCallbacks = { 1007 ContentSecurityPolicyAllows, 1008 nullptr, // codeForEvalGets 1009 subsumes}; 1010 1011 // The fully-trusted principal subsumes all other principals. 1012 MOZ_RUNINIT ShellPrincipals ShellPrincipals::fullyTrusted(-1, 1); 1013 1014 #ifdef EDITLINE 1015 extern "C" { 1016 extern MOZ_EXPORT char* readline(const char* prompt); 1017 extern MOZ_EXPORT void add_history(char* line); 1018 } // extern "C" 1019 #endif 1020 1021 ShellContext::ShellContext(JSContext* cx, IsWorkerEnum isWorker_) 1022 : cx_(nullptr), 1023 isWorker(isWorker_), 1024 lastWarningEnabled(false), 1025 trackUnhandledRejections(true), 1026 timeoutInterval(-1.0), 1027 startTime(PRMJ_Now()), 1028 serviceInterrupt(false), 1029 haveInterruptFunc(false), 1030 interruptFunc(cx, NullValue()), 1031 lastWarning(cx, NullValue()), 1032 promiseRejectionTrackerCallback(cx, NullValue()), 1033 unhandledRejectedPromises(cx), 1034 watchdogLock(mutexid::ShellContextWatchdog), 1035 exitCode(0), 1036 quitting(false), 1037 readLineBufPos(0), 1038 errFilePtr(nullptr), 1039 outFilePtr(nullptr), 1040 offThreadMonitor(mutexid::ShellOffThreadState), 1041 taskCallbacks(cx) {} 1042 1043 ShellContext* js::shell::GetShellContext(JSContext* cx) { 1044 ShellContext* sc = static_cast<ShellContext*>(JS_GetContextPrivate(cx)); 1045 MOZ_ASSERT(sc); 1046 return sc; 1047 } 1048 1049 static void TraceRootArrays(JSTracer* trc, gc::MarkColor color) { 1050 JSRuntime* rt = trc->runtime(); 1051 for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { 1052 for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) { 1053 auto priv = static_cast<ShellCompartmentPrivate*>( 1054 JS_GetCompartmentPrivate(comp.get())); 1055 if (!priv) { 1056 continue; 1057 } 1058 1059 GCPtr<ArrayObject*>& array = 1060 (color == gc::MarkColor::Black) ? priv->blackRoot : priv->grayRoot; 1061 TraceNullableEdge(trc, &array, "shell root array"); 1062 1063 if (array) { 1064 // Trace the array elements as part of root marking. 1065 for (uint32_t i = 0; i < array->getDenseInitializedLength(); i++) { 1066 Value& value = const_cast<Value&>(array->getDenseElement(i)); 1067 TraceManuallyBarrieredEdge(trc, &value, "shell root array element"); 1068 } 1069 } 1070 } 1071 } 1072 } 1073 1074 static void TraceBlackRoots(JSTracer* trc, void* data) { 1075 TraceRootArrays(trc, gc::MarkColor::Black); 1076 } 1077 1078 static bool TraceGrayRoots(JSTracer* trc, JS::SliceBudget& budget, void* data) { 1079 TraceRootArrays(trc, gc::MarkColor::Gray); 1080 return true; 1081 } 1082 1083 static inline JSString* NewStringCopyUTF8(JSContext* cx, const char* chars) { 1084 return JS_NewStringCopyUTF8N(cx, JS::UTF8Chars(chars, strlen(chars))); 1085 } 1086 1087 static mozilla::UniqueFreePtr<char[]> GetLine(FILE* file, const char* prompt) { 1088 #ifdef EDITLINE 1089 /* 1090 * Use readline only if file is stdin, because there's no way to specify 1091 * another handle. Are other filehandles interactive? 1092 */ 1093 if (file == stdin) { 1094 mozilla::UniqueFreePtr<char[]> linep(readline(prompt)); 1095 /* 1096 * We set it to zero to avoid complaining about inappropriate ioctl 1097 * for device in the case of EOF. Looks like errno == 251 if line is 1098 * finished with EOF and errno == 25 (EINVAL on Mac) if there is 1099 * nothing left to read. 1100 */ 1101 if (errno == 251 || errno == 25 || errno == EINVAL) { 1102 errno = 0; 1103 } 1104 if (!linep) { 1105 return nullptr; 1106 } 1107 if (linep[0] != '\0') { 1108 add_history(linep.get()); 1109 } 1110 return linep; 1111 } 1112 #endif 1113 1114 size_t len = 0; 1115 if (*prompt != '\0' && gOutFile->isOpen()) { 1116 fprintf(gOutFile->fp, "%s", prompt); 1117 fflush(gOutFile->fp); 1118 } 1119 1120 size_t size = 80; 1121 mozilla::UniqueFreePtr<char[]> buffer(static_cast<char*>(malloc(size))); 1122 if (!buffer) { 1123 return nullptr; 1124 } 1125 1126 char* current = buffer.get(); 1127 do { 1128 while (true) { 1129 if (fgets(current, size - len, file)) { 1130 break; 1131 } 1132 if (errno != EINTR) { 1133 return nullptr; 1134 } 1135 } 1136 1137 len += strlen(current); 1138 char* t = buffer.get() + len - 1; 1139 if (*t == '\n') { 1140 /* Line was read. We remove '\n' and exit. */ 1141 *t = '\0'; 1142 break; 1143 } 1144 1145 if (len + 1 == size) { 1146 size = size * 2; 1147 char* raw = buffer.release(); 1148 char* tmp = static_cast<char*>(realloc(raw, size)); 1149 if (!tmp) { 1150 free(raw); 1151 return nullptr; 1152 } 1153 buffer.reset(tmp); 1154 } 1155 current = buffer.get() + len; 1156 } while (true); 1157 return buffer; 1158 } 1159 1160 static bool EvaluateInner(JSContext* cx, HandleString code, 1161 MutableHandleObject global, HandleObject opts, 1162 HandleObject cacheEntry, MutableHandleValue rval); 1163 1164 static bool ShellInterruptCallback(JSContext* cx) { 1165 ShellContext* sc = GetShellContext(cx); 1166 if (!sc->serviceInterrupt) { 1167 return true; 1168 } 1169 1170 // Reset serviceInterrupt. CancelExecution or InterruptIf will set it to 1171 // true to distinguish watchdog or user triggered interrupts. 1172 // Do this first to prevent other interrupts that may occur while the 1173 // user-supplied callback is executing from re-entering the handler. 1174 sc->serviceInterrupt = false; 1175 1176 bool result; 1177 if (sc->haveInterruptFunc) { 1178 RootedValue rval(cx); 1179 bool wasAlreadyThrowing = cx->isExceptionPending(); 1180 JS::AutoSaveExceptionState savedExc(cx); 1181 1182 if (sc->interruptFunc.isObject()) { 1183 JSAutoRealm ar(cx, &sc->interruptFunc.toObject()); 1184 1185 // Report any exceptions thrown by the JS interrupt callback, but do 1186 // *not* keep it on the cx. The interrupt handler is invoked at points 1187 // that are not expected to throw catchable exceptions, like at 1188 // JSOp::RetRval. 1189 // 1190 // If the interrupted JS code was already throwing, any exceptions 1191 // thrown by the interrupt handler are silently swallowed. 1192 Maybe<AutoReportException> are; 1193 if (!wasAlreadyThrowing) { 1194 are.emplace(cx); 1195 } 1196 result = JS_CallFunctionValue(cx, nullptr, sc->interruptFunc, 1197 JS::HandleValueArray::empty(), &rval); 1198 } else { 1199 RootedString str(cx, sc->interruptFunc.toString()); 1200 1201 Maybe<AutoReportException> are; 1202 if (!wasAlreadyThrowing) { 1203 are.emplace(cx); 1204 } 1205 1206 JS::RealmOptions options; 1207 SetStandardRealmOptions(options); 1208 RootedObject glob(cx, NewGlobalObject(cx, options, nullptr, 1209 ShellGlobalKind::WindowProxy, 1210 /* immutablePrototype = */ true)); 1211 if (!glob) { 1212 return false; 1213 } 1214 1215 RootedObject opts(cx, nullptr); 1216 RootedObject cacheEntry(cx, nullptr); 1217 JSAutoRealm ar(cx, glob); 1218 if (!EvaluateInner(cx, str, &glob, opts, cacheEntry, &rval)) { 1219 return false; 1220 } 1221 } 1222 savedExc.restore(); 1223 1224 if (rval.isBoolean()) { 1225 result = rval.toBoolean(); 1226 } else { 1227 result = false; 1228 } 1229 } else { 1230 result = false; 1231 } 1232 1233 if (!result && sc->exitCode == 0) { 1234 static const char msg[] = "Script terminated by interrupt handler.\n"; 1235 fputs(msg, stderr); 1236 1237 sc->exitCode = EXITCODE_TIMEOUT; 1238 } 1239 1240 return result; 1241 } 1242 1243 static void GCSliceCallback(JSContext* cx, JS::GCProgress progress, 1244 const JS::GCDescription& desc) { 1245 if (progress == JS::GC_CYCLE_END) { 1246 #if defined(MOZ_MEMORY) 1247 // We call this here to match the browser's DOMGCSliceCallback. 1248 jemalloc_free_dirty_pages(); 1249 #endif 1250 } 1251 } 1252 1253 /* 1254 * Some UTF-8 files, notably those written using Notepad, have a Unicode 1255 * Byte-Order-Mark (BOM) as their first character. This is useless (byte-order 1256 * is meaningless for UTF-8) but causes a syntax error unless we skip it. 1257 */ 1258 static void SkipUTF8BOM(FILE* file) { 1259 int ch1 = fgetc(file); 1260 int ch2 = fgetc(file); 1261 int ch3 = fgetc(file); 1262 1263 // Skip the BOM 1264 if (ch1 == 0xEF && ch2 == 0xBB && ch3 == 0xBF) { 1265 return; 1266 } 1267 1268 // No BOM - revert 1269 if (ch3 != EOF) { 1270 ungetc(ch3, file); 1271 } 1272 if (ch2 != EOF) { 1273 ungetc(ch2, file); 1274 } 1275 if (ch1 != EOF) { 1276 ungetc(ch1, file); 1277 } 1278 } 1279 1280 void EnvironmentPreparer::invoke(HandleObject global, Closure& closure) { 1281 MOZ_ASSERT(JS_IsGlobalObject(global)); 1282 1283 JSContext* cx = TlsContext.get(); 1284 MOZ_ASSERT(!JS_IsExceptionPending(cx)); 1285 1286 AutoRealm ar(cx, global); 1287 AutoReportException are(cx); 1288 if (!closure(cx)) { 1289 return; 1290 } 1291 } 1292 1293 static bool RegisterScriptPathWithModuleLoader(JSContext* cx, 1294 HandleScript script, 1295 const char* filename) { 1296 // Set the private value associated with a script to a object containing the 1297 // script's filename so that the module loader can use it to resolve 1298 // relative imports. 1299 1300 RootedString path(cx, NewStringCopyUTF8(cx, filename)); 1301 if (!path) { 1302 return false; 1303 } 1304 1305 MOZ_ASSERT(JS::GetScriptPrivate(script).isUndefined()); 1306 RootedObject infoObject(cx, js::CreateScriptPrivate(cx, path)); 1307 if (!infoObject) { 1308 return false; 1309 } 1310 1311 JS::SetScriptPrivate(script, ObjectValue(*infoObject)); 1312 return true; 1313 } 1314 1315 enum class CompileUtf8 { 1316 InflateToUtf16, 1317 DontInflate, 1318 }; 1319 1320 [[nodiscard]] static bool RunFile(JSContext* cx, const char* filename, 1321 FILE* file, CompileUtf8 compileMethod, 1322 bool compileOnly, bool fullParse) { 1323 SkipUTF8BOM(file); 1324 1325 int64_t t1 = PRMJ_Now(); 1326 RootedScript script(cx); 1327 1328 if (!filename) filename = "-"; 1329 1330 { 1331 CompileOptions options(cx); 1332 options.setIntroductionType("js shell file") 1333 .setFileAndLine(filename, 1) 1334 .setIsRunOnce(true) 1335 .setNoScriptRval(true); 1336 1337 if (fullParse) { 1338 options.setForceFullParse(); 1339 } else { 1340 options.setEagerDelazificationStrategy(defaultDelazificationMode); 1341 } 1342 1343 if (compileMethod == CompileUtf8::DontInflate) { 1344 script = JS::CompileUtf8File(cx, options, file); 1345 } else { 1346 fprintf(stderr, "(compiling '%s' after inflating to UTF-16)\n", filename); 1347 1348 FileContents buffer(cx); 1349 if (!ReadCompleteFile(cx, file, buffer)) { 1350 return false; 1351 } 1352 1353 size_t length = buffer.length(); 1354 auto chars = UniqueTwoByteChars( 1355 UTF8CharsToNewTwoByteCharsZ( 1356 cx, 1357 JS::UTF8Chars(reinterpret_cast<const char*>(buffer.begin()), 1358 buffer.length()), 1359 &length, js::MallocArena) 1360 .get()); 1361 if (!chars) { 1362 return false; 1363 } 1364 1365 JS::SourceText<char16_t> source; 1366 if (!source.init(cx, std::move(chars), length)) { 1367 return false; 1368 } 1369 1370 script = JS::Compile(cx, options, source); 1371 } 1372 1373 if (!script) { 1374 return false; 1375 } 1376 } 1377 1378 if (!RegisterScriptPathWithModuleLoader(cx, script, filename)) { 1379 return false; 1380 } 1381 1382 #ifdef DEBUG 1383 if (dumpEntrainedVariables) { 1384 AnalyzeEntrainedVariables(cx, script); 1385 } 1386 #endif 1387 if (!compileOnly) { 1388 if (!JS_ExecuteScript(cx, script)) { 1389 return false; 1390 } 1391 int64_t t2 = PRMJ_Now() - t1; 1392 if (printTiming) { 1393 printf("runtime = %.3f ms\n", double(t2) / PRMJ_USEC_PER_MSEC); 1394 } 1395 } 1396 return true; 1397 } 1398 1399 [[nodiscard]] static bool RunModule(JSContext* cx, const char* filename, 1400 bool compileOnly) { 1401 ShellContext* sc = GetShellContext(cx); 1402 1403 RootedString path(cx, NewStringCopyUTF8(cx, filename)); 1404 if (!path) { 1405 return false; 1406 } 1407 1408 path = ResolvePath(cx, path, RootRelative); 1409 if (!path) { 1410 return false; 1411 } 1412 1413 return sc->moduleLoader->loadRootModule(cx, path); 1414 } 1415 1416 static void ShellCleanupFinalizationRegistryCallback(JSFunction* doCleanup, 1417 JSObject* incumbentGlobal, 1418 void* data) { 1419 // In the browser this queues a task. Shell jobs correspond to microtasks so 1420 // we arrange for cleanup to happen after all jobs/microtasks have run. The 1421 // incumbent global is ignored in the shell. 1422 1423 auto sc = static_cast<ShellContext*>(data); 1424 AutoEnterOOMUnsafeRegion oomUnsafe; 1425 if (!sc->taskCallbacks.append(doCleanup)) { 1426 oomUnsafe.crash("ShellCleanupFinalizationRegistryCallback"); 1427 } 1428 } 1429 1430 // Run any tasks queued on the ShellContext and return whether any ran. 1431 static bool MaybeRunShellTasks(JSContext* cx) { 1432 ShellContext* sc = GetShellContext(cx); 1433 MOZ_ASSERT(!sc->quitting); 1434 1435 Rooted<ShellContext::ObjectVector> callbacks(cx); 1436 std::swap(callbacks.get(), sc->taskCallbacks.get()); 1437 1438 bool ranTasks = false; 1439 1440 RootedValue callback(cx); 1441 for (JSObject* o : callbacks) { 1442 callback = ObjectValue(*o); 1443 1444 JS::ExposeValueToActiveJS(callback); 1445 AutoRealm ar(cx, o); 1446 1447 { 1448 AutoReportException are(cx); 1449 RootedValue unused(cx); 1450 (void)JS_CallFunctionValue(cx, nullptr, callback, 1451 HandleValueArray::empty(), &unused); 1452 } 1453 1454 ranTasks = true; 1455 1456 if (sc->quitting) { 1457 break; 1458 } 1459 } 1460 1461 return ranTasks; 1462 } 1463 1464 static void RunShellJobs(JSContext* cx) { 1465 ShellContext* sc = GetShellContext(cx); 1466 if (sc->quitting) { 1467 return; 1468 } 1469 1470 while (true) { 1471 // Run microtasks. 1472 js::RunJobs(cx); 1473 if (sc->quitting) { 1474 return; 1475 } 1476 1477 // Run tasks. 1478 bool ranTasks = MaybeRunShellTasks(cx); 1479 if (!ranTasks) { 1480 break; 1481 } 1482 } 1483 } 1484 1485 static bool DrainJobQueue(JSContext* cx, unsigned argc, Value* vp) { 1486 CallArgs args = CallArgsFromVp(argc, vp); 1487 1488 if (GetShellContext(cx)->quitting) { 1489 JS_ReportErrorASCII( 1490 cx, "Mustn't drain the job queue when the shell is quitting"); 1491 return false; 1492 } 1493 1494 if (cx->isEvaluatingModule != 0) { 1495 JS_ReportErrorASCII( 1496 cx, 1497 "Can't drain the job queue when executing the top level of a module"); 1498 return false; 1499 } 1500 1501 RunShellJobs(cx); 1502 1503 if (GetShellContext(cx)->quitting) { 1504 return false; 1505 } 1506 1507 args.rval().setUndefined(); 1508 return true; 1509 } 1510 1511 static bool GlobalOfFirstJobInQueue(JSContext* cx, unsigned argc, Value* vp) { 1512 CallArgs args = CallArgsFromVp(argc, vp); 1513 1514 if (JS::Prefs::use_js_microtask_queue()) { 1515 if (cx->microTaskQueues->microTaskQueue.empty()) { 1516 JS_ReportErrorASCII(cx, "Job queue is empty"); 1517 return false; 1518 } 1519 1520 auto& genericJob = cx->microTaskQueues->microTaskQueue.front(); 1521 JS::JSMicroTask* job = JS::ToUnwrappedJSMicroTask(genericJob); 1522 if (!job) { 1523 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1524 JSMSG_DEAD_OBJECT); 1525 1526 return false; 1527 } 1528 1529 RootedObject global(cx, JS::GetExecutionGlobalFromJSMicroTask(job)); 1530 if (!global) { 1531 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1532 JSMSG_DEAD_OBJECT); 1533 return false; 1534 } 1535 MOZ_ASSERT(global); 1536 if (!cx->compartment()->wrap(cx, &global)) { 1537 return false; 1538 } 1539 1540 args.rval().setObject(*global); 1541 } else { 1542 RootedObject job(cx, cx->internalJobQueue->maybeFront()); 1543 if (!job) { 1544 JS_ReportErrorASCII(cx, "Job queue is empty"); 1545 return false; 1546 } 1547 1548 RootedObject global(cx, &job->nonCCWGlobal()); 1549 if (!cx->compartment()->wrap(cx, &global)) { 1550 return false; 1551 } 1552 1553 args.rval().setObject(*global); 1554 } 1555 1556 return true; 1557 } 1558 1559 static bool TrackUnhandledRejections(JSContext* cx, JS::HandleObject promise, 1560 JS::PromiseRejectionHandlingState state) { 1561 ShellContext* sc = GetShellContext(cx); 1562 if (!sc->trackUnhandledRejections) { 1563 return true; 1564 } 1565 1566 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT) 1567 if (cx->runningOOMTest) { 1568 // When OOM happens, we cannot reliably track the set of unhandled 1569 // promise rejections. Throw error only when simulated OOM is used 1570 // *and* promises are used in the test. 1571 JS_ReportErrorASCII( 1572 cx, 1573 "Can't track unhandled rejections while running simulated OOM " 1574 "test. Call ignoreUnhandledRejections before using oomTest etc."); 1575 return false; 1576 } 1577 #endif 1578 1579 if (!sc->unhandledRejectedPromises) { 1580 sc->unhandledRejectedPromises = SetObject::create(cx); 1581 if (!sc->unhandledRejectedPromises) { 1582 return false; 1583 } 1584 } 1585 1586 RootedValue promiseVal(cx, ObjectValue(*promise)); 1587 1588 AutoRealm ar(cx, sc->unhandledRejectedPromises); 1589 if (!cx->compartment()->wrap(cx, &promiseVal)) { 1590 return false; 1591 } 1592 1593 switch (state) { 1594 case JS::PromiseRejectionHandlingState::Unhandled: 1595 if (!sc->unhandledRejectedPromises->add(cx, promiseVal)) { 1596 return false; 1597 } 1598 break; 1599 case JS::PromiseRejectionHandlingState::Handled: 1600 bool deleted = false; 1601 if (!sc->unhandledRejectedPromises->delete_(cx, promiseVal, &deleted)) { 1602 return false; 1603 } 1604 // We can't MOZ_ASSERT(deleted) here, because it's possible we failed to 1605 // add the promise in the first place, due to OOM. 1606 break; 1607 } 1608 1609 return true; 1610 } 1611 1612 static void ForwardingPromiseRejectionTrackerCallback( 1613 JSContext* cx, bool mutedErrors, JS::HandleObject promise, 1614 JS::PromiseRejectionHandlingState state, void* data) { 1615 AutoReportException are(cx); 1616 1617 if (!TrackUnhandledRejections(cx, promise, state)) { 1618 return; 1619 } 1620 1621 RootedValue callback(cx, 1622 GetShellContext(cx)->promiseRejectionTrackerCallback); 1623 if (callback.isNull()) { 1624 return; 1625 } 1626 1627 AutoRealm ar(cx, &callback.toObject()); 1628 1629 FixedInvokeArgs<2> args(cx); 1630 args[0].setObject(*promise); 1631 args[1].setInt32(static_cast<int32_t>(state)); 1632 1633 if (!JS_WrapValue(cx, args[0])) { 1634 return; 1635 } 1636 1637 RootedValue rval(cx); 1638 (void)Call(cx, callback, UndefinedHandleValue, args, &rval); 1639 } 1640 1641 static bool SetPromiseRejectionTrackerCallback(JSContext* cx, unsigned argc, 1642 Value* vp) { 1643 CallArgs args = CallArgsFromVp(argc, vp); 1644 1645 if (!IsFunctionObject(args.get(0))) { 1646 JS_ReportErrorASCII( 1647 cx, 1648 "setPromiseRejectionTrackerCallback expects a function as its sole " 1649 "argument"); 1650 return false; 1651 } 1652 1653 GetShellContext(cx)->promiseRejectionTrackerCallback = args[0]; 1654 1655 args.rval().setUndefined(); 1656 return true; 1657 } 1658 1659 static bool SetTimeout(JSContext* cx, unsigned argc, Value* vp) { 1660 CallArgs args = CallArgsFromVp(argc, vp); 1661 if (args.length() != 1 && args.length() != 2) { 1662 JS_ReportErrorASCII(cx, "expected one or two arguments"); 1663 return false; 1664 } 1665 1666 JS::RootedValue functionRefValue(cx, args.get(0)); 1667 JS::RootedValue delayValue(cx, args.get(1)); 1668 1669 if (!functionRefValue.isObject() || 1670 !JS::IsCallable(&functionRefValue.toObject())) { 1671 JS_ReportErrorASCII(cx, "functionRef must be callable"); 1672 return false; 1673 } 1674 1675 if (IsCrossCompartmentWrapper(functionRefValue.toObjectOrNull())) { 1676 JS_ReportErrorASCII(cx, "functionRef cannot be a CCW"); 1677 return false; 1678 } 1679 1680 int32_t delay; 1681 if (!JS::ToInt32(cx, delayValue, &delay)) { 1682 return false; 1683 } 1684 if (delay != 0) { 1685 JS::WarnASCII(cx, "Treating non-zero delay as zero in setTimeout"); 1686 if (cx->isThrowingOutOfMemory()) { 1687 return false; 1688 } 1689 } 1690 1691 ShellContext* sc = GetShellContext(cx); 1692 if (sc->quitting) { 1693 JS_ReportErrorASCII(cx, "Cannot setTimeout while quitting"); 1694 return false; 1695 } 1696 1697 if (!sc->taskCallbacks.append(functionRefValue.toObjectOrNull())) { 1698 ReportOutOfMemory(cx); 1699 return false; 1700 } 1701 1702 args.rval().setUndefined(); 1703 return true; 1704 } 1705 1706 static bool SetCSPEnabled(JSContext* cx, unsigned argc, Value* vp) { 1707 CallArgs args = CallArgsFromVp(argc, vp); 1708 1709 if (args.length() != 1) { 1710 JS_ReportErrorASCII(cx, "setCSPEnabled() requires one boolean argument"); 1711 return false; 1712 } 1713 1714 gCSPEnabled = JS::ToBoolean(args[0]); 1715 1716 args.rval().setUndefined(); 1717 return true; 1718 } 1719 1720 static const char* telemetryNames[static_cast<int>(JSMetric::Count)] = { 1721 #define LIT(NAME, _) #NAME, 1722 FOR_EACH_JS_METRIC(LIT) 1723 #undef LIT 1724 }; 1725 1726 // Telemetry can be executed from multiple threads, and the callback is 1727 // responsible to avoid contention on the recorded telemetry data. 1728 class MOZ_RAII AutoLockTelemetry : public LockGuard<Mutex> { 1729 using Base = LockGuard<Mutex>; 1730 1731 Mutex& getMutex() { 1732 static Mutex mutex(mutexid::ShellTelemetry); 1733 return mutex; 1734 } 1735 1736 public: 1737 AutoLockTelemetry() : Base(getMutex()) {} 1738 }; 1739 1740 using TelemetrySamples = mozilla::Vector<uint32_t, 0, js::SystemAllocPolicy>; 1741 constinit static mozilla::Array<UniquePtr<TelemetrySamples>, 1742 size_t(JSMetric::Count)> 1743 recordedTelemetrySamples; 1744 1745 static void AccumulateTelemetryDataCallback(JSMetric id, uint32_t sample) { 1746 AutoLockTelemetry alt; 1747 TelemetrySamples* samples = recordedTelemetrySamples[size_t(id)].get(); 1748 if (!samples) { 1749 return; 1750 } 1751 1752 AutoEnterOOMUnsafeRegion oomUnsafe; 1753 if (!samples->append(sample)) { 1754 oomUnsafe.crash("AccumulateTelemetrySamplesCallback"); 1755 } 1756 } 1757 1758 static bool LookupTelemetryName(JSContext* cx, Handle<Value> nameValue, 1759 JSMetric* resultOut) { 1760 static_assert(size_t(JSMetric::Count) == std::size(telemetryNames)); 1761 MOZ_ASSERT(resultOut); 1762 1763 JSString* str = ToString(cx, nameValue); 1764 if (!str) { 1765 return false; 1766 } 1767 1768 UniqueChars name = EncodeLatin1(cx, str); 1769 if (!name) { 1770 return false; 1771 } 1772 1773 for (size_t i = 0; i < std::size(telemetryNames); i++) { 1774 if (strcmp(telemetryNames[i], name.get()) == 0) { 1775 *resultOut = JSMetric(i); 1776 return true; 1777 } 1778 } 1779 1780 return false; 1781 } 1782 1783 static bool StartRecordingTelemetry(JSContext* cx, unsigned argc, Value* vp) { 1784 CallArgs args = CallArgsFromVp(argc, vp); 1785 if (args.length() != 1) { 1786 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, 1787 JSSMSG_INVALID_ARGS, "startRecordingTelemetry"); 1788 return false; 1789 } 1790 1791 JSMetric id; 1792 if (!LookupTelemetryName(cx, args[0], &id)) { 1793 JS_ReportErrorASCII(cx, "Telemetry probe name not found"); 1794 return false; 1795 } 1796 1797 if (recordedTelemetrySamples[size_t(id)]) { 1798 JS_ReportErrorASCII(cx, "Already recording telemetry for this probe"); 1799 return false; 1800 } 1801 1802 auto samples = MakeUnique<TelemetrySamples>(); 1803 if (!samples) { 1804 ReportOutOfMemory(cx); 1805 return false; 1806 } 1807 1808 recordedTelemetrySamples[size_t(id)] = std::move(samples); 1809 args.rval().setUndefined(); 1810 return true; 1811 } 1812 1813 static bool StopRecordingTelemetry(JSContext* cx, unsigned argc, Value* vp) { 1814 CallArgs args = CallArgsFromVp(argc, vp); 1815 if (args.length() != 1) { 1816 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, 1817 JSSMSG_INVALID_ARGS, "stopRecordingTelemetry"); 1818 return false; 1819 } 1820 1821 JSMetric id; 1822 if (!LookupTelemetryName(cx, args[0], &id)) { 1823 JS_ReportErrorASCII(cx, "Telemetry probe name not found"); 1824 return false; 1825 } 1826 1827 if (!recordedTelemetrySamples[size_t(id)]) { 1828 JS_ReportErrorASCII(cx, "Not recording telemetry for this probe"); 1829 return false; 1830 } 1831 1832 recordedTelemetrySamples[size_t(id)].reset(); 1833 args.rval().setUndefined(); 1834 return true; 1835 } 1836 1837 static bool GetTelemetrySamples(JSContext* cx, unsigned argc, Value* vp) { 1838 CallArgs args = CallArgsFromVp(argc, vp); 1839 if (args.length() != 1) { 1840 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, 1841 JSSMSG_INVALID_ARGS, "getTelemetrySamples"); 1842 return false; 1843 } 1844 1845 JSMetric id; 1846 if (!LookupTelemetryName(cx, args[0], &id)) { 1847 JS_ReportErrorASCII(cx, "Telemetry probe name not found"); 1848 return false; 1849 } 1850 1851 TelemetrySamples* samples = recordedTelemetrySamples[size_t(id)].get(); 1852 if (!samples) { 1853 args.rval().setUndefined(); 1854 return true; 1855 } 1856 1857 Rooted<ArrayObject*> array(cx, NewDenseEmptyArray(cx)); 1858 if (!array) { 1859 return false; 1860 } 1861 1862 for (uint32_t sample : *samples) { 1863 if (!NewbornArrayPush(cx, array, NumberValue(sample))) { 1864 return false; 1865 } 1866 } 1867 1868 args.rval().setObject(*array); 1869 return true; 1870 } 1871 1872 // Use Counter introspection 1873 MOZ_RUNINIT static Mutex useCounterLock(mutexid::ShellUseCounters); 1874 class MOZ_RAII AutoLockUseCounters : public LockGuard<Mutex> { 1875 using Base = LockGuard<Mutex>; 1876 1877 public: 1878 AutoLockUseCounters() : Base(useCounterLock) {} 1879 }; 1880 1881 using UseCounterArray = 1882 mozilla::Array<uint32_t, static_cast<size_t>(JSUseCounter::COUNT)>; 1883 static UseCounterArray useCounterResults; 1884 static void SetUseCounterCallback(JSObject* obj, JSUseCounter counter) { 1885 MOZ_RELEASE_ASSERT(obj); 1886 AutoLockUseCounters aluc; 1887 // Maybe should ensure obj is a global object? 1888 useCounterResults[static_cast<size_t>(counter)]++; 1889 } 1890 1891 static bool GetUseCounterResults(JSContext* cx, unsigned argc, Value* vp) { 1892 CallArgs args = CallArgsFromVp(argc, vp); 1893 Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx)); 1894 if (!obj) { 1895 return false; 1896 } 1897 1898 // Make a private copy holding the lock then release, because we can't 1899 // hold this mutex while doing JS_DefineProperty, which holds MemoryTracker 1900 // mutex. 1901 UseCounterArray local; 1902 { 1903 AutoLockUseCounters aluc; 1904 local = useCounterResults; 1905 } 1906 1907 RootedValue val(cx); 1908 #define ADD_VALUE(ENUM, NAME) \ 1909 val.setInt32(local[static_cast<size_t>(JSUseCounter::ENUM)]); \ 1910 if (!JS_DefineProperty(cx, obj, #NAME, val, JSPROP_ENUMERATE)) { \ 1911 return false; \ 1912 } 1913 1914 FOR_EACH_JS_USE_COUNTER(ADD_VALUE); 1915 1916 args.rval().setObject(*obj); 1917 return true; 1918 } 1919 1920 static bool BoundToAsyncStack(JSContext* cx, unsigned argc, Value* vp) { 1921 CallArgs args = CallArgsFromVp(argc, vp); 1922 1923 RootedValue function(cx, GetFunctionNativeReserved(&args.callee(), 0)); 1924 RootedObject options( 1925 cx, &GetFunctionNativeReserved(&args.callee(), 1).toObject()); 1926 1927 Rooted<SavedFrame*> stack(cx, nullptr); 1928 bool isExplicit; 1929 1930 RootedValue v(cx); 1931 1932 if (!JS_GetProperty(cx, options, "stack", &v)) { 1933 return false; 1934 } 1935 if (!v.isObject() || !v.toObject().is<SavedFrame>()) { 1936 JS_ReportErrorASCII(cx, 1937 "The 'stack' property must be a SavedFrame object."); 1938 return false; 1939 } 1940 stack = &v.toObject().as<SavedFrame>(); 1941 1942 if (!JS_GetProperty(cx, options, "cause", &v)) { 1943 return false; 1944 } 1945 RootedString causeString(cx, ToString(cx, v)); 1946 if (!causeString) { 1947 return false; 1948 } 1949 1950 UniqueChars cause = JS_EncodeStringToUTF8(cx, causeString); 1951 if (!cause) { 1952 MOZ_ASSERT(cx->isExceptionPending()); 1953 return false; 1954 } 1955 1956 if (!JS_GetProperty(cx, options, "explicit", &v)) { 1957 return false; 1958 } 1959 isExplicit = v.isUndefined() ? true : ToBoolean(v); 1960 1961 auto kind = 1962 (isExplicit ? JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT 1963 : JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::IMPLICIT); 1964 1965 JS::AutoSetAsyncStackForNewCalls asasfnckthxbye(cx, stack, cause.get(), kind); 1966 return Call(cx, UndefinedHandleValue, function, JS::HandleValueArray::empty(), 1967 args.rval()); 1968 } 1969 1970 static bool BindToAsyncStack(JSContext* cx, unsigned argc, Value* vp) { 1971 CallArgs args = CallArgsFromVp(argc, vp); 1972 1973 if (args.length() != 2) { 1974 JS_ReportErrorASCII(cx, "bindToAsyncStack takes exactly two arguments."); 1975 return false; 1976 } 1977 1978 if (!args[0].isObject() || !IsCallable(args[0])) { 1979 JS_ReportErrorASCII( 1980 cx, "bindToAsyncStack's first argument should be a function."); 1981 return false; 1982 } 1983 1984 if (!args[1].isObject()) { 1985 JS_ReportErrorASCII( 1986 cx, "bindToAsyncStack's second argument should be an object."); 1987 return false; 1988 } 1989 1990 RootedFunction bound(cx, NewFunctionWithReserved(cx, BoundToAsyncStack, 0, 0, 1991 "bindToAsyncStack thunk")); 1992 if (!bound) { 1993 return false; 1994 } 1995 SetFunctionNativeReserved(bound, 0, args[0]); 1996 SetFunctionNativeReserved(bound, 1, args[1]); 1997 1998 args.rval().setObject(*bound); 1999 return true; 2000 } 2001 2002 #ifdef JS_HAS_INTL_API 2003 static bool AddIntlExtras(JSContext* cx, unsigned argc, Value* vp) { 2004 CallArgs args = CallArgsFromVp(argc, vp); 2005 if (!args.get(0).isObject()) { 2006 JS_ReportErrorASCII(cx, "addIntlExtras must be passed an object"); 2007 return false; 2008 } 2009 JS::RootedObject intl(cx, &args[0].toObject()); 2010 2011 static const JSFunctionSpec funcs[] = { 2012 JS_SELF_HOSTED_FN("getCalendarInfo", "Intl_getCalendarInfo", 1, 0), 2013 JS_FS_END, 2014 }; 2015 2016 if (!JS_DefineFunctions(cx, intl, funcs)) { 2017 return false; 2018 } 2019 2020 if (!JS::AddMozDateTimeFormatConstructor(cx, intl)) { 2021 return false; 2022 } 2023 2024 if (!JS::AddMozDisplayNamesConstructor(cx, intl)) { 2025 return false; 2026 } 2027 2028 args.rval().setUndefined(); 2029 return true; 2030 } 2031 #endif // JS_HAS_INTL_API 2032 2033 [[nodiscard]] static bool EvalUtf8AndPrint(JSContext* cx, const char* bytes, 2034 size_t length, int lineno, 2035 bool compileOnly) { 2036 // Eval. 2037 JS::CompileOptions options(cx); 2038 options.setIntroductionType("js shell interactive") 2039 .setIsRunOnce(true) 2040 .setFileAndLine("typein", lineno) 2041 .setEagerDelazificationStrategy(defaultDelazificationMode); 2042 2043 JS::SourceText<Utf8Unit> srcBuf; 2044 if (!srcBuf.init(cx, bytes, length, JS::SourceOwnership::Borrowed)) { 2045 return false; 2046 } 2047 2048 RootedScript script(cx, JS::Compile(cx, options, srcBuf)); 2049 if (!script) { 2050 return false; 2051 } 2052 if (compileOnly) { 2053 return true; 2054 } 2055 RootedValue result(cx); 2056 if (!JS_ExecuteScript(cx, script, &result)) { 2057 return false; 2058 } 2059 2060 if (!result.isUndefined() && gOutFile->isOpen()) { 2061 // Print. 2062 RootedString str(cx, JS_ValueToSource(cx, result)); 2063 if (!str) { 2064 return false; 2065 } 2066 2067 UniqueChars utf8chars = JS_EncodeStringToUTF8(cx, str); 2068 if (!utf8chars) { 2069 return false; 2070 } 2071 fprintf(gOutFile->fp, "%s\n", utf8chars.get()); 2072 } 2073 return true; 2074 } 2075 2076 [[nodiscard]] static bool ReadEvalPrintLoop(JSContext* cx, FILE* in, 2077 bool compileOnly) { 2078 ShellContext* sc = GetShellContext(cx); 2079 int lineno = 1; 2080 bool hitEOF = false; 2081 2082 do { 2083 /* 2084 * Accumulate lines until we get a 'compilable unit' - one that either 2085 * generates an error (before running out of source) or that compiles 2086 * cleanly. This should be whenever we get a complete statement that 2087 * coincides with the end of a line. 2088 */ 2089 int startline = lineno; 2090 using CharBuffer = Vector<char, 32>; 2091 RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment()); 2092 CharBuffer buffer(cx); 2093 do { 2094 ScheduleWatchdog(cx, -1); 2095 sc->serviceInterrupt = false; 2096 errno = 0; 2097 2098 mozilla::UniqueFreePtr<char[]> line = 2099 GetLine(in, startline == lineno ? "js> " : ""); 2100 if (!line) { 2101 if (errno) { 2102 if (UniqueChars error = SystemErrorMessage(cx, errno)) { 2103 JS_ReportErrorUTF8(cx, "%s", error.get()); 2104 } 2105 return false; 2106 } 2107 hitEOF = true; 2108 break; 2109 } 2110 2111 if (!buffer.append(line.get(), strlen(line.get())) || 2112 !buffer.append('\n')) { 2113 return false; 2114 } 2115 2116 lineno++; 2117 if (!ScheduleWatchdog(cx, sc->timeoutInterval)) { 2118 hitEOF = true; 2119 break; 2120 } 2121 } while (!JS_Utf8BufferIsCompilableUnit(cx, cx->global(), buffer.begin(), 2122 buffer.length())); 2123 2124 if (hitEOF && buffer.empty()) { 2125 break; 2126 } 2127 2128 { 2129 // Report exceptions but keep going. 2130 AutoReportException are(cx); 2131 (void)EvalUtf8AndPrint(cx, buffer.begin(), buffer.length(), startline, 2132 compileOnly); 2133 } 2134 2135 // If a let or const fail to initialize they will remain in an unusable 2136 // without further intervention. This call cleans up the global scope, 2137 // setting uninitialized lexicals to undefined so that they may still 2138 // be used. This behavior is _only_ acceptable in the context of the repl. 2139 if (JS::ForceLexicalInitialization(cx, globalLexical) && 2140 gErrFile->isOpen()) { 2141 fputs( 2142 "Warning: According to the standard, after the above exception,\n" 2143 "Warning: the global bindings should be permanently uninitialized.\n" 2144 "Warning: We have non-standard-ly initialized them to `undefined`" 2145 "for you.\nWarning: This nicety only happens in the JS shell.\n", 2146 stderr); 2147 } 2148 2149 RunShellJobs(cx); 2150 } while (!hitEOF && !sc->quitting); 2151 2152 if (gOutFile->isOpen()) { 2153 fprintf(gOutFile->fp, "\n"); 2154 } 2155 2156 return true; 2157 } 2158 2159 enum FileKind { 2160 PreludeScript, // UTF-8 script, fully-parsed, to avoid conflicting 2161 // configurations. 2162 FileScript, // UTF-8, directly parsed as such 2163 FileScriptUtf16, // FileScript, but inflate to UTF-16 before parsing 2164 FileModule, 2165 }; 2166 2167 [[nodiscard]] static bool Process(JSContext* cx, const char* filename, 2168 bool forceTTY, FileKind kind) { 2169 FILE* file; 2170 if (forceTTY || !filename || strcmp(filename, "-") == 0) { 2171 file = stdin; 2172 } else { 2173 file = OpenFile(cx, filename, "rb"); 2174 if (!file) { 2175 return false; 2176 } 2177 } 2178 AutoCloseFile autoClose(file); 2179 2180 bool fullParse = false; 2181 if (!forceTTY && !isatty(fileno(file))) { 2182 // It's not interactive - just execute it. 2183 switch (kind) { 2184 case PreludeScript: 2185 fullParse = true; 2186 if (!RunFile(cx, filename, file, CompileUtf8::DontInflate, compileOnly, 2187 fullParse)) { 2188 return false; 2189 } 2190 break; 2191 case FileScript: 2192 if (!RunFile(cx, filename, file, CompileUtf8::DontInflate, compileOnly, 2193 fullParse)) { 2194 return false; 2195 } 2196 break; 2197 case FileScriptUtf16: 2198 if (!RunFile(cx, filename, file, CompileUtf8::InflateToUtf16, 2199 compileOnly, fullParse)) { 2200 return false; 2201 } 2202 break; 2203 case FileModule: 2204 if (!RunModule(cx, filename, compileOnly)) { 2205 return false; 2206 } 2207 break; 2208 default: 2209 MOZ_CRASH("Impossible FileKind!"); 2210 } 2211 } else { 2212 // It's an interactive filehandle; drop into read-eval-print loop. 2213 MOZ_ASSERT(kind == FileScript); 2214 if (!ReadEvalPrintLoop(cx, file, compileOnly)) { 2215 return false; 2216 } 2217 } 2218 #ifdef FUZZING_JS_FUZZILLI 2219 fprintf(stderr, "executionHash is 0x%x with %d inputs\n", cx->executionHash, 2220 cx->executionHashInputs); 2221 #endif 2222 return true; 2223 } 2224 2225 #ifdef XP_WIN 2226 # define GET_FD_FROM_FILE(a) int(_get_osfhandle(fileno(a))) 2227 #else 2228 # define GET_FD_FROM_FILE(a) fileno(a) 2229 #endif 2230 2231 static void freeExternalCallback(void* contents, void* userData) { 2232 MOZ_ASSERT(!userData); 2233 js_free(contents); 2234 } 2235 2236 static bool CreateExternalArrayBuffer(JSContext* cx, unsigned argc, Value* vp) { 2237 CallArgs args = CallArgsFromVp(argc, vp); 2238 if (args.length() != 1) { 2239 JS_ReportErrorNumberASCII( 2240 cx, my_GetErrorMessage, nullptr, 2241 args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS, 2242 "createExternalArrayBuffer"); 2243 return false; 2244 } 2245 2246 int32_t bytes = 0; 2247 if (!ToInt32(cx, args[0], &bytes)) { 2248 return false; 2249 } 2250 2251 if (bytes < 0) { 2252 JS_ReportErrorASCII(cx, "Size must be non-negative"); 2253 return false; 2254 } 2255 2256 void* buffer = js_calloc(bytes); 2257 if (!buffer) { 2258 JS_ReportOutOfMemory(cx); 2259 return false; 2260 } 2261 2262 UniquePtr<void, JS::BufferContentsDeleter> ptr{buffer, 2263 {&freeExternalCallback}}; 2264 RootedObject arrayBuffer( 2265 cx, JS::NewExternalArrayBuffer(cx, bytes, std::move(ptr))); 2266 if (!arrayBuffer) { 2267 return false; 2268 } 2269 2270 args.rval().setObject(*arrayBuffer); 2271 return true; 2272 } 2273 2274 static bool CreateMappedArrayBuffer(JSContext* cx, unsigned argc, Value* vp) { 2275 CallArgs args = CallArgsFromVp(argc, vp); 2276 2277 if (args.length() < 1 || args.length() > 3) { 2278 JS_ReportErrorNumberASCII( 2279 cx, my_GetErrorMessage, nullptr, 2280 args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS, 2281 "createMappedArrayBuffer"); 2282 return false; 2283 } 2284 2285 RootedString rawFilenameStr(cx, JS::ToString(cx, args[0])); 2286 if (!rawFilenameStr) { 2287 return false; 2288 } 2289 // It's a little bizarre to resolve relative to the script, but for testing 2290 // I need a file at a known location, and the only good way I know of to do 2291 // that right now is to include it in the repo alongside the test script. 2292 // Bug 944164 would introduce an alternative. 2293 Rooted<JSString*> filenameStr( 2294 cx, ResolvePath(cx, rawFilenameStr, ScriptRelative)); 2295 if (!filenameStr) { 2296 return false; 2297 } 2298 UniqueChars filename = JS_EncodeStringToUTF8(cx, filenameStr); 2299 if (!filename) { 2300 return false; 2301 } 2302 2303 uint32_t offset = 0; 2304 if (args.length() >= 2) { 2305 if (!JS::ToUint32(cx, args[1], &offset)) { 2306 return false; 2307 } 2308 } 2309 2310 bool sizeGiven = false; 2311 uint32_t size; 2312 if (args.length() >= 3) { 2313 if (!JS::ToUint32(cx, args[2], &size)) { 2314 return false; 2315 } 2316 sizeGiven = true; 2317 if (size == 0) { 2318 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 2319 JSMSG_BAD_ARRAY_LENGTH); 2320 return false; 2321 } 2322 } 2323 2324 FILE* file = OpenFile(cx, filename.get(), "rb"); 2325 if (!file) { 2326 return false; 2327 } 2328 AutoCloseFile autoClose(file); 2329 2330 struct stat st; 2331 if (fstat(fileno(file), &st) < 0) { 2332 JS_ReportErrorASCII(cx, "Unable to stat file"); 2333 return false; 2334 } 2335 2336 if ((st.st_mode & S_IFMT) != S_IFREG) { 2337 JS_ReportErrorASCII(cx, "Path is not a regular file"); 2338 return false; 2339 } 2340 2341 if (!sizeGiven) { 2342 if (off_t(offset) >= st.st_size) { 2343 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 2344 JSMSG_OFFSET_LARGER_THAN_FILESIZE); 2345 return false; 2346 } 2347 size = st.st_size - offset; 2348 } 2349 2350 void* contents = 2351 JS::CreateMappedArrayBufferContents(GET_FD_FROM_FILE(file), offset, size); 2352 if (!contents) { 2353 JS_ReportErrorASCII(cx, 2354 "failed to allocate mapped array buffer contents " 2355 "(possibly due to bad alignment)"); 2356 return false; 2357 } 2358 2359 RootedObject obj(cx, 2360 JS::NewMappedArrayBufferWithContents(cx, size, contents)); 2361 if (!obj) { 2362 return false; 2363 } 2364 2365 args.rval().setObject(*obj); 2366 return true; 2367 } 2368 2369 #undef GET_FD_FROM_FILE 2370 2371 class UserBufferObject : public NativeObject { 2372 static const uint32_t BUFFER_SLOT = 0; 2373 static const uint32_t BYTE_LENGTH_SLOT = 1; 2374 static const uint32_t RESERVED_SLOTS = 2; 2375 2376 static constexpr auto BufferMemoryUse = MemoryUse::Embedding1; 2377 2378 static void finalize(JS::GCContext* gcx, JSObject* obj); 2379 2380 public: 2381 static const JSClassOps classOps_; 2382 static const JSClass class_; 2383 2384 [[nodiscard]] static UserBufferObject* create(JSContext* cx, 2385 size_t byteLength); 2386 2387 void* buffer() const { 2388 auto& buffer = getReservedSlot(BUFFER_SLOT); 2389 if (buffer.isUndefined()) { 2390 return nullptr; 2391 } 2392 return buffer.toPrivate(); 2393 } 2394 2395 size_t byteLength() const { 2396 return size_t(getReservedSlot(BYTE_LENGTH_SLOT).toPrivate()); 2397 } 2398 }; 2399 2400 const JSClassOps UserBufferObject::classOps_ = { 2401 nullptr, // addProperty 2402 nullptr, // delProperty 2403 nullptr, // enumerate 2404 nullptr, // newEnumerate 2405 nullptr, // resolve 2406 nullptr, // mayResolve 2407 UserBufferObject::finalize, // finalize 2408 nullptr, // call 2409 nullptr, // construct 2410 nullptr, // trace 2411 }; 2412 2413 const JSClass UserBufferObject::class_ = { 2414 "UserBufferObject", 2415 JSCLASS_HAS_RESERVED_SLOTS(UserBufferObject::RESERVED_SLOTS) | 2416 JSCLASS_BACKGROUND_FINALIZE, 2417 &UserBufferObject::classOps_, 2418 }; 2419 2420 UserBufferObject* UserBufferObject::create(JSContext* cx, size_t byteLength) { 2421 void* buffer = js_calloc(byteLength); 2422 if (!buffer) { 2423 JS_ReportOutOfMemory(cx); 2424 return nullptr; 2425 } 2426 UniquePtr<void, JS::FreePolicy> ptr(buffer); 2427 2428 auto* userBuffer = NewObjectWithGivenProto<UserBufferObject>(cx, nullptr); 2429 if (!userBuffer) { 2430 return nullptr; 2431 } 2432 2433 InitReservedSlot(userBuffer, BUFFER_SLOT, ptr.release(), byteLength, 2434 BufferMemoryUse); 2435 userBuffer->initReservedSlot(BYTE_LENGTH_SLOT, PrivateValue(byteLength)); 2436 2437 return userBuffer; 2438 } 2439 2440 void UserBufferObject::finalize(JS::GCContext* gcx, JSObject* obj) { 2441 auto* userBuffer = &obj->as<UserBufferObject>(); 2442 if (auto* buffer = userBuffer->buffer()) { 2443 gcx->free_(userBuffer, buffer, userBuffer->byteLength(), BufferMemoryUse); 2444 } 2445 } 2446 2447 static bool CreateUserArrayBuffer(JSContext* cx, unsigned argc, Value* vp) { 2448 CallArgs args = CallArgsFromVp(argc, vp); 2449 if (args.length() != 1) { 2450 JS_ReportErrorNumberASCII( 2451 cx, my_GetErrorMessage, nullptr, 2452 args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS, 2453 "createUserArrayBuffer"); 2454 return false; 2455 } 2456 2457 int32_t bytes = 0; 2458 if (!ToInt32(cx, args[0], &bytes)) { 2459 return false; 2460 } 2461 if (bytes < 0) { 2462 JS_ReportErrorASCII(cx, "Size must be non-negative"); 2463 return false; 2464 } 2465 2466 Rooted<UserBufferObject*> userBuffer(cx, UserBufferObject::create(cx, bytes)); 2467 if (!userBuffer) { 2468 return false; 2469 } 2470 2471 Rooted<JSObject*> arrayBuffer( 2472 cx, JS::NewArrayBufferWithUserOwnedContents(cx, userBuffer->byteLength(), 2473 userBuffer->buffer())); 2474 if (!arrayBuffer) { 2475 return false; 2476 } 2477 2478 // Create a strong reference from |arrayBuffer| to |userBuffer|. This ensures 2479 // |userBuffer| can't outlive |arrayBuffer|. That way we don't have to worry 2480 // about detaching the ArrayBuffer object when |userBuffer| gets finalized. 2481 // The reference is made through a private name, because we don't want to 2482 // expose |userBuffer| to user-code. 2483 2484 auto* privateName = NewPrivateName(cx, cx->names().empty_.toHandle()); 2485 if (!privateName) { 2486 return false; 2487 } 2488 2489 Rooted<PropertyKey> id(cx, PropertyKey::Symbol(privateName)); 2490 Rooted<JS::Value> userBufferVal(cx, ObjectValue(*userBuffer)); 2491 if (!js::DefineDataProperty(cx, arrayBuffer, id, userBufferVal, 0)) { 2492 return false; 2493 } 2494 2495 args.rval().setObject(*arrayBuffer); 2496 return true; 2497 } 2498 2499 static bool AddPromiseReactions(JSContext* cx, unsigned argc, Value* vp) { 2500 CallArgs args = CallArgsFromVp(argc, vp); 2501 2502 if (args.length() != 3) { 2503 JS_ReportErrorNumberASCII( 2504 cx, my_GetErrorMessage, nullptr, 2505 args.length() < 3 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS, 2506 "addPromiseReactions"); 2507 return false; 2508 } 2509 2510 RootedObject promise(cx); 2511 if (args[0].isObject()) { 2512 promise = &args[0].toObject(); 2513 } 2514 2515 if (!promise || !JS::IsPromiseObject(promise)) { 2516 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, 2517 JSSMSG_INVALID_ARGS, "addPromiseReactions"); 2518 return false; 2519 } 2520 2521 RootedObject onResolve(cx); 2522 if (args[1].isObject()) { 2523 onResolve = &args[1].toObject(); 2524 } 2525 2526 RootedObject onReject(cx); 2527 if (args[2].isObject()) { 2528 onReject = &args[2].toObject(); 2529 } 2530 2531 if (!onResolve || !onResolve->is<JSFunction>() || !onReject || 2532 !onReject->is<JSFunction>()) { 2533 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, 2534 JSSMSG_INVALID_ARGS, "addPromiseReactions"); 2535 return false; 2536 } 2537 2538 return JS::AddPromiseReactions(cx, promise, onResolve, onReject); 2539 } 2540 2541 static bool IgnoreUnhandledRejections(JSContext* cx, unsigned argc, Value* vp) { 2542 CallArgs args = CallArgsFromVp(argc, vp); 2543 2544 ShellContext* sc = GetShellContext(cx); 2545 sc->trackUnhandledRejections = false; 2546 2547 args.rval().setUndefined(); 2548 return true; 2549 } 2550 2551 static bool Options(JSContext* cx, unsigned argc, Value* vp) { 2552 CallArgs args = CallArgsFromVp(argc, vp); 2553 2554 JS::ContextOptions oldContextOptions = JS::ContextOptionsRef(cx); 2555 for (unsigned i = 0; i < args.length(); i++) { 2556 RootedString str(cx, JS::ToString(cx, args[i])); 2557 if (!str) { 2558 return false; 2559 } 2560 2561 Rooted<JSLinearString*> opt(cx, str->ensureLinear(cx)); 2562 if (!opt) { 2563 return false; 2564 } 2565 2566 if (StringEqualsLiteral(opt, "throw_on_asmjs_validation_failure")) { 2567 JS::ContextOptionsRef(cx).toggleThrowOnAsmJSValidationFailure(); 2568 } else { 2569 UniqueChars optChars = QuoteString(cx, opt, '"'); 2570 if (!optChars) { 2571 return false; 2572 } 2573 2574 JS_ReportErrorASCII(cx, 2575 "unknown option name %s." 2576 " The valid name is " 2577 "throw_on_asmjs_validation_failure.", 2578 optChars.get()); 2579 return false; 2580 } 2581 } 2582 2583 UniqueChars names = DuplicateString(""); 2584 bool found = false; 2585 if (names && oldContextOptions.throwOnAsmJSValidationFailure()) { 2586 names = JS_sprintf_append(std::move(names), "%s%s", found ? "," : "", 2587 "throw_on_asmjs_validation_failure"); 2588 found = true; 2589 } 2590 if (!names) { 2591 JS_ReportOutOfMemory(cx); 2592 return false; 2593 } 2594 2595 JSString* str = JS_NewStringCopyZ(cx, names.get()); 2596 if (!str) { 2597 return false; 2598 } 2599 args.rval().setString(str); 2600 return true; 2601 } 2602 2603 static bool LoadScript(JSContext* cx, unsigned argc, Value* vp, 2604 bool scriptRelative) { 2605 CallArgs args = CallArgsFromVp(argc, vp); 2606 2607 RootedString str(cx); 2608 for (unsigned i = 0; i < args.length(); i++) { 2609 str = JS::ToString(cx, args[i]); 2610 if (!str) { 2611 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, 2612 JSSMSG_INVALID_ARGS, "load"); 2613 return false; 2614 } 2615 2616 str = ResolvePath(cx, str, scriptRelative ? ScriptRelative : RootRelative); 2617 if (!str) { 2618 JS_ReportErrorASCII(cx, "unable to resolve path"); 2619 return false; 2620 } 2621 2622 UniqueChars filename = JS_EncodeStringToUTF8(cx, str); 2623 if (!filename) { 2624 return false; 2625 } 2626 2627 errno = 0; 2628 2629 CompileOptions opts(cx); 2630 opts.setIntroductionType("js shell load") 2631 .setIsRunOnce(true) 2632 .setNoScriptRval(true) 2633 .setEagerDelazificationStrategy(defaultDelazificationMode); 2634 2635 RootedValue unused(cx); 2636 if (!(compileOnly 2637 ? JS::CompileUtf8Path(cx, opts, filename.get()) != nullptr 2638 : JS::EvaluateUtf8Path(cx, opts, filename.get(), &unused))) { 2639 return false; 2640 } 2641 } 2642 2643 args.rval().setUndefined(); 2644 return true; 2645 } 2646 2647 static bool Load(JSContext* cx, unsigned argc, Value* vp) { 2648 return LoadScript(cx, argc, vp, false); 2649 } 2650 2651 static bool LoadScriptRelativeToScript(JSContext* cx, unsigned argc, 2652 Value* vp) { 2653 return LoadScript(cx, argc, vp, true); 2654 } 2655 2656 static void my_LargeAllocFailCallback() { 2657 JSContext* cx = TlsContext.get(); 2658 if (!cx) { 2659 return; 2660 } 2661 2662 MOZ_ASSERT(!JS::RuntimeHeapIsBusy()); 2663 2664 JS::PrepareForFullGC(cx); 2665 cx->runtime()->gc.gc(JS::GCOptions::Shrink, 2666 JS::GCReason::SHARED_MEMORY_LIMIT); 2667 } 2668 2669 static const uint32_t CacheEntry_SOURCE = 0; 2670 static const uint32_t CacheEntry_BYTECODE = 1; 2671 static const uint32_t CacheEntry_OPTIONS = 2; 2672 2673 // Some compile options can't be combined differently between save and load. 2674 // 2675 // CacheEntries store a CacheOption set, and on load an exception is thrown 2676 // if the entries are incompatible. 2677 2678 enum CacheOptions : uint32_t { 2679 IsRunOnce, 2680 NoScriptRval, 2681 Global, 2682 NonSyntactic, 2683 SourceIsLazy, 2684 ForceFullParse, 2685 }; 2686 2687 struct CacheOptionSet : public mozilla::EnumSet<CacheOptions> { 2688 using mozilla::EnumSet<CacheOptions>::EnumSet; 2689 2690 explicit CacheOptionSet(const CompileOptions& options) : EnumSet() { 2691 initFromOptions(options); 2692 } 2693 2694 void initFromOptions(const CompileOptions& options) { 2695 if (options.noScriptRval) { 2696 *this += CacheOptions::NoScriptRval; 2697 } 2698 if (options.isRunOnce) { 2699 *this += CacheOptions::IsRunOnce; 2700 } 2701 if (options.sourceIsLazy) { 2702 *this += CacheOptions::SourceIsLazy; 2703 } 2704 if (options.forceFullParse()) { 2705 *this += CacheOptions::ForceFullParse; 2706 } 2707 if (options.nonSyntacticScope) { 2708 *this += CacheOptions::NonSyntactic; 2709 } 2710 } 2711 }; 2712 2713 static bool CacheOptionsCompatible(const CacheOptionSet& a, 2714 const CacheOptionSet& b) { 2715 // If the options are identical, they are trivially compatible. 2716 return a == b; 2717 } 2718 2719 static const JSClass CacheEntry_class = { 2720 "CacheEntryObject", 2721 JSCLASS_HAS_RESERVED_SLOTS(3), 2722 }; 2723 2724 static bool CacheEntry(JSContext* cx, unsigned argc, JS::Value* vp) { 2725 CallArgs args = CallArgsFromVp(argc, vp); 2726 2727 if (args.length() != 1 || !args[0].isString()) { 2728 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, 2729 JSSMSG_INVALID_ARGS, "CacheEntry"); 2730 return false; 2731 } 2732 2733 RootedObject obj(cx, JS_NewObject(cx, &CacheEntry_class)); 2734 if (!obj) { 2735 return false; 2736 } 2737 2738 JS::SetReservedSlot(obj, CacheEntry_SOURCE, args[0]); 2739 JS::SetReservedSlot(obj, CacheEntry_BYTECODE, UndefinedValue()); 2740 2741 // Fill in empty option set. 2742 CacheOptionSet defaultOptions; 2743 JS::SetReservedSlot(obj, CacheEntry_OPTIONS, 2744 Int32Value(defaultOptions.serialize())); 2745 2746 args.rval().setObject(*obj); 2747 return true; 2748 } 2749 2750 static bool CacheEntry_isCacheEntry(JSObject* cache) { 2751 return cache->hasClass(&CacheEntry_class); 2752 } 2753 2754 static JSString* CacheEntry_getSource(JSContext* cx, HandleObject cache) { 2755 MOZ_ASSERT(CacheEntry_isCacheEntry(cache)); 2756 Value v = JS::GetReservedSlot(cache, CacheEntry_SOURCE); 2757 if (!v.isString()) { 2758 JS_ReportErrorASCII( 2759 cx, "CacheEntry_getSource: Unexpected type of source reserved slot."); 2760 return nullptr; 2761 } 2762 2763 return v.toString(); 2764 } 2765 2766 static bool CacheEntry_compatible(JSContext* cx, HandleObject cache, 2767 const CacheOptionSet& currentOptionSet) { 2768 CacheOptionSet cacheEntryOptions; 2769 MOZ_ASSERT(CacheEntry_isCacheEntry(cache)); 2770 Value v = JS::GetReservedSlot(cache, CacheEntry_OPTIONS); 2771 cacheEntryOptions.deserialize(v.toInt32()); 2772 if (!CacheOptionsCompatible(cacheEntryOptions, currentOptionSet)) { 2773 JS_ReportErrorASCII(cx, 2774 "CacheEntry_compatible: Incompatible cache contents"); 2775 return false; 2776 } 2777 return true; 2778 } 2779 2780 static uint8_t* CacheEntry_getBytecode(JSContext* cx, HandleObject cache, 2781 size_t* length) { 2782 MOZ_ASSERT(CacheEntry_isCacheEntry(cache)); 2783 Value v = JS::GetReservedSlot(cache, CacheEntry_BYTECODE); 2784 if (!v.isObject() || !v.toObject().is<ArrayBufferObject>()) { 2785 JS_ReportErrorASCII( 2786 cx, 2787 "CacheEntry_getBytecode: Unexpected type of bytecode reserved slot."); 2788 return nullptr; 2789 } 2790 2791 ArrayBufferObject* arrayBuffer = &v.toObject().as<ArrayBufferObject>(); 2792 *length = arrayBuffer->byteLength(); 2793 return arrayBuffer->dataPointer(); 2794 } 2795 2796 static bool CacheEntry_setBytecode(JSContext* cx, HandleObject cache, 2797 const CacheOptionSet& cacheOptions, 2798 uint8_t* buffer, uint32_t length) { 2799 MOZ_ASSERT(CacheEntry_isCacheEntry(cache)); 2800 2801 using BufferContents = ArrayBufferObject::BufferContents; 2802 2803 BufferContents contents = BufferContents::createMallocedUnknownArena(buffer); 2804 Rooted<ArrayBufferObject*> arrayBuffer( 2805 cx, ArrayBufferObject::createForContents(cx, length, contents)); 2806 if (!arrayBuffer) { 2807 return false; 2808 } 2809 2810 JS::SetReservedSlot(cache, CacheEntry_BYTECODE, ObjectValue(*arrayBuffer)); 2811 JS::SetReservedSlot(cache, CacheEntry_OPTIONS, 2812 Int32Value(cacheOptions.serialize())); 2813 return true; 2814 } 2815 2816 static bool ConvertTranscodeResultToJSException(JSContext* cx, 2817 JS::TranscodeResult rv) { 2818 switch (rv) { 2819 case JS::TranscodeResult::Ok: 2820 return true; 2821 2822 default: 2823 [[fallthrough]]; 2824 case JS::TranscodeResult::Failure: 2825 MOZ_ASSERT(!cx->isExceptionPending()); 2826 JS_ReportErrorASCII(cx, "generic warning"); 2827 return false; 2828 case JS::TranscodeResult::Failure_BadBuildId: 2829 MOZ_ASSERT(!cx->isExceptionPending()); 2830 JS_ReportErrorASCII(cx, "the build-id does not match"); 2831 return false; 2832 case JS::TranscodeResult::Failure_AsmJSNotSupported: 2833 MOZ_ASSERT(!cx->isExceptionPending()); 2834 JS_ReportErrorASCII(cx, "Asm.js is not supported by XDR"); 2835 return false; 2836 case JS::TranscodeResult::Failure_BadDecode: 2837 MOZ_ASSERT(!cx->isExceptionPending()); 2838 JS_ReportErrorASCII(cx, "XDR data corruption"); 2839 return false; 2840 2841 case JS::TranscodeResult::Throw: 2842 MOZ_ASSERT(cx->isExceptionPending()); 2843 return false; 2844 } 2845 } 2846 2847 static void SetQuitting(JSContext* cx, int32_t code) { 2848 ShellContext* sc = GetShellContext(cx); 2849 js::StopDrainingJobQueue(cx); 2850 #ifdef DEBUG 2851 { 2852 AutoLockHelperThreadState helperLock; 2853 2854 OffThreadPromiseRuntimeState& state = 2855 cx->runtime()->offThreadPromiseState.ref(); 2856 state.setForceQuitting(); 2857 } 2858 #endif 2859 sc->exitCode = code; 2860 sc->quitting = true; 2861 } 2862 2863 static void UnsetQuitting(JSContext* cx) { 2864 ShellContext* sc = GetShellContext(cx); 2865 js::RestartDrainingJobQueue(cx); 2866 sc->exitCode = 0; 2867 sc->quitting = false; 2868 } 2869 2870 static bool Evaluate(JSContext* cx, unsigned argc, Value* vp) { 2871 CallArgs args = CallArgsFromVp(argc, vp); 2872 2873 if (args.length() < 1 || args.length() > 2) { 2874 JS_ReportErrorNumberASCII( 2875 cx, my_GetErrorMessage, nullptr, 2876 args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS, 2877 "evaluate"); 2878 return false; 2879 } 2880 2881 RootedString code(cx, nullptr); 2882 RootedObject cacheEntry(cx, nullptr); 2883 if (args[0].isString()) { 2884 code = args[0].toString(); 2885 } else if (args[0].isObject() && 2886 CacheEntry_isCacheEntry(&args[0].toObject())) { 2887 cacheEntry = &args[0].toObject(); 2888 code = CacheEntry_getSource(cx, cacheEntry); 2889 if (!code) { 2890 return false; 2891 } 2892 } 2893 2894 if (!code || (args.length() == 2 && args[1].isPrimitive())) { 2895 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, 2896 JSSMSG_INVALID_ARGS, "evaluate"); 2897 return false; 2898 } 2899 2900 RootedObject opts(cx); 2901 if (args.length() == 2) { 2902 if (!args[1].isObject()) { 2903 JS_ReportErrorASCII(cx, "evaluate: The 2nd argument must be an object"); 2904 return false; 2905 } 2906 2907 opts = &args[1].toObject(); 2908 } 2909 2910 RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); 2911 MOZ_ASSERT(global); 2912 2913 return EvaluateInner(cx, code, &global, opts, cacheEntry, args.rval()); 2914 } 2915 2916 static bool EvaluateInner(JSContext* cx, HandleString code, 2917 MutableHandleObject global, HandleObject opts, 2918 HandleObject cacheEntry, MutableHandleValue rval) { 2919 // Check "global" property before everything to use the given global's 2920 // option as the default value. 2921 Maybe<CompileOptions> maybeOptions; 2922 if (opts) { 2923 RootedValue v(cx); 2924 if (!JS_GetProperty(cx, opts, "global", &v)) { 2925 return false; 2926 } 2927 if (!v.isUndefined()) { 2928 if (v.isObject()) { 2929 global.set(js::CheckedUnwrapDynamic(&v.toObject(), cx, 2930 /* stopAtWindowProxy = */ false)); 2931 if (!global) { 2932 return false; 2933 } 2934 } 2935 if (!global || !(JS::GetClass(global)->flags & JSCLASS_IS_GLOBAL)) { 2936 JS_ReportErrorNumberASCII( 2937 cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE, 2938 "\"global\" passed to evaluate()", "not a global object"); 2939 return false; 2940 } 2941 2942 JSAutoRealm ar(cx, global); 2943 maybeOptions.emplace(cx); 2944 } 2945 } 2946 if (!maybeOptions) { 2947 // If "global" property is not given, use the current global's option as 2948 // the default value. 2949 maybeOptions.emplace(cx); 2950 } 2951 2952 CompileOptions& options = maybeOptions.ref(); 2953 UniqueChars fileNameBytes; 2954 RootedString displayURL(cx); 2955 RootedString sourceMapURL(cx); 2956 bool catchTermination = false; 2957 bool loadBytecode = false; 2958 bool saveBytecodeWithDelazifications = false; 2959 bool execute = true; 2960 bool assertEqBytecode = false; 2961 JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::No); 2962 RootedObject callerGlobal(cx, cx->global()); 2963 2964 options.setIntroductionType("js shell evaluate") 2965 .setFileAndLine("@evaluate", 1) 2966 .setDeferDebugMetadata(); 2967 2968 RootedValue privateValue(cx); 2969 RootedString elementAttributeName(cx); 2970 2971 if (opts) { 2972 if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) { 2973 return false; 2974 } 2975 if (!ParseDebugMetadata(cx, opts, &privateValue, &elementAttributeName)) { 2976 return false; 2977 } 2978 if (!ParseSourceOptions(cx, opts, &displayURL, &sourceMapURL)) { 2979 return false; 2980 } 2981 2982 RootedValue v(cx); 2983 if (!JS_GetProperty(cx, opts, "catchTermination", &v)) { 2984 return false; 2985 } 2986 if (!v.isUndefined()) { 2987 catchTermination = ToBoolean(v); 2988 } 2989 2990 if (!JS_GetProperty(cx, opts, "loadBytecode", &v)) { 2991 return false; 2992 } 2993 if (!v.isUndefined()) { 2994 loadBytecode = ToBoolean(v); 2995 } 2996 2997 if (!JS_GetProperty(cx, opts, "saveBytecodeWithDelazifications", &v)) { 2998 return false; 2999 } 3000 if (!v.isUndefined()) { 3001 saveBytecodeWithDelazifications = ToBoolean(v); 3002 } 3003 3004 if (!JS_GetProperty(cx, opts, "execute", &v)) { 3005 return false; 3006 } 3007 if (!v.isUndefined()) { 3008 execute = ToBoolean(v); 3009 } 3010 3011 if (!JS_GetProperty(cx, opts, "assertEqBytecode", &v)) { 3012 return false; 3013 } 3014 if (!v.isUndefined()) { 3015 assertEqBytecode = ToBoolean(v); 3016 } 3017 3018 if (!JS_GetProperty(cx, opts, "envChainObject", &v)) { 3019 return false; 3020 } 3021 if (!v.isUndefined()) { 3022 if (!v.isObject()) { 3023 JS_ReportErrorNumberASCII( 3024 cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE, 3025 "\"envChainObject\" passed to evaluate()", "not an object"); 3026 return false; 3027 } 3028 3029 RootedObject obj(cx, &v.toObject()); 3030 { 3031 // This may be a CCW, so try to unwrap before checking 3032 // if it is an unqualified variables object. We still append 3033 // the original object to the environment chain however. 3034 JSObject* unwrappedObj = js::UncheckedUnwrap(obj, cx); 3035 if (unwrappedObj->isUnqualifiedVarObj()) { 3036 JS_ReportErrorASCII( 3037 cx, 3038 "\"envChainObject\" passed to evaluate() should not be an " 3039 "unqualified variables object"); 3040 return false; 3041 } 3042 } 3043 3044 if (!envChain.append(obj)) { 3045 return false; 3046 } 3047 } 3048 3049 if (!JS_GetProperty(cx, opts, "supportUnscopables", &v)) { 3050 return false; 3051 } 3052 if (!v.isUndefined()) { 3053 envChain.setSupportUnscopables(JS::SupportUnscopables(ToBoolean(v))); 3054 } 3055 3056 // We cannot load or save the bytecode if we have no object where the 3057 // bytecode cache is stored. 3058 if (loadBytecode || saveBytecodeWithDelazifications) { 3059 if (!cacheEntry) { 3060 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, 3061 JSSMSG_INVALID_ARGS, "evaluate"); 3062 return false; 3063 } 3064 } 3065 } 3066 3067 if (envChain.length() != 0) { 3068 // Wrap the envChainObject list into target realm. 3069 JSAutoRealm ar(cx, global); 3070 for (size_t i = 0; i < envChain.length(); ++i) { 3071 if (!JS_WrapObject(cx, envChain.chain()[i])) { 3072 return false; 3073 } 3074 } 3075 3076 options.setNonSyntacticScope(true); 3077 } 3078 3079 // The `loadBuffer` we use below outlives the Stencil we generate so we can 3080 // use its contents directly in the Stencil. 3081 options.borrowBuffer = true; 3082 3083 // We need to track the options used to generate bytecode for a CacheEntry to 3084 // avoid mismatches. This is primarily a concern when fuzzing the jsshell. 3085 CacheOptionSet cacheOptions; 3086 cacheOptions.initFromOptions(options); 3087 3088 JS::TranscodeBuffer loadBuffer; 3089 JS::TranscodeBuffer saveBuffer; 3090 3091 if (loadBytecode) { 3092 size_t loadLength = 0; 3093 uint8_t* loadData = nullptr; 3094 3095 if (!CacheEntry_compatible(cx, cacheEntry, cacheOptions)) { 3096 return false; 3097 } 3098 3099 loadData = CacheEntry_getBytecode(cx, cacheEntry, &loadLength); 3100 if (!loadData) { 3101 return false; 3102 } 3103 if (!loadBuffer.append(loadData, loadLength)) { 3104 JS_ReportOutOfMemory(cx); 3105 return false; 3106 } 3107 } 3108 3109 { 3110 JSAutoRealm ar(cx, global); 3111 RefPtr<JS::Stencil> stencil; 3112 3113 if (loadBytecode) { 3114 JS::TranscodeRange range(loadBuffer.begin(), loadBuffer.length()); 3115 JS::DecodeOptions decodeOptions(options); 3116 3117 JS::TranscodeResult rv = 3118 JS::DecodeStencil(cx, decodeOptions, range, getter_AddRefs(stencil)); 3119 if (JS::IsTranscodeFailureResult(rv)) { 3120 JS_ReportErrorASCII(cx, "failed to decode cache"); 3121 return false; 3122 } 3123 3124 if (!ConvertTranscodeResultToJSException(cx, rv)) { 3125 return false; 3126 } 3127 } else { 3128 AutoStableStringChars linearChars(cx); 3129 if (!linearChars.initTwoByte(cx, code)) { 3130 return false; 3131 } 3132 3133 JS::SourceText<char16_t> srcBuf; 3134 if (!srcBuf.initMaybeBorrowed(cx, linearChars)) { 3135 return false; 3136 } 3137 3138 stencil = JS::CompileGlobalScriptToStencil(cx, options, srcBuf); 3139 if (!stencil) { 3140 return false; 3141 } 3142 } 3143 3144 if (!js::ValidateLazinessOfStencilAndGlobal(cx, stencil.get())) { 3145 return false; 3146 } 3147 3148 JS::InstantiateOptions instantiateOptions(options); 3149 RootedScript script( 3150 cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); 3151 if (!script) { 3152 return false; 3153 } 3154 3155 AutoReportFrontendContext fc(cx); 3156 if (!SetSourceOptions(cx, &fc, script->scriptSource(), displayURL, 3157 sourceMapURL)) { 3158 return false; 3159 } 3160 3161 if (!JS::UpdateDebugMetadata(cx, script, instantiateOptions, privateValue, 3162 elementAttributeName, nullptr, nullptr)) { 3163 return false; 3164 } 3165 3166 if (saveBytecodeWithDelazifications) { 3167 bool alreadyStarted; 3168 if (!JS::StartCollectingDelazifications(cx, script, stencil, 3169 alreadyStarted)) { 3170 return false; 3171 } 3172 MOZ_ASSERT(!alreadyStarted); 3173 } 3174 3175 if (execute) { 3176 if (!(envChain.empty() ? JS_ExecuteScript(cx, script, rval) 3177 : JS_ExecuteScript(cx, envChain, script, rval))) { 3178 if (catchTermination && !JS_IsExceptionPending(cx)) { 3179 ShellContext* sc = GetShellContext(cx); 3180 if (sc->quitting) { 3181 UnsetQuitting(cx); 3182 } 3183 3184 JSAutoRealm ar1(cx, callerGlobal); 3185 JSString* str = JS_NewStringCopyZ(cx, "terminated"); 3186 if (!str) { 3187 return false; 3188 } 3189 rval.setString(str); 3190 return true; 3191 } 3192 return false; 3193 } 3194 } 3195 3196 // Serialize the encoded bytecode, recorded before the execution, into a 3197 // buffer which can be deserialized linearly. 3198 if (saveBytecodeWithDelazifications) { 3199 RefPtr<JS::Stencil> stencil; 3200 if (!FinishCollectingDelazifications(cx, script, 3201 getter_AddRefs(stencil))) { 3202 return false; 3203 } 3204 JS::TranscodeResult result = JS::EncodeStencil(cx, stencil, saveBuffer); 3205 if (result != JS::TranscodeResult::Ok) { 3206 return false; 3207 } 3208 } 3209 } 3210 3211 if (saveBytecodeWithDelazifications) { 3212 // If we are both loading and saving, we assert that we are going to 3213 // replace the current bytecode by the same stream of bytes. 3214 if (loadBytecode && assertEqBytecode) { 3215 if (saveBuffer.length() != loadBuffer.length()) { 3216 char loadLengthStr[16]; 3217 SprintfLiteral(loadLengthStr, "%zu", loadBuffer.length()); 3218 char saveLengthStr[16]; 3219 SprintfLiteral(saveLengthStr, "%zu", saveBuffer.length()); 3220 3221 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, 3222 JSSMSG_CACHE_EQ_SIZE_FAILED, loadLengthStr, 3223 saveLengthStr); 3224 return false; 3225 } 3226 3227 if (!ArrayEqual(loadBuffer.begin(), saveBuffer.begin(), 3228 loadBuffer.length())) { 3229 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, 3230 JSSMSG_CACHE_EQ_CONTENT_FAILED); 3231 return false; 3232 } 3233 } 3234 3235 size_t saveLength = saveBuffer.length(); 3236 if (saveLength >= INT32_MAX) { 3237 JS_ReportErrorASCII(cx, "Cannot save large cache entry content"); 3238 return false; 3239 } 3240 uint8_t* saveData = saveBuffer.extractOrCopyRawBuffer(); 3241 if (!CacheEntry_setBytecode(cx, cacheEntry, cacheOptions, saveData, 3242 saveLength)) { 3243 js_free(saveData); 3244 return false; 3245 } 3246 } 3247 3248 return JS_WrapValue(cx, rval); 3249 } 3250 3251 JSString* js::shell::FileAsString(JSContext* cx, JS::HandleString pathnameStr) { 3252 UniqueChars pathname = JS_EncodeStringToUTF8(cx, pathnameStr); 3253 if (!pathname) { 3254 return nullptr; 3255 } 3256 3257 FILE* file = OpenFile(cx, pathname.get(), "rb"); 3258 if (!file) { 3259 return nullptr; 3260 } 3261 3262 AutoCloseFile autoClose(file); 3263 3264 struct stat st; 3265 if (fstat(fileno(file), &st) != 0) { 3266 JS_ReportErrorUTF8(cx, "can't stat %s", pathname.get()); 3267 return nullptr; 3268 } 3269 3270 if ((st.st_mode & S_IFMT) != S_IFREG) { 3271 JS_ReportErrorUTF8(cx, "can't read non-regular file %s", pathname.get()); 3272 return nullptr; 3273 } 3274 3275 size_t len; 3276 if (!FileSize(cx, pathname.get(), file, &len)) { 3277 return nullptr; 3278 } 3279 3280 UniqueChars buf(js_pod_malloc<char>(len + 1)); 3281 if (!buf) { 3282 JS_ReportErrorUTF8(cx, "out of memory reading %s", pathname.get()); 3283 return nullptr; 3284 } 3285 3286 if (!ReadFile(cx, pathname.get(), file, buf.get(), len)) { 3287 return nullptr; 3288 } 3289 3290 UniqueTwoByteChars ucbuf( 3291 JS::LossyUTF8CharsToNewTwoByteCharsZ(cx, JS::UTF8Chars(buf.get(), len), 3292 &len, js::MallocArena) 3293 .get()); 3294 if (!ucbuf) { 3295 JS_ReportErrorUTF8(cx, "Invalid UTF-8 in file '%s'", pathname.get()); 3296 return nullptr; 3297 } 3298 3299 return JS_NewUCStringCopyN(cx, ucbuf.get(), len); 3300 } 3301 3302 /* 3303 * Function to run scripts and return compilation + execution time. Semantics 3304 * are closely modelled after the equivalent function in WebKit, as this is used 3305 * to produce benchmark timings by SunSpider. 3306 */ 3307 static bool Run(JSContext* cx, unsigned argc, Value* vp) { 3308 CallArgs args = CallArgsFromVp(argc, vp); 3309 if (args.length() != 1) { 3310 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, 3311 JSSMSG_INVALID_ARGS, "run"); 3312 return false; 3313 } 3314 3315 RootedString str(cx, JS::ToString(cx, args[0])); 3316 if (!str) { 3317 return false; 3318 } 3319 args[0].setString(str); 3320 3321 str = FileAsString(cx, str); 3322 if (!str) { 3323 return false; 3324 } 3325 3326 AutoStableStringChars linearChars(cx); 3327 if (!linearChars.initTwoByte(cx, str)) { 3328 return false; 3329 } 3330 3331 JS::SourceText<char16_t> srcBuf; 3332 if (!srcBuf.initMaybeBorrowed(cx, linearChars)) { 3333 return false; 3334 } 3335 3336 RootedScript script(cx); 3337 int64_t startClock = PRMJ_Now(); 3338 { 3339 UniqueChars filename = JS_EncodeStringToUTF8(cx, str); 3340 if (!filename) { 3341 return false; 3342 } 3343 3344 JS::CompileOptions options(cx); 3345 options.setIntroductionType("js shell run") 3346 .setFileAndLine(filename.get(), 1) 3347 .setIsRunOnce(true) 3348 .setNoScriptRval(true) 3349 .setEagerDelazificationStrategy(defaultDelazificationMode); 3350 3351 script = JS::Compile(cx, options, srcBuf); 3352 if (!script) { 3353 return false; 3354 } 3355 } 3356 3357 if (!JS_ExecuteScript(cx, script)) { 3358 return false; 3359 } 3360 3361 int64_t endClock = PRMJ_Now(); 3362 3363 args.rval().setDouble((endClock - startClock) / double(PRMJ_USEC_PER_MSEC)); 3364 return true; 3365 } 3366 3367 static int js_fgets(char* buf, int size, FILE* file) { 3368 int n, i, c; 3369 bool crflag; 3370 3371 n = size - 1; 3372 if (n < 0) { 3373 return -1; 3374 } 3375 3376 // Use the fastest available getc. 3377 auto fast_getc = 3378 #if defined(HAVE_GETC_UNLOCKED) 3379 getc_unlocked 3380 #elif defined(HAVE__GETC_NOLOCK) 3381 _getc_nolock 3382 #else 3383 getc 3384 #endif 3385 ; 3386 3387 crflag = false; 3388 for (i = 0; i < n && (c = fast_getc(file)) != EOF; i++) { 3389 buf[i] = c; 3390 if (c == '\n') { // any \n ends a line 3391 i++; // keep the \n; we know there is room for \0 3392 break; 3393 } 3394 if (crflag) { // \r not followed by \n ends line at the \r 3395 ungetc(c, file); 3396 break; // and overwrite c in buf with \0 3397 } 3398 crflag = (c == '\r'); 3399 } 3400 3401 buf[i] = '\0'; 3402 return i; 3403 } 3404 3405 /* 3406 * function readline() 3407 * Provides a hook for scripts to read a line from stdin. 3408 */ 3409 static bool ReadLine(JSContext* cx, unsigned argc, Value* vp) { 3410 CallArgs args = CallArgsFromVp(argc, vp); 3411 3412 // Don't support readline() on worker threads because js_fgets is not 3413 // thread-safe. This also avoids non-deterministic behavior for fuzzers that 3414 // use readline() for communication. 3415 if (GetShellContext(cx)->isWorker) { 3416 JS_ReportErrorASCII(cx, "readline() is not supported on worker threads"); 3417 return false; 3418 } 3419 3420 static constexpr size_t BUFSIZE = 256; 3421 FILE* from = stdin; 3422 size_t buflength = 0; 3423 size_t bufsize = BUFSIZE; 3424 char* buf = (char*)JS_malloc(cx, bufsize); 3425 if (!buf) { 3426 JS_ReportOutOfMemory(cx); 3427 return false; 3428 } 3429 auto freeBuf = mozilla::MakeScopeExit([&]() { 3430 JS_free(cx, buf); 3431 buf = nullptr; 3432 }); 3433 3434 bool sawNewline = false; 3435 size_t gotlength; 3436 while ((gotlength = js_fgets(buf + buflength, bufsize - buflength, from)) > 3437 0) { 3438 buflength += gotlength; 3439 3440 /* Are we done? */ 3441 if (buf[buflength - 1] == '\n') { 3442 buf[buflength - 1] = '\0'; 3443 sawNewline = true; 3444 break; 3445 } else if (buflength < bufsize - 1) { 3446 break; 3447 } 3448 3449 /* Else, grow our buffer for another pass. */ 3450 bufsize *= 2; 3451 if (bufsize <= buflength) { 3452 JS_ReportAllocationOverflow(cx); 3453 return false; 3454 } 3455 char* tmp = static_cast<char*>(JS_realloc(cx, buf, bufsize / 2, bufsize)); 3456 if (!tmp) { 3457 JS_ReportOutOfMemory(cx); 3458 return false; 3459 } 3460 buf = tmp; 3461 } 3462 3463 /* Treat the empty string specially. */ 3464 if (buflength == 0) { 3465 args.rval().set(feof(from) ? NullValue() : JS_GetEmptyStringValue(cx)); 3466 return true; 3467 } 3468 3469 /* 3470 * Turn buf into a JSString. Note that buflength includes the trailing null 3471 * character. 3472 */ 3473 JSString* str = 3474 JS_NewStringCopyN(cx, buf, sawNewline ? buflength - 1 : buflength); 3475 if (!str) { 3476 return false; 3477 } 3478 3479 args.rval().setString(str); 3480 return true; 3481 } 3482 3483 /* 3484 * function readlineBuf() 3485 * Provides a hook for scripts to emulate readline() using a string object. 3486 */ 3487 static bool ReadLineBuf(JSContext* cx, unsigned argc, Value* vp) { 3488 CallArgs args = CallArgsFromVp(argc, vp); 3489 ShellContext* sc = GetShellContext(cx); 3490 3491 if (!args.length()) { 3492 if (!sc->readLineBuf) { 3493 JS_ReportErrorASCII(cx, 3494 "No source buffer set. You must initially " 3495 "call readlineBuf with an argument."); 3496 return false; 3497 } 3498 3499 char* currentBuf = sc->readLineBuf.get() + sc->readLineBufPos; 3500 size_t buflen = strlen(currentBuf); 3501 3502 if (!buflen) { 3503 args.rval().setNull(); 3504 return true; 3505 } 3506 3507 size_t len = 0; 3508 while (len < buflen) { 3509 if (currentBuf[len] == '\n') { 3510 break; 3511 } 3512 len++; 3513 } 3514 3515 JSString* str = JS_NewStringCopyUTF8N(cx, JS::UTF8Chars(currentBuf, len)); 3516 if (!str) { 3517 return false; 3518 } 3519 3520 if (currentBuf[len] == '\0') { 3521 sc->readLineBufPos += len; 3522 } else { 3523 sc->readLineBufPos += len + 1; 3524 } 3525 3526 args.rval().setString(str); 3527 return true; 3528 } 3529 3530 if (args.length() == 1) { 3531 sc->readLineBuf = nullptr; 3532 sc->readLineBufPos = 0; 3533 3534 RootedString str(cx, JS::ToString(cx, args[0])); 3535 if (!str) { 3536 return false; 3537 } 3538 sc->readLineBuf = JS_EncodeStringToUTF8(cx, str); 3539 if (!sc->readLineBuf) { 3540 return false; 3541 } 3542 3543 args.rval().setUndefined(); 3544 return true; 3545 } 3546 3547 JS_ReportErrorASCII(cx, "Must specify at most one argument"); 3548 return false; 3549 } 3550 3551 static bool PutStr(JSContext* cx, unsigned argc, Value* vp) { 3552 CallArgs args = CallArgsFromVp(argc, vp); 3553 3554 if (args.length() != 0) { 3555 if (!gOutFile->isOpen()) { 3556 JS_ReportErrorASCII(cx, "output file is closed"); 3557 return false; 3558 } 3559 3560 RootedString str(cx, JS::ToString(cx, args[0])); 3561 if (!str) { 3562 return false; 3563 } 3564 UniqueChars bytes = JS_EncodeStringToUTF8(cx, str); 3565 if (!bytes) { 3566 return false; 3567 } 3568 fputs(bytes.get(), gOutFile->fp); 3569 fflush(gOutFile->fp); 3570 } 3571 3572 args.rval().setUndefined(); 3573 return true; 3574 } 3575 3576 static bool Now(JSContext* cx, unsigned argc, Value* vp) { 3577 CallArgs args = CallArgsFromVp(argc, vp); 3578 double now = PRMJ_Now() / double(PRMJ_USEC_PER_MSEC); 3579 args.rval().setDouble(now); 3580 return true; 3581 } 3582 3583 static bool CpuNow(JSContext* cx, unsigned argc, Value* vp) { 3584 CallArgs args = CallArgsFromVp(argc, vp); 3585 double now = double(std::clock()) / double(CLOCKS_PER_SEC); 3586 args.rval().setDouble(now); 3587 return true; 3588 } 3589 3590 static bool PrintInternal(JSContext* cx, const CallArgs& args, RCFile* file) { 3591 if (!file->isOpen()) { 3592 JS_ReportErrorASCII(cx, "output file is closed"); 3593 return false; 3594 } 3595 3596 for (unsigned i = 0; i < args.length(); i++) { 3597 RootedString str(cx, JS::ToString(cx, args[i])); 3598 if (!str) { 3599 return false; 3600 } 3601 UniqueChars bytes = JS_EncodeStringToUTF8(cx, str); 3602 if (!bytes) { 3603 return false; 3604 } 3605 fprintf(file->fp, "%s%s", i ? " " : "", bytes.get()); 3606 } 3607 3608 fputc('\n', file->fp); 3609 fflush(file->fp); 3610 3611 args.rval().setUndefined(); 3612 return true; 3613 } 3614 3615 static bool Print(JSContext* cx, unsigned argc, Value* vp) { 3616 CallArgs args = CallArgsFromVp(argc, vp); 3617 #ifdef FUZZING_INTERFACES 3618 if (fuzzHaveModule && !fuzzDoDebug) { 3619 // When fuzzing and not debugging, suppress any print() output, 3620 // as it slows down fuzzing and makes libFuzzer's output hard 3621 // to read. 3622 args.rval().setUndefined(); 3623 return true; 3624 } 3625 #endif // FUZZING_INTERFACES 3626 return PrintInternal(cx, args, gOutFile); 3627 } 3628 3629 static bool PrintErr(JSContext* cx, unsigned argc, Value* vp) { 3630 CallArgs args = CallArgsFromVp(argc, vp); 3631 return PrintInternal(cx, args, gErrFile); 3632 } 3633 3634 static bool Help(JSContext* cx, unsigned argc, Value* vp); 3635 3636 static bool Quit(JSContext* cx, unsigned argc, Value* vp) { 3637 // Print a message to stderr in differential testing to help jsfunfuzz 3638 // find uncatchable-exception bugs. 3639 if (js::SupportDifferentialTesting()) { 3640 fprintf(stderr, "quit called\n"); 3641 } 3642 3643 CallArgs args = CallArgsFromVp(argc, vp); 3644 int32_t code; 3645 if (!ToInt32(cx, args.get(0), &code)) { 3646 return false; 3647 } 3648 3649 // The fuzzers check the shell's exit code and assume a value >= 128 means 3650 // the process crashed (for instance, SIGSEGV will result in code 139). On 3651 // POSIX platforms, the exit code is 8-bit and negative values can also 3652 // result in an exit code >= 128. We restrict the value to range [0, 127] to 3653 // avoid false positives. 3654 if (code < 0 || code >= 128) { 3655 JS_ReportErrorASCII(cx, "quit exit code should be in range 0-127"); 3656 return false; 3657 } 3658 3659 SetQuitting(cx, code); 3660 JS::ReportUncatchableException(cx); 3661 return false; 3662 } 3663 3664 static bool StartTimingMutator(JSContext* cx, unsigned argc, Value* vp) { 3665 CallArgs args = CallArgsFromVp(argc, vp); 3666 if (args.length() > 0) { 3667 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, 3668 JSSMSG_TOO_MANY_ARGS, "startTimingMutator"); 3669 return false; 3670 } 3671 3672 if (!cx->runtime()->gc.stats().startTimingMutator()) { 3673 JS_ReportErrorASCII( 3674 cx, "StartTimingMutator should only be called from outside of GC"); 3675 return false; 3676 } 3677 3678 args.rval().setUndefined(); 3679 return true; 3680 } 3681 3682 static bool StopTimingMutator(JSContext* cx, unsigned argc, Value* vp) { 3683 CallArgs args = CallArgsFromVp(argc, vp); 3684 if (args.length() > 0) { 3685 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, 3686 JSSMSG_TOO_MANY_ARGS, "stopTimingMutator"); 3687 return false; 3688 } 3689 3690 double mutator_ms, gc_ms; 3691 if (!cx->runtime()->gc.stats().stopTimingMutator(mutator_ms, gc_ms)) { 3692 JS_ReportErrorASCII(cx, 3693 "stopTimingMutator called when not timing the mutator"); 3694 return false; 3695 } 3696 double total_ms = mutator_ms + gc_ms; 3697 if (total_ms > 0 && gOutFile->isOpen()) { 3698 fprintf(gOutFile->fp, "Mutator: %.3fms (%.1f%%), GC: %.3fms (%.1f%%)\n", 3699 mutator_ms, mutator_ms / total_ms * 100.0, gc_ms, 3700 gc_ms / total_ms * 100.0); 3701 } 3702 3703 args.rval().setUndefined(); 3704 return true; 3705 } 3706 3707 static const char* ToSource(JSContext* cx, HandleValue vp, UniqueChars* bytes) { 3708 RootedString str(cx, JS_ValueToSource(cx, vp)); 3709 if (str) { 3710 *bytes = JS_EncodeStringToUTF8(cx, str); 3711 if (*bytes) { 3712 return bytes->get(); 3713 } 3714 } 3715 JS_ClearPendingException(cx); 3716 return "<<error converting value to string>>"; 3717 } 3718 3719 static bool AssertEq(JSContext* cx, unsigned argc, Value* vp) { 3720 CallArgs args = CallArgsFromVp(argc, vp); 3721 if (!(args.length() == 2 || (args.length() == 3 && args[2].isString()))) { 3722 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, 3723 (args.length() < 2) ? JSSMSG_NOT_ENOUGH_ARGS 3724 : (args.length() == 3) ? JSSMSG_INVALID_ARGS 3725 : JSSMSG_TOO_MANY_ARGS, 3726 "assertEq"); 3727 return false; 3728 } 3729 3730 bool same; 3731 if (!JS::SameValue(cx, args[0], args[1], &same)) { 3732 return false; 3733 } 3734 if (!same) { 3735 UniqueChars bytes0, bytes1; 3736 const char* actual = ToSource(cx, args[0], &bytes0); 3737 const char* expected = ToSource(cx, args[1], &bytes1); 3738 if (args.length() == 2) { 3739 JS_ReportErrorNumberUTF8(cx, my_GetErrorMessage, nullptr, 3740 JSSMSG_ASSERT_EQ_FAILED, actual, expected); 3741 } else { 3742 RootedString message(cx, args[2].toString()); 3743 UniqueChars bytes2 = QuoteString(cx, message); 3744 if (!bytes2) { 3745 return false; 3746 } 3747 JS_ReportErrorNumberUTF8(cx, my_GetErrorMessage, nullptr, 3748 JSSMSG_ASSERT_EQ_FAILED_MSG, actual, expected, 3749 bytes2.get()); 3750 } 3751 return false; 3752 } 3753 args.rval().setUndefined(); 3754 return true; 3755 } 3756 3757 static JSScript* GetTopScript(JSContext* cx) { 3758 NonBuiltinScriptFrameIter iter(cx); 3759 return iter.done() ? nullptr : iter.script(); 3760 } 3761 3762 static bool GetScriptAndPCArgs(JSContext* cx, CallArgs& args, 3763 MutableHandleScript scriptp, int32_t* ip) { 3764 RootedScript script(cx, GetTopScript(cx)); 3765 *ip = 0; 3766 if (!args.get(0).isUndefined()) { 3767 HandleValue v = args[0]; 3768 unsigned intarg = 0; 3769 if (v.isObject() && JS::GetClass(&v.toObject())->isJSFunction()) { 3770 script = TestingFunctionArgumentToScript(cx, v); 3771 if (!script) { 3772 return false; 3773 } 3774 intarg++; 3775 } 3776 if (!args.get(intarg).isUndefined()) { 3777 if (!JS::ToInt32(cx, args[intarg], ip)) { 3778 return false; 3779 } 3780 if ((uint32_t)*ip >= script->length()) { 3781 JS_ReportErrorASCII(cx, "Invalid PC"); 3782 return false; 3783 } 3784 } 3785 } 3786 3787 scriptp.set(script); 3788 3789 return true; 3790 } 3791 3792 static bool LineToPC(JSContext* cx, unsigned argc, Value* vp) { 3793 CallArgs args = CallArgsFromVp(argc, vp); 3794 3795 if (args.length() == 0) { 3796 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, 3797 JSSMSG_LINE2PC_USAGE); 3798 return false; 3799 } 3800 3801 RootedScript script(cx, GetTopScript(cx)); 3802 int32_t lineArg = 0; 3803 if (args[0].isObject() && args[0].toObject().is<JSFunction>()) { 3804 script = TestingFunctionArgumentToScript(cx, args[0]); 3805 if (!script) { 3806 return false; 3807 } 3808 lineArg++; 3809 } 3810 3811 uint32_t lineno; 3812 if (!ToUint32(cx, args.get(lineArg), &lineno)) { 3813 return false; 3814 } 3815 3816 jsbytecode* pc = LineNumberToPC(script, lineno); 3817 if (!pc) { 3818 return false; 3819 } 3820 args.rval().setInt32(script->pcToOffset(pc)); 3821 return true; 3822 } 3823 3824 static bool PCToLine(JSContext* cx, unsigned argc, Value* vp) { 3825 CallArgs args = CallArgsFromVp(argc, vp); 3826 RootedScript script(cx); 3827 int32_t i; 3828 unsigned lineno; 3829 3830 if (!GetScriptAndPCArgs(cx, args, &script, &i)) { 3831 return false; 3832 } 3833 lineno = PCToLineNumber(script, script->offsetToPC(i)); 3834 if (!lineno) { 3835 return false; 3836 } 3837 args.rval().setInt32(lineno); 3838 return true; 3839 } 3840 3841 #if defined(DEBUG) || defined(JS_JITSPEW) 3842 3843 static bool Notes(JSContext* cx, unsigned argc, Value* vp) { 3844 CallArgs args = CallArgsFromVp(argc, vp); 3845 JSSprinter sprinter(cx); 3846 if (!sprinter.init()) { 3847 return false; 3848 } 3849 3850 for (unsigned i = 0; i < args.length(); i++) { 3851 RootedScript script(cx, TestingFunctionArgumentToScript(cx, args[i])); 3852 if (!script) { 3853 return false; 3854 } 3855 3856 if (!JSScript::dumpSrcNotes(cx, script, &sprinter)) { 3857 return false; 3858 } 3859 } 3860 3861 JSString* str = sprinter.release(cx); 3862 if (!str) { 3863 return false; 3864 } 3865 args.rval().setString(str); 3866 return true; 3867 } 3868 3869 namespace { 3870 3871 struct DisassembleOptionParser { 3872 unsigned argc; 3873 Value* argv; 3874 JSScript::DumpOptions options; 3875 3876 DisassembleOptionParser(unsigned argc, Value* argv) 3877 : argc(argc), argv(argv) {} 3878 3879 bool parse(JSContext* cx) { 3880 options.recursive = false; 3881 3882 /* Read options off early arguments */ 3883 while (argc > 0 && argv[0].isString()) { 3884 JSString* str = argv[0].toString(); 3885 JSLinearString* linearStr = JS_EnsureLinearString(cx, str); 3886 if (!linearStr) { 3887 return false; 3888 } 3889 if (JS_LinearStringEqualsLiteral(linearStr, "-r")) { 3890 options.recursive = true; 3891 } else { 3892 break; 3893 } 3894 argv++; 3895 argc--; 3896 } 3897 return true; 3898 } 3899 }; 3900 3901 } /* anonymous namespace */ 3902 3903 static bool DisassembleToSprinter(JSContext* cx, unsigned argc, Value* vp, 3904 StringPrinter* sp) { 3905 CallArgs args = CallArgsFromVp(argc, vp); 3906 DisassembleOptionParser p(args.length(), args.array()); 3907 if (!p.parse(cx)) { 3908 return false; 3909 } 3910 3911 if (p.argc == 0) { 3912 /* Without arguments, disassemble the current script. */ 3913 RootedScript script(cx, GetTopScript(cx)); 3914 if (script) { 3915 JSAutoRealm ar(cx, script); 3916 if (!JSScript::dump(cx, script, p.options, sp)) { 3917 return false; 3918 } 3919 } 3920 } else { 3921 for (unsigned i = 0; i < p.argc; i++) { 3922 RootedFunction fun(cx); 3923 RootedScript script(cx); 3924 RootedValue value(cx, p.argv[i]); 3925 if (value.isObject() && value.toObject().is<ShellModuleObjectWrapper>()) { 3926 auto* module = value.toObject().as<ShellModuleObjectWrapper>().get(); 3927 script = module->maybeScript(); 3928 if (!script) { 3929 JS_ReportErrorASCII(cx, "module does not have an associated script"); 3930 return false; 3931 } 3932 } else { 3933 script = TestingFunctionArgumentToScript(cx, value, fun.address()); 3934 if (!script) { 3935 return false; 3936 } 3937 } 3938 3939 if (!JSScript::dump(cx, script, p.options, sp)) { 3940 return false; 3941 } 3942 } 3943 } 3944 3945 return true; 3946 } 3947 3948 static bool DisassembleToString(JSContext* cx, unsigned argc, Value* vp) { 3949 CallArgs args = CallArgsFromVp(argc, vp); 3950 JSSprinter sprinter(cx); 3951 if (!sprinter.init()) { 3952 return false; 3953 } 3954 if (!DisassembleToSprinter(cx, args.length(), vp, &sprinter)) { 3955 return false; 3956 } 3957 3958 JSString* str = sprinter.release(cx); 3959 if (!str) { 3960 return false; 3961 } 3962 args.rval().setString(str); 3963 return true; 3964 } 3965 3966 static bool Disassemble(JSContext* cx, unsigned argc, Value* vp) { 3967 CallArgs args = CallArgsFromVp(argc, vp); 3968 3969 if (!gOutFile->isOpen()) { 3970 JS_ReportErrorASCII(cx, "output file is closed"); 3971 return false; 3972 } 3973 3974 Sprinter sprinter(cx); 3975 if (!sprinter.init()) { 3976 return false; 3977 } 3978 if (!DisassembleToSprinter(cx, args.length(), vp, &sprinter)) { 3979 return false; 3980 } 3981 3982 JS::UniqueChars str = sprinter.release(); 3983 if (!str) { 3984 return false; 3985 } 3986 fprintf(gOutFile->fp, "%s\n", str.get()); 3987 args.rval().setUndefined(); 3988 return true; 3989 } 3990 3991 static bool DisassFile(JSContext* cx, unsigned argc, Value* vp) { 3992 CallArgs args = CallArgsFromVp(argc, vp); 3993 3994 if (!gOutFile->isOpen()) { 3995 JS_ReportErrorASCII(cx, "output file is closed"); 3996 return false; 3997 } 3998 3999 /* Support extra options at the start, just like Disassemble. */ 4000 DisassembleOptionParser p(args.length(), args.array()); 4001 if (!p.parse(cx)) { 4002 return false; 4003 } 4004 4005 if (!p.argc) { 4006 args.rval().setUndefined(); 4007 return true; 4008 } 4009 4010 // We should change DisassembleOptionParser to store CallArgs. 4011 Rooted<JSString*> str( 4012 cx, JS::ToString(cx, HandleValue::fromMarkedLocation(&p.argv[0]))); 4013 if (!str) { 4014 return false; 4015 } 4016 UniqueChars filename = JS_EncodeStringToUTF8(cx, str); 4017 if (!filename) { 4018 return false; 4019 } 4020 RootedScript script(cx); 4021 4022 { 4023 CompileOptions options(cx); 4024 options.setIntroductionType("js shell disFile") 4025 .setFileAndLine(filename.get(), 1) 4026 .setIsRunOnce(true) 4027 .setNoScriptRval(true) 4028 .setEagerDelazificationStrategy(defaultDelazificationMode); 4029 4030 script = JS::CompileUtf8Path(cx, options, filename.get()); 4031 if (!script) { 4032 return false; 4033 } 4034 } 4035 4036 Sprinter sprinter(cx); 4037 if (!sprinter.init()) { 4038 return false; 4039 } 4040 if (!JSScript::dump(cx, script, p.options, &sprinter)) { 4041 return false; 4042 } 4043 4044 JS::UniqueChars chars = sprinter.release(); 4045 if (!chars) { 4046 return false; 4047 } 4048 fprintf(gOutFile->fp, "%s\n", chars.get()); 4049 4050 args.rval().setUndefined(); 4051 return true; 4052 } 4053 4054 static bool DisassWithSrc(JSContext* cx, unsigned argc, Value* vp) { 4055 CallArgs args = CallArgsFromVp(argc, vp); 4056 4057 if (!gOutFile->isOpen()) { 4058 JS_ReportErrorASCII(cx, "output file is closed"); 4059 return false; 4060 } 4061 4062 const size_t lineBufLen = 512; 4063 unsigned len, line1, line2, bupline; 4064 char linebuf[lineBufLen]; 4065 static const char sep[] = ";-------------------------"; 4066 4067 RootedScript script(cx); 4068 for (unsigned i = 0; i < args.length(); i++) { 4069 script = TestingFunctionArgumentToScript(cx, args[i]); 4070 if (!script) { 4071 return false; 4072 } 4073 4074 if (!script->filename()) { 4075 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, 4076 JSSMSG_FILE_SCRIPTS_ONLY); 4077 return false; 4078 } 4079 4080 FILE* file = OpenFile(cx, script->filename(), "rb"); 4081 if (!file) { 4082 return false; 4083 } 4084 auto closeFile = MakeScopeExit([file] { fclose(file); }); 4085 4086 jsbytecode* pc = script->code(); 4087 jsbytecode* end = script->codeEnd(); 4088 4089 Sprinter sprinter(cx); 4090 if (!sprinter.init()) { 4091 return false; 4092 } 4093 4094 /* burn the leading lines */ 4095 line2 = PCToLineNumber(script, pc); 4096 for (line1 = 0; line1 < line2 - 1; line1++) { 4097 char* tmp = fgets(linebuf, lineBufLen, file); 4098 if (!tmp) { 4099 JS_ReportErrorUTF8(cx, "failed to read %s fully", script->filename()); 4100 return false; 4101 } 4102 } 4103 4104 bupline = 0; 4105 while (pc < end) { 4106 line2 = PCToLineNumber(script, pc); 4107 4108 if (line2 < line1) { 4109 if (bupline != line2) { 4110 bupline = line2; 4111 sprinter.printf("%s %3u: BACKUP\n", sep, line2); 4112 } 4113 } else { 4114 if (bupline && line1 == line2) { 4115 sprinter.printf("%s %3u: RESTORE\n", sep, line2); 4116 } 4117 bupline = 0; 4118 while (line1 < line2) { 4119 if (!fgets(linebuf, lineBufLen, file)) { 4120 JS_ReportErrorNumberUTF8(cx, my_GetErrorMessage, nullptr, 4121 JSSMSG_UNEXPECTED_EOF, script->filename()); 4122 return false; 4123 } 4124 line1++; 4125 sprinter.printf("%s %3u: %s", sep, line1, linebuf); 4126 } 4127 } 4128 4129 len = 4130 Disassemble1(cx, script, pc, script->pcToOffset(pc), true, &sprinter); 4131 if (!len) { 4132 return false; 4133 } 4134 4135 pc += len; 4136 } 4137 4138 JS::UniqueChars str = sprinter.release(); 4139 if (!str) { 4140 return false; 4141 } 4142 fprintf(gOutFile->fp, "%s\n", str.get()); 4143 } 4144 4145 args.rval().setUndefined(); 4146 return true; 4147 } 4148 4149 #endif /* defined(DEBUG) || defined(JS_JITSPEW) */ 4150 4151 #ifdef JS_CACHEIR_SPEW 4152 static bool CacheIRHealthReport(JSContext* cx, unsigned argc, Value* vp) { 4153 CallArgs args = CallArgsFromVp(argc, vp); 4154 4155 js::jit::CacheIRHealth cih; 4156 RootedScript script(cx); 4157 4158 // In the case that we are calling this function from the shell and 4159 // the environment variable is not set, AutoSpewChannel automatically 4160 // sets and unsets the proper channel for the duration of spewing 4161 // a health report. 4162 AutoSpewChannel channel(cx, SpewChannel::CacheIRHealthReport, script); 4163 if (!argc) { 4164 // Calling CacheIRHealthReport without any arguments will create health 4165 // reports for all scripts in the zone. 4166 if (jit::JitZone* jitZone = cx->zone()->jitZone()) { 4167 jitZone->forEachJitScript([&](jit::JitScript* jitScript) { 4168 script = jitScript->owningScript(); 4169 if (!script->selfHosted()) { 4170 cih.healthReportForScript(cx, script, js::jit::SpewContext::Shell); 4171 } 4172 }); 4173 } 4174 } else { 4175 RootedValue value(cx, args.get(0)); 4176 4177 if (value.isObject() && value.toObject().is<ShellModuleObjectWrapper>()) { 4178 script = 4179 value.toObject().as<ShellModuleObjectWrapper>().get()->maybeScript(); 4180 if (!script) { 4181 JS_ReportErrorASCII(cx, "module does not have an associated script"); 4182 return false; 4183 } 4184 } else { 4185 script = TestingFunctionArgumentToScript(cx, args.get(0)); 4186 if (!script) { 4187 return false; 4188 } 4189 } 4190 4191 cih.healthReportForScript(cx, script, js::jit::SpewContext::Shell); 4192 } 4193 4194 args.rval().setUndefined(); 4195 return true; 4196 } 4197 #endif /* JS_CACHEIR_SPEW */ 4198 4199 /* Pretend we can always preserve wrappers for dummy DOM objects. */ 4200 static bool DummyPreserveWrapperCallback(JSContext* cx, HandleObject obj) { 4201 return true; 4202 } 4203 4204 static bool DummyHasReleasedWrapperCallback(HandleObject obj) { return true; } 4205 4206 #ifdef FUZZING_JS_FUZZILLI 4207 static bool fuzzilli_hash(JSContext* cx, unsigned argc, Value* vp) { 4208 CallArgs args = CallArgsFromVp(argc, vp); 4209 args.rval().setUndefined(); 4210 4211 if (argc != 1) { 4212 return true; 4213 } 4214 uint32_t hash; 4215 JS::Handle<JS::Value> v = args.get(0); 4216 if (v.isInt32()) { 4217 int32_t i = v.toInt32(); 4218 hash = FuzzilliHashDouble((double)i); 4219 } else if (v.isDouble()) { 4220 double d = v.toDouble(); 4221 d = JS::CanonicalizeNaN(d); 4222 hash = FuzzilliHashDouble(d); 4223 } else if (v.isNull()) { 4224 hash = FuzzilliHashDouble(1.0); 4225 } else if (v.isUndefined()) { 4226 hash = FuzzilliHashDouble(2.0); 4227 } else if (v.isBoolean()) { 4228 hash = FuzzilliHashDouble(3.0 + v.toBoolean()); 4229 } else if (v.isBigInt()) { 4230 JS::BigInt* bigInt = v.toBigInt(); 4231 hash = FuzzilliHashBigInt(bigInt); 4232 } else if (v.isObject()) { 4233 JSObject& obj = v.toObject(); 4234 FuzzilliHashObject(cx, &obj); 4235 return true; 4236 } else { 4237 hash = 0; 4238 } 4239 4240 cx->executionHashInputs += 1; 4241 cx->executionHash = mozilla::RotateLeft(cx->executionHash + hash, 1); 4242 return true; 4243 } 4244 4245 // We have to assume that the fuzzer will be able to call this function e.g. by 4246 // enumerating the properties of the global object and eval'ing them. As such 4247 // this function is implemented in a way that requires passing some magic value 4248 // as first argument (with the idea being that the fuzzer won't be able to 4249 // generate this value) which then also acts as a selector for the operation 4250 // to perform. 4251 static bool Fuzzilli(JSContext* cx, unsigned argc, Value* vp) { 4252 CallArgs args = CallArgsFromVp(argc, vp); 4253 4254 RootedString arg(cx, JS::ToString(cx, args.get(0))); 4255 if (!arg) { 4256 return false; 4257 } 4258 Rooted<JSLinearString*> operation(cx, StringToLinearString(cx, arg)); 4259 if (!operation) { 4260 return false; 4261 } 4262 4263 if (StringEqualsLiteral(operation, "FUZZILLI_CRASH")) { 4264 int type; 4265 if (!ToInt32(cx, args.get(1), &type)) { 4266 return false; 4267 } 4268 4269 // With this, we can test the various ways the JS shell can crash and make 4270 // sure that Fuzzilli is able to detect all of these failures properly. 4271 switch (type) { 4272 case 0: 4273 *((int*)0x41414141) = 0x1337; 4274 break; 4275 case 1: 4276 MOZ_RELEASE_ASSERT(false); 4277 break; 4278 case 2: 4279 MOZ_ASSERT(false); 4280 break; 4281 case 3: 4282 # if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) 4283 __asm__("int3"); 4284 # elif defined(JS_CODEGEN_ARM64) 4285 __asm__("brk #0"); 4286 # endif 4287 break; 4288 default: 4289 exit(1); 4290 } 4291 } else if (StringEqualsLiteral(operation, "FUZZILLI_PRINT")) { 4292 static FILE* fzliout = fdopen(REPRL_DWFD, "w"); 4293 if (!fzliout) { 4294 fprintf( 4295 stderr, 4296 "Fuzzer output channel not available, printing to stdout instead\n"); 4297 fzliout = stdout; 4298 } 4299 4300 RootedString str(cx, JS::ToString(cx, args.get(1))); 4301 if (!str) { 4302 return false; 4303 } 4304 UniqueChars bytes = JS_EncodeStringToUTF8(cx, str); 4305 if (!bytes) { 4306 return false; 4307 } 4308 fprintf(fzliout, "%s\n", bytes.get()); 4309 fflush(fzliout); 4310 } else if (StringEqualsLiteral(operation, "FUZZILLI_RANDOM")) { 4311 // This is an entropy source which can be called during fuzzing. 4312 // Its currently used to tests whether Fuzzilli detects non-deterministic 4313 // behavior. 4314 args.rval().setInt32(static_cast<uint32_t>(mozilla::RandomUint64OrDie())); 4315 return true; 4316 } 4317 4318 args.rval().setUndefined(); 4319 return true; 4320 } 4321 4322 static bool FuzzilliReprlGetAndRun(JSContext* cx) { 4323 size_t scriptSize = 0; 4324 4325 unsigned action; 4326 MOZ_RELEASE_ASSERT(read(REPRL_CRFD, &action, 4) == 4); 4327 if (action == 'cexe') { 4328 MOZ_RELEASE_ASSERT(read(REPRL_CRFD, &scriptSize, 8) == 8); 4329 } else { 4330 fprintf(stderr, "Unknown action: %u\n", action); 4331 _exit(-1); 4332 } 4333 4334 CompileOptions options(cx); 4335 options.setIntroductionType("reprl") 4336 .setFileAndLine("reprl", 1) 4337 .setIsRunOnce(true) 4338 .setNoScriptRval(true) 4339 .setEagerDelazificationStrategy(defaultDelazificationMode); 4340 4341 char* scriptSrc = static_cast<char*>(js_malloc(scriptSize)); 4342 4343 char* ptr = scriptSrc; 4344 size_t remaining = scriptSize; 4345 while (remaining > 0) { 4346 ssize_t rv = read(REPRL_DRFD, ptr, remaining); 4347 if (rv <= 0) { 4348 fprintf(stderr, "Failed to load script\n"); 4349 _exit(-1); 4350 } 4351 remaining -= rv; 4352 ptr += rv; 4353 } 4354 4355 JS::SourceText<Utf8Unit> srcBuf; 4356 if (!srcBuf.init(cx, scriptSrc, scriptSize, 4357 JS::SourceOwnership::TakeOwnership)) { 4358 return false; 4359 } 4360 4361 RootedScript script(cx, JS::Compile(cx, options, srcBuf)); 4362 if (!script) { 4363 return false; 4364 } 4365 4366 if (!JS_ExecuteScript(cx, script)) { 4367 return false; 4368 } 4369 4370 return true; 4371 } 4372 4373 #endif /* FUZZING_JS_FUZZILLI */ 4374 4375 static bool FuzzilliUseReprlMode(OptionParser* op) { 4376 #ifdef FUZZING_JS_FUZZILLI 4377 // Check if we should use REPRL mode 4378 bool reprl_mode = op->getBoolOption("reprl"); 4379 if (reprl_mode) { 4380 // Check in with parent 4381 char helo[] = "HELO"; 4382 if (write(REPRL_CWFD, helo, 4) != 4 || read(REPRL_CRFD, helo, 4) != 4) { 4383 reprl_mode = false; 4384 } 4385 4386 if (memcmp(helo, "HELO", 4) != 0) { 4387 fprintf(stderr, "Invalid response from parent\n"); 4388 _exit(-1); 4389 } 4390 } 4391 return reprl_mode; 4392 #else 4393 return false; 4394 #endif /* FUZZING_JS_FUZZILLI */ 4395 } 4396 4397 static bool Crash(JSContext* cx, unsigned argc, Value* vp) { 4398 CallArgs args = CallArgsFromVp(argc, vp); 4399 if (args.length() == 0) { 4400 MOZ_CRASH("forced crash"); 4401 } 4402 RootedString message(cx, JS::ToString(cx, args[0])); 4403 if (!message) { 4404 return false; 4405 } 4406 UniqueChars utf8chars = JS_EncodeStringToUTF8(cx, message); 4407 if (!utf8chars) { 4408 return false; 4409 } 4410 if (args.get(1).isObject()) { 4411 RootedValue v(cx); 4412 RootedObject opts(cx, &args[1].toObject()); 4413 if (!JS_GetProperty(cx, opts, "suppress_minidump", &v)) { 4414 return false; 4415 } 4416 if (v.isBoolean() && v.toBoolean()) { 4417 js::NoteIntentionalCrash(); 4418 } 4419 } 4420 #ifndef DEBUG 4421 MOZ_ReportCrash(utf8chars.get(), __FILE__, __LINE__); 4422 #endif 4423 MOZ_CRASH_UNSAFE(utf8chars.get()); 4424 } 4425 4426 static bool GetSLX(JSContext* cx, unsigned argc, Value* vp) { 4427 CallArgs args = CallArgsFromVp(argc, vp); 4428 RootedScript script(cx); 4429 4430 script = TestingFunctionArgumentToScript(cx, args.get(0)); 4431 if (!script) { 4432 return false; 4433 } 4434 args.rval().setInt32(GetScriptLineExtent(script)); 4435 return true; 4436 } 4437 4438 static bool ThrowError(JSContext* cx, unsigned argc, Value* vp) { 4439 JS_ReportErrorASCII(cx, "This is an error"); 4440 return false; 4441 } 4442 4443 static bool CopyErrorReportToObject(JSContext* cx, JSErrorReport* report, 4444 HandleObject obj) { 4445 RootedString nameStr(cx); 4446 if (report->exnType == JSEXN_WARN) { 4447 nameStr = JS_NewStringCopyZ(cx, "Warning"); 4448 if (!nameStr) { 4449 return false; 4450 } 4451 } else { 4452 nameStr = GetErrorTypeName(cx, report->exnType); 4453 // GetErrorTypeName doesn't set an exception, but 4454 // can fail for InternalError or non-error objects. 4455 if (!nameStr) { 4456 nameStr = cx->runtime()->emptyString; 4457 } 4458 } 4459 RootedValue nameVal(cx, StringValue(nameStr)); 4460 if (!DefineDataProperty(cx, obj, cx->names().name, nameVal)) { 4461 return false; 4462 } 4463 4464 RootedString messageStr(cx, report->newMessageString(cx)); 4465 if (!messageStr) { 4466 return false; 4467 } 4468 RootedValue messageVal(cx, StringValue(messageStr)); 4469 if (!DefineDataProperty(cx, obj, cx->names().message, messageVal)) { 4470 return false; 4471 } 4472 4473 RootedValue linenoVal(cx, Int32Value(report->lineno)); 4474 if (!DefineDataProperty(cx, obj, cx->names().lineNumber, linenoVal)) { 4475 return false; 4476 } 4477 4478 RootedValue columnVal(cx, Int32Value(report->column.oneOriginValue())); 4479 if (!DefineDataProperty(cx, obj, cx->names().columnNumber, columnVal)) { 4480 return false; 4481 } 4482 4483 RootedObject notesArray(cx, CreateErrorNotesArray(cx, report)); 4484 if (!notesArray) { 4485 return false; 4486 } 4487 4488 RootedValue notesArrayVal(cx, ObjectValue(*notesArray)); 4489 return DefineDataProperty(cx, obj, cx->names().notes, notesArrayVal); 4490 } 4491 4492 static bool CreateErrorReport(JSContext* cx, unsigned argc, Value* vp) { 4493 CallArgs args = CallArgsFromVp(argc, vp); 4494 4495 // We don't have a stack here, so just initialize with null. 4496 JS::ExceptionStack exnStack(cx, args.get(0), nullptr); 4497 JS::ErrorReportBuilder report(cx); 4498 if (!report.init(cx, exnStack, JS::ErrorReportBuilder::WithSideEffects)) { 4499 return false; 4500 } 4501 4502 MOZ_ASSERT(!report.report()->isWarning()); 4503 4504 RootedObject obj(cx, JS_NewPlainObject(cx)); 4505 if (!obj) { 4506 return false; 4507 } 4508 4509 RootedString toString(cx, NewStringCopyUTF8Z(cx, report.toStringResult())); 4510 if (!toString) { 4511 return false; 4512 } 4513 4514 if (!JS_DefineProperty(cx, obj, "toStringResult", toString, 4515 JSPROP_ENUMERATE)) { 4516 return false; 4517 } 4518 4519 if (!CopyErrorReportToObject(cx, report.report(), obj)) { 4520 return false; 4521 } 4522 4523 args.rval().setObject(*obj); 4524 return true; 4525 } 4526 4527 #define LAZY_STANDARD_CLASSES 4528 4529 /* A class for easily testing the inner/outer object callbacks. */ 4530 struct ComplexObject { 4531 bool isInner; 4532 bool frozen; 4533 JSObject* inner; 4534 JSObject* outer; 4535 }; 4536 4537 static bool sandbox_enumerate(JSContext* cx, JS::HandleObject obj, 4538 JS::MutableHandleIdVector properties, 4539 bool enumerableOnly) { 4540 RootedValue v(cx); 4541 4542 if (!JS_GetProperty(cx, obj, "lazy", &v)) { 4543 return false; 4544 } 4545 4546 if (!ToBoolean(v)) { 4547 return true; 4548 } 4549 4550 return JS_NewEnumerateStandardClasses(cx, obj, properties, enumerableOnly); 4551 } 4552 4553 static bool sandbox_resolve(JSContext* cx, HandleObject obj, HandleId id, 4554 bool* resolvedp) { 4555 RootedValue v(cx); 4556 if (!JS_GetProperty(cx, obj, "lazy", &v)) { 4557 return false; 4558 } 4559 4560 if (ToBoolean(v)) { 4561 return JS_ResolveStandardClass(cx, obj, id, resolvedp); 4562 } 4563 return true; 4564 } 4565 4566 static const JSClassOps sandbox_classOps = { 4567 nullptr, // addProperty 4568 nullptr, // delProperty 4569 nullptr, // enumerate 4570 sandbox_enumerate, // newEnumerate 4571 sandbox_resolve, // resolve 4572 nullptr, // mayResolve 4573 nullptr, // finalize 4574 nullptr, // call 4575 nullptr, // construct 4576 JS_GlobalObjectTraceHook, // trace 4577 }; 4578 4579 static const JSClass sandbox_class = { 4580 "sandbox", 4581 JSCLASS_GLOBAL_FLAGS, 4582 &sandbox_classOps, 4583 }; 4584 4585 static void SetStandardRealmOptions(JS::RealmOptions& options) { 4586 options.creationOptions() 4587 .setSharedMemoryAndAtomicsEnabled(enableSharedMemory) 4588 .setCoopAndCoepEnabled(false) 4589 .setToSourceEnabled(enableToSource); 4590 } 4591 4592 [[nodiscard]] static bool CheckRealmOptions(JSContext* cx, 4593 JS::RealmOptions& options, 4594 JSPrincipals* principals) { 4595 JS::RealmCreationOptions& creationOptions = options.creationOptions(); 4596 if (creationOptions.compartmentSpecifier() != 4597 JS::CompartmentSpecifier::ExistingCompartment) { 4598 return true; 4599 } 4600 4601 JS::Compartment* comp = creationOptions.compartment(); 4602 4603 // All realms in a compartment must be either system or non-system. 4604 bool isSystem = 4605 principals && principals == cx->runtime()->trustedPrincipals(); 4606 if (isSystem != IsSystemCompartment(comp)) { 4607 JS_ReportErrorASCII(cx, 4608 "Cannot create system and non-system realms in the " 4609 "same compartment"); 4610 return false; 4611 } 4612 4613 // Debugger visibility is per-compartment, not per-realm, so make sure the 4614 // requested visibility matches the existing compartment's. 4615 if (creationOptions.invisibleToDebugger() != comp->invisibleToDebugger()) { 4616 JS_ReportErrorASCII(cx, 4617 "All the realms in a compartment must have " 4618 "the same debugger visibility"); 4619 return false; 4620 } 4621 4622 return true; 4623 } 4624 4625 static JSObject* NewSandbox(JSContext* cx, bool lazy) { 4626 JS::RealmOptions options; 4627 SetStandardRealmOptions(options); 4628 4629 if (defaultToSameCompartment) { 4630 options.creationOptions().setExistingCompartment(cx->global()); 4631 } else { 4632 options.creationOptions().setNewCompartmentAndZone(); 4633 } 4634 4635 JSPrincipals* principals = nullptr; 4636 if (!CheckRealmOptions(cx, options, principals)) { 4637 return nullptr; 4638 } 4639 4640 RootedObject obj(cx, 4641 JS_NewGlobalObject(cx, &sandbox_class, principals, 4642 JS::DontFireOnNewGlobalHook, options)); 4643 if (!obj) { 4644 return nullptr; 4645 } 4646 4647 { 4648 JSAutoRealm ar(cx, obj); 4649 if (!lazy && !JS::InitRealmStandardClasses(cx)) { 4650 return nullptr; 4651 } 4652 4653 RootedValue value(cx, BooleanValue(lazy)); 4654 if (!JS_DefineProperty(cx, obj, "lazy", value, 4655 JSPROP_PERMANENT | JSPROP_READONLY)) { 4656 return nullptr; 4657 } 4658 4659 JS_FireOnNewGlobalObject(cx, obj); 4660 } 4661 4662 if (!cx->compartment()->wrap(cx, &obj)) { 4663 return nullptr; 4664 } 4665 return obj; 4666 } 4667 4668 static bool EvalInContext(JSContext* cx, unsigned argc, Value* vp) { 4669 CallArgs args = CallArgsFromVp(argc, vp); 4670 if (!args.requireAtLeast(cx, "evalcx", 1)) { 4671 return false; 4672 } 4673 4674 RootedString str(cx, ToString(cx, args[0])); 4675 if (!str) { 4676 return false; 4677 } 4678 4679 RootedObject sobj(cx); 4680 if (args.hasDefined(1)) { 4681 sobj = ToObject(cx, args[1]); 4682 if (!sobj) { 4683 return false; 4684 } 4685 } 4686 4687 AutoStableStringChars strChars(cx); 4688 if (!strChars.initTwoByte(cx, str)) { 4689 return false; 4690 } 4691 4692 mozilla::Range<const char16_t> chars = strChars.twoByteRange(); 4693 size_t srclen = chars.length(); 4694 const char16_t* src = chars.begin().get(); 4695 4696 bool lazy = false; 4697 if (srclen == 4) { 4698 if (src[0] == 'l' && src[1] == 'a' && src[2] == 'z' && src[3] == 'y') { 4699 lazy = true; 4700 srclen = 0; 4701 } 4702 } 4703 4704 if (!sobj) { 4705 sobj = NewSandbox(cx, lazy); 4706 if (!sobj) { 4707 return false; 4708 } 4709 } 4710 4711 if (srclen == 0) { 4712 args.rval().setObject(*sobj); 4713 return true; 4714 } 4715 4716 JS::AutoFilename filename; 4717 uint32_t lineno; 4718 4719 DescribeScriptedCaller(&filename, cx, &lineno); 4720 { 4721 sobj = UncheckedUnwrap(sobj, true); 4722 4723 JSAutoRealm ar(cx, sobj); 4724 4725 sobj = ToWindowIfWindowProxy(sobj); 4726 4727 if (!JS_IsGlobalObject(sobj)) { 4728 JS_ReportErrorASCII(cx, "Invalid scope argument to evalcx"); 4729 return false; 4730 } 4731 4732 JS::CompileOptions opts(cx); 4733 opts.setFileAndLine(filename.get(), lineno) 4734 .setEagerDelazificationStrategy(defaultDelazificationMode); 4735 4736 JS::SourceText<char16_t> srcBuf; 4737 if (!srcBuf.init(cx, src, srclen, JS::SourceOwnership::Borrowed) || 4738 !JS::Evaluate(cx, opts, srcBuf, args.rval())) { 4739 return false; 4740 } 4741 } 4742 4743 if (!cx->compartment()->wrap(cx, args.rval())) { 4744 return false; 4745 } 4746 4747 return true; 4748 } 4749 4750 static bool EnsureGeckoProfilingStackInstalled(JSContext* cx, 4751 ShellContext* sc) { 4752 if (cx->geckoProfiler().infraInstalled()) { 4753 MOZ_ASSERT(sc->geckoProfilingStack); 4754 return true; 4755 } 4756 4757 MOZ_ASSERT(!sc->geckoProfilingStack); 4758 sc->geckoProfilingStack = MakeUnique<ProfilingStack>(); 4759 if (!sc->geckoProfilingStack) { 4760 JS_ReportOutOfMemory(cx); 4761 return false; 4762 } 4763 4764 SetContextProfilingStack(cx, sc->geckoProfilingStack.get()); 4765 return true; 4766 } 4767 4768 struct WorkerInput { 4769 JSRuntime* parentRuntime; 4770 UniqueTwoByteChars chars; 4771 size_t length; 4772 4773 WorkerInput(JSRuntime* parentRuntime, UniqueTwoByteChars chars, size_t length) 4774 : parentRuntime(parentRuntime), chars(std::move(chars)), length(length) {} 4775 }; 4776 4777 static void DestroyShellCompartmentPrivate(JS::GCContext* gcx, 4778 JS::Compartment* compartment) { 4779 auto priv = static_cast<ShellCompartmentPrivate*>( 4780 JS_GetCompartmentPrivate(compartment)); 4781 js_delete(priv); 4782 } 4783 4784 static void SetWorkerContextOptions(JSContext* cx); 4785 static bool ShellBuildId(JS::BuildIdCharVector* buildId); 4786 4787 static constexpr size_t gWorkerStackSize = 2 * 128 * sizeof(size_t) * 1024; 4788 4789 static void WorkerMain(UniquePtr<WorkerInput> input) { 4790 MOZ_ASSERT(input->parentRuntime); 4791 4792 JSContext* cx = JS_NewContext(8L * 1024L * 1024L, input->parentRuntime); 4793 if (!cx) { 4794 return; 4795 } 4796 auto destroyContext = MakeScopeExit([cx] { JS_DestroyContext(cx); }); 4797 4798 UniquePtr<ShellContext> sc = 4799 MakeUnique<ShellContext>(cx, ShellContext::Worker); 4800 if (!sc || !sc->registerWithCx(cx)) { 4801 return; 4802 } 4803 4804 if (!JS::InitSelfHostedCode(cx)) { 4805 return; 4806 } 4807 4808 EnvironmentPreparer environmentPreparer(cx); 4809 4810 do { 4811 JS::RealmOptions realmOptions; 4812 SetStandardRealmOptions(realmOptions); 4813 4814 RootedObject global(cx, NewGlobalObject(cx, realmOptions, nullptr, 4815 ShellGlobalKind::WindowProxy, 4816 /* immutablePrototype = */ true)); 4817 if (!global) { 4818 break; 4819 } 4820 4821 JSAutoRealm ar(cx, global); 4822 4823 JS::ConstUTF8CharsZ path(processWideModuleLoadPath.get(), 4824 strlen(processWideModuleLoadPath.get())); 4825 RootedString moduleLoadPath(cx, JS_NewStringCopyUTF8Z(cx, path)); 4826 if (!moduleLoadPath) { 4827 return; 4828 } 4829 sc->moduleLoader = js::MakeUnique<ModuleLoader>(); 4830 if (!sc->moduleLoader || !sc->moduleLoader->init(cx, moduleLoadPath)) { 4831 return; 4832 } 4833 4834 JS::CompileOptions options(cx); 4835 options.setFileAndLine("<string>", 1) 4836 .setIsRunOnce(true) 4837 .setEagerDelazificationStrategy(defaultDelazificationMode); 4838 4839 { 4840 AutoReportException are(cx); 4841 JS::SourceText<char16_t> srcBuf; 4842 if (!srcBuf.init(cx, input->chars.get(), input->length, 4843 JS::SourceOwnership::Borrowed)) { 4844 break; 4845 } 4846 4847 RootedScript script(cx, JS::Compile(cx, options, srcBuf)); 4848 if (!script) { 4849 break; 4850 } 4851 RootedValue result(cx); 4852 JS_ExecuteScript(cx, script, &result); 4853 } 4854 4855 RunShellJobs(cx); 4856 } while (0); 4857 4858 KillWatchdog(cx); 4859 } 4860 4861 // Workers can spawn other workers, so we need a lock to access workerThreads. 4862 static Mutex* workerThreadsLock = nullptr; 4863 MOZ_RUNINIT static Vector<UniquePtr<js::Thread>, 0, SystemAllocPolicy> 4864 workerThreads; 4865 4866 class MOZ_RAII AutoLockWorkerThreads : public LockGuard<Mutex> { 4867 using Base = LockGuard<Mutex>; 4868 4869 public: 4870 AutoLockWorkerThreads() : Base(*workerThreadsLock) { 4871 MOZ_ASSERT(workerThreadsLock); 4872 } 4873 }; 4874 4875 static bool EvalInWorker(JSContext* cx, unsigned argc, Value* vp) { 4876 if (!CanUseExtraThreads()) { 4877 JS_ReportErrorASCII(cx, "Can't create threads with --no-threads"); 4878 return false; 4879 } 4880 4881 CallArgs args = CallArgsFromVp(argc, vp); 4882 if (!args.get(0).isString()) { 4883 JS_ReportErrorASCII(cx, "Invalid arguments"); 4884 return false; 4885 } 4886 4887 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT) 4888 if (cx->runningOOMTest) { 4889 JS_ReportErrorASCII( 4890 cx, "Can't create threads while running simulated OOM test"); 4891 return false; 4892 } 4893 #endif 4894 4895 if (!args[0].toString()->ensureLinear(cx)) { 4896 return false; 4897 } 4898 4899 if (!workerThreadsLock) { 4900 workerThreadsLock = js_new<Mutex>(mutexid::ShellWorkerThreads); 4901 if (!workerThreadsLock) { 4902 ReportOutOfMemory(cx); 4903 return false; 4904 } 4905 } 4906 4907 JSLinearString* str = &args[0].toString()->asLinear(); 4908 4909 UniqueTwoByteChars chars(js_pod_malloc<char16_t>(str->length())); 4910 if (!chars) { 4911 ReportOutOfMemory(cx); 4912 return false; 4913 } 4914 4915 CopyChars(chars.get(), *str); 4916 4917 auto input = js::MakeUnique<WorkerInput>(JS_GetParentRuntime(cx), 4918 std::move(chars), str->length()); 4919 if (!input) { 4920 ReportOutOfMemory(cx); 4921 return false; 4922 } 4923 4924 UniquePtr<Thread> thread; 4925 { 4926 AutoEnterOOMUnsafeRegion oomUnsafe; 4927 thread = js::MakeUnique<Thread>( 4928 Thread::Options().setStackSize(gWorkerStackSize + 512 * 1024)); 4929 if (!thread || !thread->init(WorkerMain, std::move(input))) { 4930 oomUnsafe.crash("EvalInWorker"); 4931 } 4932 } 4933 4934 AutoLockWorkerThreads alwt; 4935 if (!workerThreads.append(std::move(thread))) { 4936 ReportOutOfMemory(cx); 4937 thread->join(); 4938 return false; 4939 } 4940 4941 args.rval().setUndefined(); 4942 return true; 4943 } 4944 4945 static bool ShapeOf(JSContext* cx, unsigned argc, JS::Value* vp) { 4946 CallArgs args = CallArgsFromVp(argc, vp); 4947 if (!args.get(0).isObject()) { 4948 JS_ReportErrorASCII(cx, "shapeOf: object expected"); 4949 return false; 4950 } 4951 JSObject* obj = &args[0].toObject(); 4952 args.rval().set(JS_NumberValue(double(uintptr_t(obj->shape()) >> 3))); 4953 return true; 4954 } 4955 4956 static bool Sleep_fn(JSContext* cx, unsigned argc, Value* vp) { 4957 ShellContext* sc = GetShellContext(cx); 4958 CallArgs args = CallArgsFromVp(argc, vp); 4959 4960 TimeDuration duration = TimeDuration::FromSeconds(0.0); 4961 if (args.length() > 0) { 4962 double t_secs; 4963 if (!ToNumber(cx, args[0], &t_secs)) { 4964 return false; 4965 } 4966 if (std::isnan(t_secs)) { 4967 JS_ReportErrorASCII(cx, "sleep interval is not a number"); 4968 return false; 4969 } 4970 4971 duration = TimeDuration::FromSeconds(std::max(0.0, t_secs)); 4972 const TimeDuration MAX_TIMEOUT_INTERVAL = 4973 TimeDuration::FromSeconds(MAX_TIMEOUT_SECONDS); 4974 if (duration > MAX_TIMEOUT_INTERVAL) { 4975 JS_ReportErrorASCII(cx, "Excessive sleep interval"); 4976 return false; 4977 } 4978 } 4979 { 4980 LockGuard<Mutex> guard(sc->watchdogLock); 4981 TimeStamp toWakeup = TimeStamp::Now() + duration; 4982 for (;;) { 4983 sc->sleepWakeup.wait_for(guard, duration); 4984 if (sc->serviceInterrupt) { 4985 break; 4986 } 4987 auto now = TimeStamp::Now(); 4988 if (now >= toWakeup) { 4989 break; 4990 } 4991 duration = toWakeup - now; 4992 } 4993 } 4994 4995 if (sc->serviceInterrupt) { 4996 JS::ReportUncatchableException(cx); 4997 return false; 4998 } 4999 5000 args.rval().setUndefined(); 5001 return true; 5002 } 5003 5004 static void KillWatchdog(JSContext* cx) { 5005 ShellContext* sc = GetShellContext(cx); 5006 Maybe<Thread> thread; 5007 5008 { 5009 LockGuard<Mutex> guard(sc->watchdogLock); 5010 std::swap(sc->watchdogThread, thread); 5011 if (thread) { 5012 // The watchdog thread becoming Nothing is its signal to exit. 5013 sc->watchdogWakeup.notify_one(); 5014 } 5015 } 5016 if (thread) { 5017 thread->join(); 5018 } 5019 5020 MOZ_ASSERT(!sc->watchdogThread); 5021 } 5022 5023 static void WatchdogMain(JSContext* cx) { 5024 ThisThread::SetName("JS Watchdog"); 5025 5026 ShellContext* sc = GetShellContext(cx); 5027 5028 { 5029 LockGuard<Mutex> guard(sc->watchdogLock); 5030 while (sc->watchdogThread) { 5031 auto now = TimeStamp::Now(); 5032 if (sc->watchdogTimeout && now >= sc->watchdogTimeout.value()) { 5033 /* 5034 * The timeout has just expired. Request an interrupt callback 5035 * outside the lock. 5036 */ 5037 sc->watchdogTimeout = Nothing(); 5038 { 5039 UnlockGuard unlock(guard); 5040 CancelExecution(cx); 5041 } 5042 5043 /* Wake up any threads doing sleep. */ 5044 sc->sleepWakeup.notify_all(); 5045 } else { 5046 if (sc->watchdogTimeout) { 5047 /* 5048 * Time hasn't expired yet. Simulate an interrupt callback 5049 * which doesn't abort execution. 5050 */ 5051 JS_RequestInterruptCallback(cx); 5052 } 5053 5054 TimeDuration sleepDuration = sc->watchdogTimeout 5055 ? TimeDuration::FromSeconds(0.1) 5056 : TimeDuration::Forever(); 5057 sc->watchdogWakeup.wait_for(guard, sleepDuration); 5058 } 5059 } 5060 } 5061 } 5062 5063 static bool ScheduleWatchdog(JSContext* cx, double t) { 5064 ShellContext* sc = GetShellContext(cx); 5065 5066 if (t <= 0) { 5067 LockGuard<Mutex> guard(sc->watchdogLock); 5068 sc->watchdogTimeout = Nothing(); 5069 return true; 5070 } 5071 5072 #ifdef __wasi__ 5073 return false; 5074 #endif 5075 5076 auto interval = TimeDuration::FromSeconds(t); 5077 auto timeout = TimeStamp::Now() + interval; 5078 LockGuard<Mutex> guard(sc->watchdogLock); 5079 if (!sc->watchdogThread) { 5080 MOZ_ASSERT(!sc->watchdogTimeout); 5081 sc->watchdogThread.emplace(); 5082 AutoEnterOOMUnsafeRegion oomUnsafe; 5083 if (!sc->watchdogThread->init(WatchdogMain, cx)) { 5084 oomUnsafe.crash("watchdogThread.init"); 5085 } 5086 } else if (!sc->watchdogTimeout || timeout < sc->watchdogTimeout.value()) { 5087 sc->watchdogWakeup.notify_one(); 5088 } 5089 sc->watchdogTimeout = Some(timeout); 5090 return true; 5091 } 5092 5093 static void KillWorkerThreads(JSContext* cx) { 5094 MOZ_ASSERT_IF(!CanUseExtraThreads(), workerThreads.empty()); 5095 5096 if (!workerThreadsLock) { 5097 MOZ_ASSERT(workerThreads.empty()); 5098 return; 5099 } 5100 5101 while (true) { 5102 // We need to leave the AutoLockWorkerThreads scope before we call 5103 // js::Thread::join, to avoid deadlocks when AutoLockWorkerThreads is 5104 // used by the worker thread. 5105 UniquePtr<Thread> thread; 5106 { 5107 AutoLockWorkerThreads alwt; 5108 if (workerThreads.empty()) { 5109 break; 5110 } 5111 thread = std::move(workerThreads.back()); 5112 workerThreads.popBack(); 5113 } 5114 thread->join(); 5115 } 5116 5117 workerThreads.clearAndFree(); 5118 5119 js_delete(workerThreadsLock); 5120 workerThreadsLock = nullptr; 5121 } 5122 5123 static void CancelExecution(JSContext* cx) { 5124 ShellContext* sc = GetShellContext(cx); 5125 sc->serviceInterrupt = true; 5126 JS_RequestInterruptCallback(cx); 5127 } 5128 5129 static bool SetTimeoutValue(JSContext* cx, double t) { 5130 if (std::isnan(t)) { 5131 JS_ReportErrorASCII(cx, "timeout is not a number"); 5132 return false; 5133 } 5134 const TimeDuration MAX_TIMEOUT_INTERVAL = 5135 TimeDuration::FromSeconds(MAX_TIMEOUT_SECONDS); 5136 if (TimeDuration::FromSeconds(t) > MAX_TIMEOUT_INTERVAL) { 5137 JS_ReportErrorASCII(cx, "Excessive timeout value"); 5138 return false; 5139 } 5140 GetShellContext(cx)->timeoutInterval = t; 5141 if (!ScheduleWatchdog(cx, t)) { 5142 JS_ReportErrorASCII(cx, "Failed to create the watchdog"); 5143 return false; 5144 } 5145 return true; 5146 } 5147 5148 static bool MaybeSetInterruptFunc(JSContext* cx, HandleValue func) { 5149 ShellContext* sc = GetShellContext(cx); 5150 5151 bool isSupportedFunction = 5152 func.isObject() && func.toObject().is<JSFunction>() && !fuzzingSafe; 5153 if (!func.isString() && !isSupportedFunction) { 5154 JS_ReportErrorASCII(cx, "Argument must be a function or string"); 5155 return false; 5156 } 5157 sc->interruptFunc = func; 5158 sc->haveInterruptFunc = true; 5159 5160 return true; 5161 } 5162 5163 static bool Timeout(JSContext* cx, unsigned argc, Value* vp) { 5164 ShellContext* sc = GetShellContext(cx); 5165 CallArgs args = CallArgsFromVp(argc, vp); 5166 5167 if (args.length() == 0) { 5168 args.rval().setNumber(sc->timeoutInterval); 5169 return true; 5170 } 5171 5172 if (args.length() > 2) { 5173 JS_ReportErrorASCII(cx, "Wrong number of arguments"); 5174 return false; 5175 } 5176 5177 double t; 5178 if (!ToNumber(cx, args[0], &t)) { 5179 return false; 5180 } 5181 5182 if (args.length() > 1) { 5183 RootedValue value(cx, args[1]); 5184 if (!MaybeSetInterruptFunc(cx, value)) { 5185 return false; 5186 } 5187 } 5188 5189 args.rval().setUndefined(); 5190 return SetTimeoutValue(cx, t); 5191 } 5192 5193 static bool InterruptIf(JSContext* cx, unsigned argc, Value* vp) { 5194 CallArgs args = CallArgsFromVp(argc, vp); 5195 5196 if (args.length() != 1) { 5197 JS_ReportErrorASCII(cx, "Wrong number of arguments"); 5198 return false; 5199 } 5200 5201 if (ToBoolean(args[0])) { 5202 GetShellContext(cx)->serviceInterrupt = true; 5203 JS_RequestInterruptCallback(cx); 5204 } 5205 5206 args.rval().setUndefined(); 5207 return true; 5208 } 5209 5210 static bool InvokeInterruptCallbackWrapper(JSContext* cx, unsigned argc, 5211 Value* vp) { 5212 CallArgs args = CallArgsFromVp(argc, vp); 5213 if (args.length() != 1) { 5214 JS_ReportErrorASCII(cx, "Wrong number of arguments"); 5215 return false; 5216 } 5217 5218 GetShellContext(cx)->serviceInterrupt = true; 5219 JS_RequestInterruptCallback(cx); 5220 bool interruptRv = CheckForInterrupt(cx); 5221 5222 // The interrupt handler could have set a pending exception. Since we call 5223 // back into JS, don't have it see the pending exception. If we have an 5224 // uncatchable exception that's not propagating a debug mode forced 5225 // return, return. 5226 if (!interruptRv && !cx->isExceptionPending() && 5227 !cx->isPropagatingForcedReturn()) { 5228 return false; 5229 } 5230 5231 JS::AutoSaveExceptionState savedExc(cx); 5232 5233 FixedInvokeArgs<1> iargs(cx); 5234 5235 iargs[0].setBoolean(interruptRv); 5236 5237 RootedValue rv(cx); 5238 if (!js::Call(cx, args[0], UndefinedHandleValue, iargs, &rv)) { 5239 return false; 5240 } 5241 5242 args.rval().setUndefined(); 5243 return interruptRv; 5244 } 5245 5246 static bool SetInterruptCallback(JSContext* cx, unsigned argc, Value* vp) { 5247 CallArgs args = CallArgsFromVp(argc, vp); 5248 5249 if (args.length() != 1) { 5250 JS_ReportErrorASCII(cx, "Wrong number of arguments"); 5251 return false; 5252 } 5253 5254 RootedValue value(cx, args[0]); 5255 if (!MaybeSetInterruptFunc(cx, value)) { 5256 return false; 5257 } 5258 5259 args.rval().setUndefined(); 5260 return true; 5261 } 5262 5263 #ifdef DEBUG 5264 // var s0 = "A".repeat(10*1024); 5265 // interruptRegexp(/a(bc|bd)/, s0); 5266 // first arg is regexp 5267 // second arg is string 5268 static bool InterruptRegexp(JSContext* cx, unsigned argc, Value* vp) { 5269 CallArgs args = CallArgsFromVp(argc, vp); 5270 ShellContext* sc = GetShellContext(cx); 5271 RootedObject callee(cx, &args.callee()); 5272 5273 if (args.length() != 2) { 5274 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments."); 5275 return false; 5276 } 5277 if (!(args[0].isObject() && args[0].toObject().is<RegExpObject>())) { 5278 ReportUsageErrorASCII(cx, callee, 5279 "First argument must be a regular expression."); 5280 return false; 5281 } 5282 if (!args[1].isString()) { 5283 ReportUsageErrorASCII(cx, callee, "Second argument must be a String."); 5284 return false; 5285 } 5286 // Set interrupt flags 5287 sc->serviceInterrupt = true; 5288 js::irregexp::IsolateSetShouldSimulateInterrupt(cx->isolate); 5289 5290 RootedObject regexp(cx, &args[0].toObject()); 5291 RootedString string(cx, args[1].toString()); 5292 int32_t lastIndex = 0; 5293 5294 return js::RegExpMatcherRaw(cx, regexp, string, lastIndex, nullptr, 5295 args.rval()); 5296 } 5297 #endif 5298 5299 static bool CheckRegExpSyntax(JSContext* cx, unsigned argc, Value* vp) { 5300 CallArgs args = CallArgsFromVp(argc, vp); 5301 RootedObject callee(cx, &args.callee()); 5302 5303 if (args.length() != 1) { 5304 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments."); 5305 return false; 5306 } 5307 if (!args[0].isString()) { 5308 ReportUsageErrorASCII(cx, callee, "First argument must be a string."); 5309 return false; 5310 } 5311 5312 RootedString string(cx, args[0].toString()); 5313 AutoStableStringChars stableChars(cx); 5314 if (!stableChars.initTwoByte(cx, string)) { 5315 return false; 5316 } 5317 5318 const char16_t* chars = stableChars.twoByteRange().begin().get(); 5319 size_t length = string->length(); 5320 5321 Rooted<JS::Value> error(cx); 5322 if (!JS::CheckRegExpSyntax(cx, chars, length, JS::RegExpFlag::NoFlags, 5323 &error)) { 5324 return false; 5325 } 5326 5327 args.rval().set(error); 5328 return true; 5329 } 5330 5331 static bool IsPrefAvailable(const char* pref) { 5332 if (!fuzzingSafe) { 5333 // All prefs in fuzzing unsafe mode are enabled. 5334 return true; 5335 } 5336 #define WASM_FEATURE(NAME, LOWER_NAME, COMPILE_PRED, COMPILER_PRED, FLAG_PRED, \ 5337 FLAG_FORCE_ON, FLAG_FUZZ_ON, PREF) \ 5338 if constexpr (!FLAG_FUZZ_ON) { \ 5339 if (strcmp("wasm_" #PREF, pref) == 0) { \ 5340 return false; \ 5341 } \ 5342 } 5343 JS_FOR_WASM_FEATURES(WASM_FEATURE) 5344 #undef WASM_FEATURE 5345 return true; 5346 } 5347 5348 template <typename T> 5349 static bool ParsePrefValue(const char* name, const char* val, T* result) { 5350 if constexpr (std::is_same_v<T, bool>) { 5351 if (strcmp(val, "true") == 0) { 5352 *result = true; 5353 return true; 5354 } 5355 if (strcmp(val, "false") == 0) { 5356 *result = false; 5357 return true; 5358 } 5359 fprintf(stderr, "Invalid value for boolean pref %s: %s\n", name, val); 5360 return false; 5361 } else { 5362 static_assert(std::is_same_v<T, int32_t> || std::is_same_v<T, uint32_t>); 5363 char* end; 5364 long v = strtol(val, &end, 10); 5365 if (end != val + strlen(val) || static_cast<long>(static_cast<T>(v)) != v) { 5366 fprintf(stderr, "Invalid value for integer pref %s: %s\n", name, val); 5367 return false; 5368 } 5369 *result = static_cast<T>(v); 5370 return true; 5371 } 5372 } 5373 5374 static bool SetPrefToTrueForBool(const char* name) { 5375 // Search for a matching pref and try to set it to a default value for the 5376 // type. 5377 #define CHECK_PREF(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF) \ 5378 if (strcmp(name, NAME) == 0) { \ 5379 if constexpr (std::is_same_v<TYPE, bool>) { \ 5380 JS::Prefs::SETTER(true); \ 5381 return true; \ 5382 } else { \ 5383 fprintf(stderr, "Pref %s must have a value specified.\n", name); \ 5384 return false; \ 5385 } \ 5386 } 5387 FOR_EACH_JS_PREF(CHECK_PREF) 5388 #undef CHECK_PREF 5389 5390 // Nothing matched. If --fuzzing-safe is used, return true after printing a 5391 // message, to continue execution without breaking fuzzing when a pref is 5392 // removed. 5393 if (fuzzingSafe) { 5394 fprintf(stderr, "Warning: Ignoring unknown pref name: %s\n", name); 5395 return true; 5396 } 5397 fprintf(stderr, "Invalid pref name: %s\n", name); 5398 return false; 5399 } 5400 5401 template <typename T> 5402 static bool ConvertPrefValue(JSContext* cx, HandleValue val, T* result) { 5403 if constexpr (std::is_same_v<T, bool>) { 5404 *result = ToBoolean(val); 5405 return true; 5406 } else if constexpr (std::is_same_v<T, int32_t>) { 5407 return ToInt32(cx, val, result); 5408 } else { 5409 static_assert(std::is_same_v<T, uint32_t>); 5410 return ToUint32(cx, val, result); 5411 } 5412 } 5413 5414 static bool SetPrefValue(JSContext* cx, unsigned argc, Value* vp) { 5415 CallArgs args = CallArgsFromVp(argc, vp); 5416 if (!args.requireAtLeast(cx, "setPrefValue", 2)) { 5417 return false; 5418 } 5419 5420 if (!args[0].isString()) { 5421 JS_ReportErrorASCII(cx, "expected string argument"); 5422 return false; 5423 } 5424 5425 Rooted<JSLinearString*> name(cx, args[0].toString()->ensureLinear(cx)); 5426 if (!name) { 5427 return false; 5428 } 5429 5430 // Search for a matching pref and try to set it to the provided value. 5431 #define CHECK_PREF(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF) \ 5432 if (IsPrefAvailable(NAME) && StringEqualsLiteral(name, NAME)) { \ 5433 if (IS_STARTUP_PREF) { \ 5434 JS_ReportErrorASCII(cx, "%s is a startup pref and can't be set", NAME); \ 5435 return false; \ 5436 } \ 5437 TYPE v; \ 5438 if (!ConvertPrefValue<TYPE>(cx, args[1], &v)) { \ 5439 return false; \ 5440 } \ 5441 JS::Prefs::SETTER(v); \ 5442 args.rval().setUndefined(); \ 5443 return true; \ 5444 } 5445 FOR_EACH_JS_PREF(CHECK_PREF) 5446 #undef CHECK_PREF 5447 5448 // Fuzzing ignores missing prefs so it doesn't break if we remove a pref 5449 if (fuzzingSafe) { 5450 args.rval().setUndefined(); 5451 return true; 5452 } 5453 JS_ReportErrorASCII(cx, "invalid pref name"); 5454 return false; 5455 } 5456 5457 static bool SetPrefToValue(const char* name, size_t nameLen, 5458 const char* value) { 5459 // Search for a matching pref and try to set it to the provided value. 5460 #define CHECK_PREF(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF) \ 5461 if (nameLen == strlen(NAME) && memcmp(name, NAME, strlen(NAME)) == 0) { \ 5462 TYPE v; \ 5463 if (!ParsePrefValue<TYPE>(NAME, value, &v)) { \ 5464 return false; \ 5465 } \ 5466 JS::Prefs::SETTER(v); \ 5467 return true; \ 5468 } 5469 FOR_EACH_JS_PREF(CHECK_PREF) 5470 #undef CHECK_PREF 5471 5472 // Nothing matched. If --fuzzing-safe is used, return true after printing a 5473 // message, to continue execution without breaking fuzzing when a pref is 5474 // removed. 5475 if (fuzzingSafe) { 5476 fprintf(stderr, "Warning: Ignoring unknown pref name: %s\n", name); 5477 return true; 5478 } 5479 fprintf(stderr, "Invalid pref name: %s\n", name); 5480 return false; 5481 } 5482 5483 static bool SetPref(const char* pref) { 5484 const char* assign = strchr(pref, '='); 5485 if (!assign) { 5486 if (IsPrefAvailable(pref) && !SetPrefToTrueForBool(pref)) { 5487 return false; 5488 } 5489 return true; 5490 } 5491 5492 size_t nameLen = assign - pref; 5493 const char* valStart = assign + 1; // Skip '='. 5494 5495 if (IsPrefAvailable(pref) && !SetPrefToValue(pref, nameLen, valStart)) { 5496 return false; 5497 } 5498 return true; 5499 } 5500 5501 static void ListPrefs() { 5502 auto printPref = [](const char* name, auto defaultVal) { 5503 if (!IsPrefAvailable(name)) { 5504 return; 5505 } 5506 using T = decltype(defaultVal); 5507 if constexpr (std::is_same_v<T, bool>) { 5508 fprintf(stderr, "%s=%s\n", name, defaultVal ? "true" : "false"); 5509 } else if constexpr (std::is_same_v<T, int32_t>) { 5510 fprintf(stderr, "%s=%d\n", name, defaultVal); 5511 } else { 5512 static_assert(std::is_same_v<T, uint32_t>); 5513 fprintf(stderr, "%s=%u\n", name, defaultVal); 5514 } 5515 }; 5516 5517 #define PRINT_PREF(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF) \ 5518 printPref(NAME, JS::Prefs::CPP_NAME()); 5519 FOR_EACH_JS_PREF(PRINT_PREF) 5520 #undef PRINT_PREF 5521 } 5522 5523 static bool SetJitCompilerOption(JSContext* cx, unsigned argc, Value* vp) { 5524 CallArgs args = CallArgsFromVp(argc, vp); 5525 RootedObject callee(cx, &args.callee()); 5526 5527 if (args.length() != 2) { 5528 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments."); 5529 return false; 5530 } 5531 5532 if (!args[0].isString()) { 5533 ReportUsageErrorASCII(cx, callee, "First argument must be a String."); 5534 return false; 5535 } 5536 5537 if (!args[1].isInt32()) { 5538 ReportUsageErrorASCII(cx, callee, "Second argument must be an Int32."); 5539 return false; 5540 } 5541 5542 // Disallow setting JIT options when there are worker threads, to avoid 5543 // races. 5544 if (workerThreadsLock) { 5545 ReportUsageErrorASCII( 5546 cx, callee, "Can't set JIT options when there are worker threads."); 5547 return false; 5548 } 5549 5550 JSLinearString* strArg = JS_EnsureLinearString(cx, args[0].toString()); 5551 if (!strArg) { 5552 return false; 5553 } 5554 5555 #define JIT_COMPILER_MATCH(key, string) \ 5556 else if (JS_LinearStringEqualsLiteral(strArg, string)) opt = \ 5557 JSJITCOMPILER_##key; 5558 5559 JSJitCompilerOption opt = JSJITCOMPILER_NOT_AN_OPTION; 5560 if (false) { 5561 } 5562 JIT_COMPILER_OPTIONS(JIT_COMPILER_MATCH); 5563 #undef JIT_COMPILER_MATCH 5564 5565 if (opt == JSJITCOMPILER_NOT_AN_OPTION) { 5566 ReportUsageErrorASCII( 5567 cx, callee, 5568 "First argument does not name a valid option (see jsapi.h)."); 5569 return false; 5570 } 5571 5572 int32_t number = args[1].toInt32(); 5573 if (number < 0) { 5574 number = -1; 5575 } 5576 5577 // Disallow enabling or disabling the Baseline Interpreter at runtime. 5578 // Enabling is a problem because the Baseline Interpreter code is only 5579 // present if the interpreter was enabled when the JitRuntime was created. 5580 // To support disabling we would have to discard all JitScripts. Furthermore, 5581 // we really want JitOptions to be immutable after startup so it's better to 5582 // use shell flags. 5583 if (opt == JSJITCOMPILER_BASELINE_INTERPRETER_ENABLE && 5584 bool(number) != jit::IsBaselineInterpreterEnabled()) { 5585 JS_ReportErrorASCII(cx, 5586 "Enabling or disabling the Baseline Interpreter at " 5587 "runtime is not supported."); 5588 return false; 5589 } 5590 5591 // Throw if disabling the JITs and there's JIT code on the stack, to avoid 5592 // assertion failures. 5593 if ((opt == JSJITCOMPILER_BASELINE_ENABLE || 5594 opt == JSJITCOMPILER_ION_ENABLE) && 5595 number == 0) { 5596 js::jit::JitActivationIterator iter(cx); 5597 if (!iter.done()) { 5598 JS_ReportErrorASCII(cx, 5599 "Can't turn off JITs with JIT code on the stack."); 5600 return false; 5601 } 5602 } 5603 5604 // Changing code memory protection settings at runtime is not supported. Don't 5605 // throw if not changing the setting because some jit-tests depend on that. 5606 if (opt == JSJITCOMPILER_WRITE_PROTECT_CODE) { 5607 uint32_t writeProtect; 5608 MOZ_ALWAYS_TRUE(JS_GetGlobalJitCompilerOption( 5609 cx, JSJITCOMPILER_WRITE_PROTECT_CODE, &writeProtect)); 5610 if (bool(number) != writeProtect) { 5611 JS_ReportErrorASCII(cx, "Can't change code write protection at runtime"); 5612 return false; 5613 } 5614 return true; 5615 } 5616 5617 // Throw if trying to disable all the Wasm compilers. The logic here is that 5618 // if we're trying to disable a compiler that is currently enabled and that is 5619 // the last compiler enabled then we must throw. 5620 // 5621 // Note that this check does not prevent an error from being thrown later. 5622 // Actual compiler availability is dynamic and depends on other conditions, 5623 // such as other options set and whether a debugger is present. 5624 if ((opt == JSJITCOMPILER_WASM_JIT_BASELINE || 5625 opt == JSJITCOMPILER_WASM_JIT_OPTIMIZING) && 5626 number == 0) { 5627 uint32_t baseline, optimizing; 5628 MOZ_ALWAYS_TRUE(JS_GetGlobalJitCompilerOption( 5629 cx, JSJITCOMPILER_WASM_JIT_BASELINE, &baseline)); 5630 MOZ_ALWAYS_TRUE(JS_GetGlobalJitCompilerOption( 5631 cx, JSJITCOMPILER_WASM_JIT_OPTIMIZING, &optimizing)); 5632 if (baseline + optimizing == 1) { 5633 if ((opt == JSJITCOMPILER_WASM_JIT_BASELINE && baseline) || 5634 (opt == JSJITCOMPILER_WASM_JIT_OPTIMIZING && optimizing)) { 5635 JS_ReportErrorASCII( 5636 cx, 5637 "Disabling all the Wasm compilers at runtime is not supported."); 5638 return false; 5639 } 5640 } 5641 } 5642 5643 // JIT compiler options are process-wide, so we have to stop off-thread 5644 // compilations for all runtimes to avoid races. 5645 WaitForAllHelperThreads(); 5646 5647 // Only release JIT code for the current runtime because there's no good 5648 // way to discard code for other runtimes. 5649 ReleaseAllJITCode(cx->gcContext()); 5650 5651 JS_SetGlobalJitCompilerOption(cx, opt, uint32_t(number)); 5652 5653 args.rval().setUndefined(); 5654 return true; 5655 } 5656 5657 static bool EnableLastWarning(JSContext* cx, unsigned argc, Value* vp) { 5658 ShellContext* sc = GetShellContext(cx); 5659 CallArgs args = CallArgsFromVp(argc, vp); 5660 5661 sc->lastWarningEnabled = true; 5662 sc->lastWarning.setNull(); 5663 5664 args.rval().setUndefined(); 5665 return true; 5666 } 5667 5668 static bool DisableLastWarning(JSContext* cx, unsigned argc, Value* vp) { 5669 ShellContext* sc = GetShellContext(cx); 5670 CallArgs args = CallArgsFromVp(argc, vp); 5671 5672 sc->lastWarningEnabled = false; 5673 sc->lastWarning.setNull(); 5674 5675 args.rval().setUndefined(); 5676 return true; 5677 } 5678 5679 static bool GetLastWarning(JSContext* cx, unsigned argc, Value* vp) { 5680 ShellContext* sc = GetShellContext(cx); 5681 CallArgs args = CallArgsFromVp(argc, vp); 5682 5683 if (!sc->lastWarningEnabled) { 5684 JS_ReportErrorASCII(cx, "Call enableLastWarning first."); 5685 return false; 5686 } 5687 5688 if (!JS_WrapValue(cx, &sc->lastWarning)) { 5689 return false; 5690 } 5691 5692 args.rval().set(sc->lastWarning); 5693 return true; 5694 } 5695 5696 static bool ClearLastWarning(JSContext* cx, unsigned argc, Value* vp) { 5697 ShellContext* sc = GetShellContext(cx); 5698 CallArgs args = CallArgsFromVp(argc, vp); 5699 5700 if (!sc->lastWarningEnabled) { 5701 JS_ReportErrorASCII(cx, "Call enableLastWarning first."); 5702 return false; 5703 } 5704 5705 sc->lastWarning.setNull(); 5706 5707 args.rval().setUndefined(); 5708 return true; 5709 } 5710 5711 #if defined(DEBUG) || defined(JS_JITSPEW) 5712 static bool StackDump(JSContext* cx, unsigned argc, Value* vp) { 5713 CallArgs args = CallArgsFromVp(argc, vp); 5714 5715 if (!gOutFile->isOpen()) { 5716 JS_ReportErrorASCII(cx, "output file is closed"); 5717 return false; 5718 } 5719 5720 bool showArgs = ToBoolean(args.get(0)); 5721 bool showLocals = ToBoolean(args.get(1)); 5722 bool showThisProps = ToBoolean(args.get(2)); 5723 5724 JS::UniqueChars buf = 5725 JS::FormatStackDump(cx, showArgs, showLocals, showThisProps); 5726 if (!buf) { 5727 fputs("Failed to format JavaScript stack for dump\n", gOutFile->fp); 5728 JS_ClearPendingException(cx); 5729 } else { 5730 fputs(buf.get(), gOutFile->fp); 5731 } 5732 5733 args.rval().setUndefined(); 5734 return true; 5735 } 5736 #endif 5737 5738 MOZ_ASAN_IGNORE static bool StackPointerInfo(JSContext* cx, unsigned argc, 5739 Value* vp) { 5740 CallArgs args = CallArgsFromVp(argc, vp); 5741 5742 // Copy the truncated stack pointer to the result. This value is not used 5743 // as a pointer but as a way to measure frame-size from JS. 5744 // The ASAN must be disabled for this function -- it may allocate `args` 5745 // not on the stack. 5746 args.rval().setInt32(int32_t(reinterpret_cast<size_t>(&args) & 0xfffffff)); 5747 return true; 5748 } 5749 5750 static bool Elapsed(JSContext* cx, unsigned argc, Value* vp) { 5751 CallArgs args = CallArgsFromVp(argc, vp); 5752 if (args.length() == 0) { 5753 double d = PRMJ_Now() - GetShellContext(cx)->startTime; 5754 args.rval().setDouble(d); 5755 return true; 5756 } 5757 JS_ReportErrorASCII(cx, "Wrong number of arguments"); 5758 return false; 5759 } 5760 5761 static ShellCompartmentPrivate* EnsureShellCompartmentPrivate(JSContext* cx) { 5762 Compartment* comp = cx->compartment(); 5763 auto priv = 5764 static_cast<ShellCompartmentPrivate*>(JS_GetCompartmentPrivate(comp)); 5765 if (!priv) { 5766 priv = cx->new_<ShellCompartmentPrivate>(); 5767 JS_SetCompartmentPrivate(cx->compartment(), priv); 5768 } 5769 return priv; 5770 } 5771 5772 static bool ParseModule(JSContext* cx, unsigned argc, Value* vp) { 5773 CallArgs args = CallArgsFromVp(argc, vp); 5774 if (!args.requireAtLeast(cx, "parseModule", 1)) { 5775 return false; 5776 } 5777 5778 UniqueChars filename; 5779 CompileOptions options(cx); 5780 JS::ModuleType moduleType = JS::ModuleType::JavaScript; 5781 if (args.length() > 1) { 5782 if (!args[1].isString()) { 5783 const char* typeName = InformalValueTypeName(args[1]); 5784 JS_ReportErrorASCII(cx, "expected filename string, got %s", typeName); 5785 return false; 5786 } 5787 5788 RootedString str(cx, args[1].toString()); 5789 filename = JS_EncodeStringToUTF8(cx, str); 5790 if (!filename) { 5791 return false; 5792 } 5793 5794 options.setFileAndLine(filename.get(), 1); 5795 5796 // The 2nd argument is the module type string. "js", "json" or "bytes" is 5797 // expected. 5798 if (args.length() == 3) { 5799 if (!args[2].isString()) { 5800 const char* typeName = InformalValueTypeName(args[2]); 5801 JS_ReportErrorASCII(cx, "expected moduleType string, got %s", typeName); 5802 return false; 5803 } 5804 5805 RootedString str(cx, args[2].toString()); 5806 JSLinearString* linearStr = JS_EnsureLinearString(cx, str); 5807 if (!linearStr) { 5808 return false; 5809 } 5810 if (JS_LinearStringEqualsLiteral(linearStr, "json")) { 5811 moduleType = JS::ModuleType::JSON; 5812 } else if (JS_LinearStringEqualsLiteral(linearStr, "bytes")) { 5813 moduleType = JS::ModuleType::Bytes; 5814 } else if (!JS_LinearStringEqualsLiteral(linearStr, "js")) { 5815 JS_ReportErrorASCII( 5816 cx, "moduleType string ('js' or 'json' or 'bytes') expected"); 5817 return false; 5818 } 5819 } 5820 } else { 5821 options.setFileAndLine("<string>", 1); 5822 } 5823 5824 RootedObject module(cx); 5825 switch (moduleType) { 5826 case JS::ModuleType::JavaScript: 5827 case JS::ModuleType::JSON: { 5828 if (!args[0].isString()) { 5829 const char* typeName = InformalValueTypeName(args[0]); 5830 JS_ReportErrorASCII(cx, "expected string to compile, got %s", typeName); 5831 return false; 5832 } 5833 5834 JSString* scriptContents = args[0].toString(); 5835 5836 AutoStableStringChars linearChars(cx); 5837 if (!linearChars.initTwoByte(cx, scriptContents)) { 5838 return false; 5839 } 5840 5841 JS::SourceText<char16_t> srcBuf; 5842 if (!srcBuf.initMaybeBorrowed(cx, linearChars)) { 5843 return false; 5844 } 5845 5846 if (moduleType == JS::ModuleType::JSON) { 5847 module = JS::CompileJsonModule(cx, options, srcBuf); 5848 } else { 5849 options.setModule(); 5850 module = JS::CompileModule(cx, options, srcBuf); 5851 } 5852 5853 break; 5854 } 5855 5856 case JS::ModuleType::Bytes: { 5857 if (!args[0].isObject() || 5858 !JS::IsArrayBufferObject(&args[0].toObject())) { 5859 const char* typeName = InformalValueTypeName(args[0]); 5860 JS_ReportErrorASCII(cx, "expected ArrayBuffer for bytes module, got %s", 5861 typeName); 5862 return false; 5863 } 5864 5865 /* 5866 * NOTE: The spec requires checking that the ArrayBuffer is immutable. 5867 * Immutable ArrayBuffers (see bug 1952253) are still only a Stage 2.7 5868 * proposal. This check will be added in a future update. 5869 */ 5870 module = JS::CreateDefaultExportSyntheticModule(cx, args[0]); 5871 break; 5872 } 5873 5874 case JS::ModuleType::CSS: 5875 case JS::ModuleType::Unknown: 5876 JS_ReportErrorASCII(cx, "Unsupported module type in parseModule"); 5877 return false; 5878 } 5879 5880 if (!module) { 5881 return false; 5882 } 5883 5884 Rooted<ShellModuleObjectWrapper*> wrapper( 5885 cx, ShellModuleObjectWrapper::create(cx, module.as<ModuleObject>(), 5886 moduleType)); 5887 if (!wrapper) { 5888 return false; 5889 } 5890 args.rval().setObject(*wrapper); 5891 return true; 5892 } 5893 5894 // A JSObject that holds XDRBuffer. 5895 class XDRBufferObject : public NativeObject { 5896 static const size_t VECTOR_SLOT = 0; 5897 static const unsigned RESERVED_SLOTS = 1; 5898 5899 public: 5900 static const JSClassOps classOps_; 5901 static const JSClass class_; 5902 5903 [[nodiscard]] inline static XDRBufferObject* create( 5904 JSContext* cx, JS::TranscodeBuffer&& buf); 5905 5906 JS::TranscodeBuffer* data() const { 5907 Value value = getReservedSlot(VECTOR_SLOT); 5908 auto buf = static_cast<JS::TranscodeBuffer*>(value.toPrivate()); 5909 MOZ_ASSERT(buf); 5910 return buf; 5911 } 5912 5913 bool hasData() const { 5914 // Data may not be present if we hit OOM in initialization. 5915 return !getReservedSlot(VECTOR_SLOT).isUndefined(); 5916 } 5917 5918 static void finalize(JS::GCContext* gcx, JSObject* obj); 5919 }; 5920 5921 /*static */ const JSClassOps XDRBufferObject::classOps_ = { 5922 nullptr, // addProperty 5923 nullptr, // delProperty 5924 nullptr, // enumerate 5925 nullptr, // newEnumerate 5926 nullptr, // resolve 5927 nullptr, // mayResolve 5928 XDRBufferObject::finalize, // finalize 5929 nullptr, // call 5930 nullptr, // construct 5931 nullptr, // trace 5932 }; 5933 5934 /*static */ const JSClass XDRBufferObject::class_ = { 5935 "XDRBufferObject", 5936 JSCLASS_HAS_RESERVED_SLOTS(XDRBufferObject::RESERVED_SLOTS) | 5937 JSCLASS_BACKGROUND_FINALIZE, 5938 &XDRBufferObject::classOps_, 5939 }; 5940 5941 XDRBufferObject* XDRBufferObject::create(JSContext* cx, 5942 JS::TranscodeBuffer&& buf) { 5943 XDRBufferObject* bufObj = 5944 NewObjectWithGivenProto<XDRBufferObject>(cx, nullptr); 5945 if (!bufObj) { 5946 return nullptr; 5947 } 5948 5949 auto heapBuf = cx->make_unique<JS::TranscodeBuffer>(std::move(buf)); 5950 if (!heapBuf) { 5951 return nullptr; 5952 } 5953 5954 size_t len = heapBuf->length(); 5955 InitReservedSlot(bufObj, VECTOR_SLOT, heapBuf.release(), len, 5956 MemoryUse::XDRBufferElements); 5957 5958 return bufObj; 5959 } 5960 5961 void XDRBufferObject::finalize(JS::GCContext* gcx, JSObject* obj) { 5962 XDRBufferObject* buf = &obj->as<XDRBufferObject>(); 5963 if (buf->hasData()) { 5964 gcx->delete_(buf, buf->data(), buf->data()->length(), 5965 MemoryUse::XDRBufferElements); 5966 } 5967 } 5968 5969 static bool InstantiateModuleStencil(JSContext* cx, uint32_t argc, Value* vp) { 5970 CallArgs args = CallArgsFromVp(argc, vp); 5971 5972 if (!args.requireAtLeast(cx, "instantiateModuleStencil", 1)) { 5973 return false; 5974 } 5975 5976 /* Prepare the input byte array. */ 5977 if (!args[0].isObject()) { 5978 JS_ReportErrorASCII(cx, 5979 "instantiateModuleStencil: Stencil object expected"); 5980 return false; 5981 } 5982 Rooted<js::StencilObject*> stencilObj( 5983 cx, args[0].toObject().maybeUnwrapIf<js::StencilObject>()); 5984 if (!stencilObj) { 5985 JS_ReportErrorASCII(cx, 5986 "instantiateModuleStencil: Stencil object expected"); 5987 return false; 5988 } 5989 5990 if (!stencilObj->stencil()->getInitial()->isModule()) { 5991 JS_ReportErrorASCII(cx, 5992 "instantiateModuleStencil: Module stencil expected"); 5993 return false; 5994 } 5995 5996 CompileOptions options(cx); 5997 UniqueChars fileNameBytes; 5998 if (args.length() == 2) { 5999 if (!args[1].isObject()) { 6000 JS_ReportErrorASCII( 6001 cx, "instantiateModuleStencil: The 2nd argument must be an object"); 6002 return false; 6003 } 6004 6005 RootedObject opts(cx, &args[1].toObject()); 6006 if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) { 6007 return false; 6008 } 6009 } 6010 6011 if (!js::ValidateLazinessOfStencilAndGlobal(cx, stencilObj->stencil())) { 6012 return false; 6013 } 6014 6015 JS::InstantiateOptions instantiateOptions(options); 6016 Rooted<JSObject*> modObject( 6017 cx, JS::InstantiateModuleStencil(cx, instantiateOptions, 6018 stencilObj->stencil(), nullptr)); 6019 if (!modObject) { 6020 return false; 6021 } 6022 6023 Rooted<ModuleObject*> module(cx, &modObject->as<ModuleObject>()); 6024 Rooted<ShellModuleObjectWrapper*> wrapper( 6025 cx, ShellModuleObjectWrapper::create(cx, module)); 6026 if (!wrapper) { 6027 return false; 6028 } 6029 args.rval().setObject(*wrapper); 6030 return true; 6031 } 6032 6033 static bool InstantiateModuleStencilXDR(JSContext* cx, uint32_t argc, 6034 Value* vp) { 6035 CallArgs args = CallArgsFromVp(argc, vp); 6036 6037 if (!args.requireAtLeast(cx, "instantiateModuleStencilXDR", 1)) { 6038 return false; 6039 } 6040 6041 /* Prepare the input byte array. */ 6042 if (!args[0].isObject()) { 6043 JS_ReportErrorASCII( 6044 cx, "instantiateModuleStencilXDR: Stencil XDR object expected"); 6045 return false; 6046 } 6047 Rooted<StencilXDRBufferObject*> xdrObj( 6048 cx, args[0].toObject().maybeUnwrapIf<StencilXDRBufferObject>()); 6049 if (!xdrObj) { 6050 JS_ReportErrorASCII( 6051 cx, "instantiateModuleStencilXDR: Stencil XDR object expected"); 6052 return false; 6053 } 6054 MOZ_ASSERT(xdrObj->hasBuffer()); 6055 6056 CompileOptions options(cx); 6057 UniqueChars fileNameBytes; 6058 if (args.length() == 2) { 6059 if (!args[1].isObject()) { 6060 JS_ReportErrorASCII( 6061 cx, 6062 "instantiateModuleStencilXDR: The 2nd argument must be an object"); 6063 return false; 6064 } 6065 6066 RootedObject opts(cx, &args[1].toObject()); 6067 if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) { 6068 return false; 6069 } 6070 } 6071 6072 JS::TranscodeRange xdrRange(xdrObj->buffer(), xdrObj->bufferLength()); 6073 JS::DecodeOptions decodeOptions(options); 6074 RefPtr<JS::Stencil> stencil; 6075 auto result = 6076 JS::DecodeStencil(cx, decodeOptions, xdrRange, getter_AddRefs(stencil)); 6077 if (result == JS::TranscodeResult::Throw) { 6078 return false; 6079 } 6080 if (JS::IsTranscodeFailureResult(result)) { 6081 JS_ReportErrorASCII(cx, "Decoding failure"); 6082 return false; 6083 } 6084 6085 if (!stencil->getInitial()->isModule()) { 6086 JS_ReportErrorASCII(cx, 6087 "instantiateModuleStencilXDR: Module stencil expected"); 6088 return false; 6089 } 6090 6091 if (!js::ValidateLazinessOfStencilAndGlobal(cx, stencil.get())) { 6092 return false; 6093 } 6094 6095 JS::InstantiateOptions instantiateOptions(options); 6096 Rooted<JSObject*> modObject( 6097 cx, 6098 JS::InstantiateModuleStencil(cx, instantiateOptions, stencil, nullptr)); 6099 if (!modObject) { 6100 return false; 6101 } 6102 6103 Rooted<ModuleObject*> module(cx, &modObject->as<ModuleObject>()); 6104 Rooted<ShellModuleObjectWrapper*> wrapper( 6105 cx, ShellModuleObjectWrapper::create(cx, module)); 6106 if (!wrapper) { 6107 return false; 6108 } 6109 args.rval().setObject(*wrapper); 6110 return true; 6111 } 6112 6113 static bool RegisterModule(JSContext* cx, unsigned argc, Value* vp) { 6114 CallArgs args = CallArgsFromVp(argc, vp); 6115 if (!args.requireAtLeast(cx, "registerModule", 2)) { 6116 return false; 6117 } 6118 6119 if (!args[0].isString()) { 6120 const char* typeName = InformalValueTypeName(args[0]); 6121 JS_ReportErrorASCII(cx, "Expected string, got %s", typeName); 6122 return false; 6123 } 6124 6125 if (!args[1].isObject() || 6126 !args[1].toObject().is<ShellModuleObjectWrapper>()) { 6127 const char* typeName = InformalValueTypeName(args[1]); 6128 JS_ReportErrorASCII(cx, "Expected module, got %s", typeName); 6129 return false; 6130 } 6131 6132 ShellContext* sc = GetShellContext(cx); 6133 Rooted<ModuleObject*> module( 6134 cx, args[1].toObject().as<ShellModuleObjectWrapper>().get()); 6135 if (module->realm() != cx->realm()) { 6136 JS_ReportErrorASCII(cx, "Module is in a different realm"); 6137 return false; 6138 } 6139 6140 Rooted<JSAtom*> specifier(cx, AtomizeString(cx, args[0].toString())); 6141 if (!specifier) { 6142 return false; 6143 } 6144 6145 JS::ModuleType moduleType = 6146 args[1].toObject().as<ShellModuleObjectWrapper>().getModuleType(); 6147 6148 RootedObject moduleRequest( 6149 cx, ModuleRequestObject::create(cx, specifier, moduleType)); 6150 if (!moduleRequest) { 6151 return false; 6152 } 6153 6154 if (!sc->moduleLoader->registerTestModule(cx, moduleRequest, module)) { 6155 return false; 6156 } 6157 6158 Rooted<ShellModuleObjectWrapper*> wrapper( 6159 cx, ShellModuleObjectWrapper::create(cx, module, moduleType)); 6160 if (!wrapper) { 6161 return false; 6162 } 6163 args.rval().setObject(*wrapper); 6164 return true; 6165 } 6166 6167 static bool ClearModules(JSContext* cx, unsigned argc, Value* vp) { 6168 CallArgs args = CallArgsFromVp(argc, vp); 6169 ShellContext* sc = GetShellContext(cx); 6170 sc->moduleLoader->clearModules(cx); 6171 args.rval().setUndefined(); 6172 return true; 6173 } 6174 6175 static bool ModuleLoadResolved(JSContext* cx, HandleValue hostDefined) { 6176 RootedObject module(cx, &hostDefined.toObject()); 6177 return JS::ModuleLink(cx, module); 6178 } 6179 6180 static bool ModuleLoadRejected(JSContext* cx, HandleValue hostDefined, 6181 HandleValue error) { 6182 JS_SetPendingException(cx, error); 6183 return false; 6184 } 6185 6186 static bool ModuleLink(JSContext* cx, unsigned argc, Value* vp) { 6187 CallArgs args = CallArgsFromVp(argc, vp); 6188 6189 if (args.length() != 1 || !args[0].isObject()) { 6190 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INVALID_ARGS, 6191 "moduleLink"); 6192 return false; 6193 } 6194 6195 RootedObject object(cx, UncheckedUnwrap(&args[0].toObject())); 6196 if (!object->is<ShellModuleObjectWrapper>()) { 6197 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INVALID_ARGS, 6198 "moduleLink"); 6199 return false; 6200 } 6201 6202 AutoRealm ar(cx, object); 6203 6204 Rooted<ModuleObject*> module(cx, 6205 object->as<ShellModuleObjectWrapper>().get()); 6206 6207 // TODO: Bug 1968904: Update ModuleLink 6208 RootedValue hostDefined(cx, ObjectValue(*module)); 6209 if (!JS::LoadRequestedModules(cx, module, hostDefined, ModuleLoadResolved, 6210 ModuleLoadRejected)) { 6211 return false; 6212 } 6213 6214 args.rval().setUndefined(); 6215 return true; 6216 } 6217 6218 static bool ModuleEvaluate(JSContext* cx, unsigned argc, Value* vp) { 6219 CallArgs args = CallArgsFromVp(argc, vp); 6220 6221 if (args.length() != 1 || !args[0].isObject()) { 6222 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INVALID_ARGS, 6223 "moduleEvaluate"); 6224 return false; 6225 } 6226 6227 RootedObject object(cx, UncheckedUnwrap(&args[0].toObject())); 6228 if (!object->is<ShellModuleObjectWrapper>()) { 6229 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INVALID_ARGS, 6230 "moduleEvaluate"); 6231 return false; 6232 } 6233 6234 { 6235 AutoRealm ar(cx, object); 6236 6237 Rooted<ModuleObject*> module(cx, 6238 object->as<ShellModuleObjectWrapper>().get()); 6239 if (!JS::ModuleEvaluate(cx, module, args.rval())) { 6240 return false; 6241 } 6242 } 6243 6244 return JS_WrapValue(cx, args.rval()); 6245 } 6246 6247 static ModuleEnvironmentObject* GetModuleInitialEnvironment( 6248 JSContext* cx, Handle<ModuleObject*> module) { 6249 // Use the initial environment so that tests can check bindings exists 6250 // before they have been instantiated. 6251 Rooted<ModuleEnvironmentObject*> env(cx, &module->initialEnvironment()); 6252 MOZ_ASSERT(env); 6253 return env; 6254 } 6255 6256 static bool GetModuleEnvironmentNames(JSContext* cx, unsigned argc, Value* vp) { 6257 CallArgs args = CallArgsFromVp(argc, vp); 6258 if (args.length() != 1) { 6259 JS_ReportErrorASCII(cx, "Wrong number of arguments"); 6260 return false; 6261 } 6262 6263 if (!args[0].isObject() || 6264 !args[0].toObject().is<ShellModuleObjectWrapper>()) { 6265 JS_ReportErrorASCII(cx, 6266 "First argument should be a ShellModuleObjectWrapper"); 6267 return false; 6268 } 6269 6270 Rooted<ModuleObject*> module( 6271 cx, args[0].toObject().as<ShellModuleObjectWrapper>().get()); 6272 if (module->hasSyntheticModuleFields()) { 6273 JS_ReportErrorASCII(cx, 6274 "Operation is not supported on JSON module objects."); 6275 return false; 6276 } 6277 6278 if (module->hadEvaluationError()) { 6279 JS_ReportErrorASCII(cx, "Module environment unavailable"); 6280 return false; 6281 } 6282 6283 Rooted<ModuleEnvironmentObject*> env(cx, 6284 GetModuleInitialEnvironment(cx, module)); 6285 Rooted<IdVector> ids(cx, IdVector(cx)); 6286 if (!JS_Enumerate(cx, env, &ids)) { 6287 return false; 6288 } 6289 6290 // The "*namespace*" binding is a detail of current implementation so hide 6291 // it to give stable results in tests. 6292 ids.eraseIfEqual(NameToId(cx->names().star_namespace_star_)); 6293 6294 uint32_t length = ids.length(); 6295 Rooted<ArrayObject*> array(cx, NewDenseFullyAllocatedArray(cx, length)); 6296 if (!array) { 6297 return false; 6298 } 6299 6300 array->setDenseInitializedLength(length); 6301 for (uint32_t i = 0; i < length; i++) { 6302 array->initDenseElement(i, StringValue(ids[i].toString())); 6303 } 6304 6305 args.rval().setObject(*array); 6306 return true; 6307 } 6308 6309 static bool GetModuleEnvironmentValue(JSContext* cx, unsigned argc, Value* vp) { 6310 CallArgs args = CallArgsFromVp(argc, vp); 6311 if (args.length() != 2) { 6312 JS_ReportErrorASCII(cx, "Wrong number of arguments"); 6313 return false; 6314 } 6315 6316 if (!args[0].isObject() || 6317 !args[0].toObject().is<ShellModuleObjectWrapper>()) { 6318 JS_ReportErrorASCII(cx, 6319 "First argument should be a ShellModuleObjectWrapper"); 6320 return false; 6321 } 6322 6323 if (!args[1].isString()) { 6324 JS_ReportErrorASCII(cx, "Second argument should be a string"); 6325 return false; 6326 } 6327 6328 Rooted<ModuleObject*> module( 6329 cx, args[0].toObject().as<ShellModuleObjectWrapper>().get()); 6330 if (module->hasSyntheticModuleFields()) { 6331 JS_ReportErrorASCII(cx, 6332 "Operation is not supported on JSON module objects."); 6333 return false; 6334 } 6335 6336 if (module->hadEvaluationError()) { 6337 JS_ReportErrorASCII(cx, "Module environment unavailable"); 6338 return false; 6339 } 6340 6341 Rooted<ModuleEnvironmentObject*> env(cx, 6342 GetModuleInitialEnvironment(cx, module)); 6343 RootedString name(cx, args[1].toString()); 6344 RootedId id(cx); 6345 if (!JS_StringToId(cx, name, &id)) { 6346 return false; 6347 } 6348 6349 if (!GetProperty(cx, env, env, id, args.rval())) { 6350 return false; 6351 } 6352 6353 if (args.rval().isMagic(JS_UNINITIALIZED_LEXICAL)) { 6354 ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, id); 6355 return false; 6356 } 6357 6358 return true; 6359 } 6360 6361 enum class DumpType { 6362 ParseNode, 6363 Stencil, 6364 }; 6365 6366 template <typename Unit> 6367 static bool DumpAST(JSContext* cx, const JS::ReadOnlyCompileOptions& options, 6368 const Unit* units, size_t length, 6369 js::frontend::CompilationState& compilationState, 6370 js::frontend::ParseGoal goal) { 6371 using namespace js::frontend; 6372 6373 AutoReportFrontendContext fc(cx); 6374 Parser<FullParseHandler, Unit> parser(&fc, options, units, length, 6375 compilationState, 6376 /* syntaxParser = */ nullptr); 6377 if (!parser.checkOptions()) { 6378 return false; 6379 } 6380 6381 // Emplace the top-level stencil. 6382 MOZ_ASSERT(compilationState.scriptData.length() == 6383 CompilationStencil::TopLevelIndex); 6384 if (!compilationState.appendScriptStencilAndData(&fc)) { 6385 return false; 6386 } 6387 6388 js::frontend::ParseNode* pn; 6389 if (goal == frontend::ParseGoal::Script) { 6390 pn = parser.parse().unwrapOr(nullptr); 6391 } else { 6392 ModuleBuilder builder(&fc, &parser); 6393 6394 SourceExtent extent = SourceExtent::makeGlobalExtent(length); 6395 ModuleSharedContext modulesc(&fc, options, builder, extent); 6396 pn = parser.moduleBody(&modulesc).unwrapOr(nullptr); 6397 } 6398 6399 if (!pn) { 6400 return false; 6401 } 6402 6403 #if defined(DEBUG) 6404 js::Fprinter out(stderr); 6405 DumpParseTree(&parser, pn, out); 6406 #endif 6407 6408 return true; 6409 } 6410 6411 template <typename Unit> 6412 [[nodiscard]] static bool DumpStencil(JSContext* cx, 6413 const JS::ReadOnlyCompileOptions& options, 6414 const Unit* units, size_t length, 6415 js::frontend::ParseGoal goal) { 6416 JS::SourceText<Unit> srcBuf; 6417 if (!srcBuf.init(cx, units, length, JS::SourceOwnership::Borrowed)) { 6418 return false; 6419 } 6420 6421 RefPtr<JS::Stencil> stencil; 6422 if (goal == frontend::ParseGoal::Script) { 6423 stencil = JS::CompileGlobalScriptToStencil(cx, options, srcBuf); 6424 } else { 6425 stencil = JS::CompileModuleScriptToStencil(cx, options, srcBuf); 6426 } 6427 6428 if (!stencil) { 6429 return false; 6430 } 6431 6432 #if defined(DEBUG) || defined(JS_JITSPEW) 6433 stencil->dump(); 6434 #endif 6435 6436 return true; 6437 } 6438 6439 static bool FrontendTest(JSContext* cx, unsigned argc, Value* vp, 6440 const char* funcName, DumpType dumpType) { 6441 using namespace js::frontend; 6442 6443 CallArgs args = CallArgsFromVp(argc, vp); 6444 6445 if (!args.requireAtLeast(cx, funcName, 1)) { 6446 return false; 6447 } 6448 if (!args[0].isString()) { 6449 const char* typeName = InformalValueTypeName(args[0]); 6450 JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName); 6451 return false; 6452 } 6453 6454 frontend::ParseGoal goal = frontend::ParseGoal::Script; 6455 6456 CompileOptions options(cx); 6457 options.setIntroductionType("js shell parse") 6458 .setFileAndLine("<string>", 1) 6459 .setIsRunOnce(true) 6460 .setNoScriptRval(true); 6461 6462 if (args.length() >= 2) { 6463 if (!args[1].isObject()) { 6464 JS_ReportErrorASCII(cx, "The 2nd argument must be an object"); 6465 return false; 6466 } 6467 6468 RootedObject objOptions(cx, &args[1].toObject()); 6469 6470 RootedValue optionModule(cx); 6471 if (!JS_GetProperty(cx, objOptions, "module", &optionModule)) { 6472 return false; 6473 } 6474 6475 if (optionModule.isBoolean()) { 6476 if (optionModule.toBoolean()) { 6477 goal = frontend::ParseGoal::Module; 6478 } 6479 } else if (!optionModule.isUndefined()) { 6480 const char* typeName = InformalValueTypeName(optionModule); 6481 JS_ReportErrorASCII(cx, "option `module` should be a boolean, got %s", 6482 typeName); 6483 return false; 6484 } 6485 if (!js::ParseCompileOptions(cx, options, objOptions, nullptr)) { 6486 return false; 6487 } 6488 6489 if (goal == frontend::ParseGoal::Module && options.lineno == 0) { 6490 JS_ReportErrorASCII(cx, "Module cannot be compiled with lineNumber == 0"); 6491 return false; 6492 } 6493 } 6494 6495 JSString* scriptContents = args[0].toString(); 6496 Rooted<JSLinearString*> linearString(cx, scriptContents->ensureLinear(cx)); 6497 if (!linearString) { 6498 return false; 6499 } 6500 6501 bool isAscii = false; 6502 if (linearString->hasLatin1Chars()) { 6503 JS::AutoCheckCannotGC nogc; 6504 isAscii = JS::StringIsASCII(mozilla::Span( 6505 reinterpret_cast<const char*>(linearString->latin1Chars(nogc)), 6506 linearString->length())); 6507 } 6508 6509 AutoStableStringChars stableChars(cx); 6510 if (isAscii) { 6511 if (!stableChars.init(cx, scriptContents)) { 6512 return false; 6513 } 6514 MOZ_ASSERT(stableChars.isLatin1()); 6515 } else { 6516 if (!stableChars.initTwoByte(cx, scriptContents)) { 6517 return false; 6518 } 6519 } 6520 6521 size_t length = scriptContents->length(); 6522 6523 if (goal == frontend::ParseGoal::Module) { 6524 // See frontend::CompileModule. 6525 options.setForceStrictMode(); 6526 options.allowHTMLComments = false; 6527 } 6528 6529 if (dumpType == DumpType::Stencil) { 6530 if (isAscii) { 6531 const Latin1Char* latin1 = stableChars.latin1Range().begin().get(); 6532 auto utf8 = reinterpret_cast<const mozilla::Utf8Unit*>(latin1); 6533 if (!DumpStencil<mozilla::Utf8Unit>(cx, options, utf8, length, goal)) { 6534 return false; 6535 } 6536 } else { 6537 MOZ_ASSERT(stableChars.isTwoByte()); 6538 const char16_t* chars = stableChars.twoByteRange().begin().get(); 6539 if (!DumpStencil<char16_t>(cx, options, chars, length, goal)) { 6540 return false; 6541 } 6542 } 6543 6544 args.rval().setUndefined(); 6545 return true; 6546 } 6547 6548 AutoReportFrontendContext fc(cx); 6549 Rooted<frontend::CompilationInput> input(cx, 6550 frontend::CompilationInput(options)); 6551 if (goal == frontend::ParseGoal::Script) { 6552 if (!input.get().initForGlobal(&fc)) { 6553 return false; 6554 } 6555 } else { 6556 if (!input.get().initForModule(&fc)) { 6557 return false; 6558 } 6559 } 6560 6561 LifoAllocScope allocScope(&cx->tempLifoAlloc()); 6562 frontend::NoScopeBindingCache scopeCache; 6563 frontend::CompilationState compilationState(&fc, allocScope, input.get()); 6564 if (!compilationState.init(&fc, &scopeCache)) { 6565 return false; 6566 } 6567 6568 if (isAscii) { 6569 const Latin1Char* latin1 = stableChars.latin1Range().begin().get(); 6570 auto utf8 = reinterpret_cast<const mozilla::Utf8Unit*>(latin1); 6571 if (!DumpAST<mozilla::Utf8Unit>(cx, options, utf8, length, compilationState, 6572 goal)) { 6573 return false; 6574 } 6575 } else { 6576 MOZ_ASSERT(stableChars.isTwoByte()); 6577 const char16_t* chars = stableChars.twoByteRange().begin().get(); 6578 if (!DumpAST<char16_t>(cx, options, chars, length, compilationState, 6579 goal)) { 6580 return false; 6581 } 6582 } 6583 args.rval().setUndefined(); 6584 return true; 6585 } 6586 6587 static bool DumpStencil(JSContext* cx, unsigned argc, Value* vp) { 6588 return FrontendTest(cx, argc, vp, "dumpStencil", DumpType::Stencil); 6589 } 6590 6591 static bool Parse(JSContext* cx, unsigned argc, Value* vp) { 6592 // Parse returns local scope information with variables ordered 6593 // differently, depending on the underlying JIT implementation. 6594 if (js::SupportDifferentialTesting()) { 6595 JS_ReportErrorASCII(cx, 6596 "Function not available in differential testing mode."); 6597 return false; 6598 } 6599 6600 return FrontendTest(cx, argc, vp, "parse", DumpType::ParseNode); 6601 } 6602 6603 static bool SyntaxParse(JSContext* cx, unsigned argc, Value* vp) { 6604 using namespace js::frontend; 6605 6606 CallArgs args = CallArgsFromVp(argc, vp); 6607 6608 if (!args.requireAtLeast(cx, "syntaxParse", 1)) { 6609 return false; 6610 } 6611 if (!args[0].isString()) { 6612 const char* typeName = InformalValueTypeName(args[0]); 6613 JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName); 6614 return false; 6615 } 6616 6617 JSString* scriptContents = args[0].toString(); 6618 6619 CompileOptions options(cx); 6620 options.setIntroductionType("js shell syntaxParse") 6621 .setFileAndLine("<string>", 1); 6622 6623 AutoStableStringChars stableChars(cx); 6624 if (!stableChars.initTwoByte(cx, scriptContents)) { 6625 return false; 6626 } 6627 6628 const char16_t* chars = stableChars.twoByteRange().begin().get(); 6629 size_t length = scriptContents->length(); 6630 6631 AutoReportFrontendContext fc(cx); 6632 Rooted<frontend::CompilationInput> input(cx, 6633 frontend::CompilationInput(options)); 6634 if (!input.get().initForGlobal(&fc)) { 6635 return false; 6636 } 6637 6638 LifoAllocScope allocScope(&cx->tempLifoAlloc()); 6639 frontend::NoScopeBindingCache scopeCache; 6640 frontend::CompilationState compilationState(&fc, allocScope, input.get()); 6641 if (!compilationState.init(&fc, &scopeCache)) { 6642 return false; 6643 } 6644 6645 Parser<frontend::SyntaxParseHandler, char16_t> parser( 6646 &fc, options, chars, length, compilationState, 6647 /* syntaxParser = */ nullptr); 6648 if (!parser.checkOptions()) { 6649 return false; 6650 } 6651 6652 bool succeeded = parser.parse().isOk(); 6653 if (fc.hadErrors()) { 6654 return false; 6655 } 6656 6657 if (!succeeded && !parser.hadAbortedSyntaxParse()) { 6658 // If no exception is posted, either there was an OOM or a language 6659 // feature unhandled by the syntax parser was encountered. 6660 MOZ_ASSERT(fc.hadOutOfMemory()); 6661 return false; 6662 } 6663 6664 args.rval().setBoolean(succeeded); 6665 return true; 6666 } 6667 6668 static bool OffThreadCompileToStencil(JSContext* cx, unsigned argc, Value* vp) { 6669 if (!CanUseExtraThreads()) { 6670 JS_ReportErrorASCII( 6671 cx, "Can't use offThreadCompileToStencil with --no-threads"); 6672 return false; 6673 } 6674 6675 CallArgs args = CallArgsFromVp(argc, vp); 6676 6677 if (!args.requireAtLeast(cx, "offThreadCompileToStencil", 1)) { 6678 return false; 6679 } 6680 if (!args[0].isString()) { 6681 const char* typeName = InformalValueTypeName(args[0]); 6682 JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName); 6683 return false; 6684 } 6685 6686 UniqueChars fileNameBytes; 6687 CompileOptions options(cx); 6688 options.setIntroductionType("js shell offThreadCompileToStencil") 6689 .setFileAndLine("<string>", 1); 6690 6691 if (args.length() >= 2) { 6692 if (!args[1].isObject()) { 6693 JS_ReportErrorASCII( 6694 cx, "offThreadCompileToStencil: The 2nd argument must be an object"); 6695 return false; 6696 } 6697 6698 // Offthread compilation requires that the debug metadata be set when the 6699 // script is collected from offthread, rather than when compiled. 6700 RootedObject opts(cx, &args[1].toObject()); 6701 if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) { 6702 return false; 6703 } 6704 } 6705 6706 // This option setting must override whatever the caller requested. 6707 options.setIsRunOnce(true); 6708 6709 JSString* scriptContents = args[0].toString(); 6710 AutoStableStringChars stableChars(cx); 6711 if (!stableChars.initTwoByte(cx, scriptContents)) { 6712 return false; 6713 } 6714 6715 size_t length = scriptContents->length(); 6716 const char16_t* chars = stableChars.twoByteChars(); 6717 6718 // Make sure we own the string's chars, so that they are not freed before 6719 // the compilation is finished. 6720 UniqueTwoByteChars ownedChars; 6721 if (stableChars.maybeGiveOwnershipToCaller()) { 6722 ownedChars.reset(const_cast<char16_t*>(chars)); 6723 } else { 6724 ownedChars.reset(cx->pod_malloc<char16_t>(length)); 6725 if (!ownedChars) { 6726 return false; 6727 } 6728 6729 mozilla::PodCopy(ownedChars.get(), chars, length); 6730 } 6731 6732 if (!js::CanUseExtraThreads()) { 6733 JS_ReportErrorASCII(cx, "cannot compile code on helper thread"); 6734 return false; 6735 } 6736 6737 JS::SourceText<char16_t> srcBuf; 6738 if (!srcBuf.init(cx, std::move(ownedChars), length)) { 6739 return false; 6740 } 6741 6742 OffThreadJob* job = NewOffThreadJob(cx, OffThreadJob::Kind::CompileScript, 6743 options, std::move(srcBuf)); 6744 if (!job) { 6745 return false; 6746 } 6747 6748 if (!job->dispatch()) { 6749 ReportOutOfMemory(cx); 6750 DeleteOffThreadJob(cx, job); 6751 return false; 6752 } 6753 6754 args.rval().setInt32(job->id); 6755 return true; 6756 } 6757 6758 static bool FinishOffThreadStencil(JSContext* cx, unsigned argc, Value* vp) { 6759 CallArgs args = CallArgsFromVp(argc, vp); 6760 6761 OffThreadJob* job = LookupOffThreadJobForArgs(cx, args, 0); 6762 if (!job) { 6763 return false; 6764 } 6765 6766 job->waitUntilDone(); 6767 6768 RefPtr<JS::Stencil> stencil = job->stealStencil(cx); 6769 DeleteOffThreadJob(cx, job); 6770 if (!stencil) { 6771 return false; 6772 } 6773 RootedObject stencilObj(cx, 6774 js::StencilObject::create(cx, std::move(stencil))); 6775 if (!stencilObj) { 6776 return false; 6777 } 6778 6779 args.rval().setObject(*stencilObj); 6780 return true; 6781 } 6782 6783 static bool OffThreadCompileModuleToStencil(JSContext* cx, unsigned argc, 6784 Value* vp) { 6785 CallArgs args = CallArgsFromVp(argc, vp); 6786 6787 if (!args.requireAtLeast(cx, "offThreadCompileModuleToStencil", 1)) { 6788 return false; 6789 } 6790 if (!args[0].isString()) { 6791 const char* typeName = InformalValueTypeName(args[0]); 6792 JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName); 6793 return false; 6794 } 6795 6796 UniqueChars fileNameBytes; 6797 CompileOptions options(cx); 6798 options.setIntroductionType("js shell offThreadCompileModuleToStencil") 6799 .setFileAndLine("<string>", 1); 6800 6801 if (args.length() >= 2) { 6802 if (!args[1].isObject()) { 6803 JS_ReportErrorASCII(cx, 6804 "offThreadCompileModuleToStencil: The 2nd argument " 6805 "must be an object"); 6806 return false; 6807 } 6808 6809 // Offthread compilation requires that the debug metadata be set when the 6810 // script is collected from offthread, rather than when compiled. 6811 RootedObject opts(cx, &args[1].toObject()); 6812 if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) { 6813 return false; 6814 } 6815 6816 if (!ValidateModuleCompileOptions(cx, options)) { 6817 return false; 6818 } 6819 } 6820 6821 options.setIsRunOnce(true).setSourceIsLazy(false); 6822 6823 JSString* scriptContents = args[0].toString(); 6824 AutoStableStringChars stableChars(cx); 6825 if (!stableChars.initTwoByte(cx, scriptContents)) { 6826 return false; 6827 } 6828 6829 size_t length = scriptContents->length(); 6830 const char16_t* chars = stableChars.twoByteChars(); 6831 6832 // Make sure we own the string's chars, so that they are not freed before 6833 // the compilation is finished. 6834 UniqueTwoByteChars ownedChars; 6835 if (stableChars.maybeGiveOwnershipToCaller()) { 6836 ownedChars.reset(const_cast<char16_t*>(chars)); 6837 } else { 6838 ownedChars.reset(cx->pod_malloc<char16_t>(length)); 6839 if (!ownedChars) { 6840 return false; 6841 } 6842 6843 mozilla::PodCopy(ownedChars.get(), chars, length); 6844 } 6845 6846 if (!js::CanUseExtraThreads()) { 6847 JS_ReportErrorASCII(cx, "cannot compile code on worker thread"); 6848 return false; 6849 } 6850 6851 JS::SourceText<char16_t> srcBuf; 6852 if (!srcBuf.init(cx, std::move(ownedChars), length)) { 6853 return false; 6854 } 6855 6856 OffThreadJob* job = NewOffThreadJob(cx, OffThreadJob::Kind::CompileModule, 6857 options, std::move(srcBuf)); 6858 if (!job) { 6859 return false; 6860 } 6861 6862 if (!job->dispatch()) { 6863 ReportOutOfMemory(cx); 6864 DeleteOffThreadJob(cx, job); 6865 return false; 6866 } 6867 6868 args.rval().setInt32(job->id); 6869 return true; 6870 } 6871 6872 static bool OffThreadDecodeStencil(JSContext* cx, unsigned argc, Value* vp) { 6873 if (!CanUseExtraThreads()) { 6874 JS_ReportErrorASCII(cx, 6875 "Can't use offThreadDecodeStencil with --no-threads"); 6876 return false; 6877 } 6878 6879 CallArgs args = CallArgsFromVp(argc, vp); 6880 6881 if (!args.requireAtLeast(cx, "offThreadDecodeStencil", 1)) { 6882 return false; 6883 } 6884 if (!args[0].isObject() || !CacheEntry_isCacheEntry(&args[0].toObject())) { 6885 const char* typeName = InformalValueTypeName(args[0]); 6886 JS_ReportErrorASCII(cx, "expected cache entry, got %s", typeName); 6887 return false; 6888 } 6889 RootedObject cacheEntry(cx, &args[0].toObject()); 6890 6891 UniqueChars fileNameBytes; 6892 CompileOptions options(cx); 6893 options.setIntroductionType("js shell offThreadDecodeStencil") 6894 .setFileAndLine("<string>", 1); 6895 6896 if (args.length() >= 2) { 6897 if (!args[1].isObject()) { 6898 JS_ReportErrorASCII( 6899 cx, "offThreadDecodeStencil: The 2nd argument must be an object"); 6900 return false; 6901 } 6902 6903 RootedObject opts(cx, &args[1].toObject()); 6904 if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) { 6905 return false; 6906 } 6907 } 6908 6909 // This option setting must override whatever the caller requested, and 6910 // this should match `Evaluate` that encodes the script. 6911 options.setIsRunOnce(false); 6912 6913 JS::TranscodeBuffer loadBuffer; 6914 size_t loadLength = 0; 6915 uint8_t* loadData = nullptr; 6916 loadData = CacheEntry_getBytecode(cx, cacheEntry, &loadLength); 6917 if (!loadData) { 6918 return false; 6919 } 6920 if (!loadBuffer.append(loadData, loadLength)) { 6921 JS_ReportOutOfMemory(cx); 6922 return false; 6923 } 6924 6925 if (!js::CanUseExtraThreads()) { 6926 JS_ReportErrorASCII(cx, "cannot compile code on worker thread"); 6927 return false; 6928 } 6929 6930 OffThreadJob* job = NewOffThreadJob(cx, OffThreadJob::Kind::Decode, options, 6931 std::move(loadBuffer)); 6932 if (!job) { 6933 return false; 6934 } 6935 6936 if (!job->dispatch()) { 6937 ReportOutOfMemory(cx); 6938 DeleteOffThreadJob(cx, job); 6939 return false; 6940 } 6941 6942 args.rval().setInt32(job->id); 6943 return true; 6944 } 6945 6946 class AutoCStringVector { 6947 Vector<char*> argv_; 6948 6949 public: 6950 explicit AutoCStringVector(JSContext* cx) : argv_(cx) {} 6951 ~AutoCStringVector() { 6952 for (size_t i = 0; i < argv_.length(); i++) { 6953 js_free(argv_[i]); 6954 } 6955 } 6956 bool append(UniqueChars&& arg) { 6957 if (!argv_.append(arg.get())) { 6958 return false; 6959 } 6960 6961 // Now owned by this vector. 6962 (void)arg.release(); 6963 return true; 6964 } 6965 char* const* get() const { return argv_.begin(); } 6966 size_t length() const { return argv_.length(); } 6967 char* operator[](size_t i) const { return argv_[i]; } 6968 void replace(size_t i, UniqueChars arg) { 6969 js_free(argv_[i]); 6970 argv_[i] = arg.release(); 6971 } 6972 }; 6973 6974 #if defined(XP_WIN) 6975 static bool EscapeForShell(JSContext* cx, AutoCStringVector& argv) { 6976 // Windows will break arguments in argv by various spaces, so we wrap each 6977 // argument in quotes and escape quotes within. Even with quotes, \ will be 6978 // treated like an escape character, so inflate each \ to \\. 6979 6980 for (size_t i = 0; i < argv.length(); i++) { 6981 if (!argv[i]) { 6982 continue; 6983 } 6984 6985 size_t newLen = 3; // quotes before and after and null-terminator 6986 for (char* p = argv[i]; *p; p++) { 6987 newLen++; 6988 if (*p == '\"' || *p == '\\') { 6989 newLen++; 6990 } 6991 } 6992 6993 auto escaped = cx->make_pod_array<char>(newLen); 6994 if (!escaped) { 6995 return false; 6996 } 6997 6998 char* src = argv[i]; 6999 char* dst = escaped.get(); 7000 *dst++ = '\"'; 7001 while (*src) { 7002 if (*src == '\"' || *src == '\\') { 7003 *dst++ = '\\'; 7004 } 7005 *dst++ = *src++; 7006 } 7007 *dst++ = '\"'; 7008 *dst++ = '\0'; 7009 MOZ_ASSERT(escaped.get() + newLen == dst); 7010 7011 argv.replace(i, std::move(escaped)); 7012 } 7013 return true; 7014 } 7015 #endif 7016 7017 #ifndef __wasi__ 7018 static bool ReadAll(int fd, wasm::Bytes* bytes) { 7019 size_t lastLength = bytes->length(); 7020 while (true) { 7021 static const int ChunkSize = 64 * 1024; 7022 if (!bytes->growBy(ChunkSize)) { 7023 return false; 7024 } 7025 7026 intptr_t readCount; 7027 while (true) { 7028 readCount = read(fd, bytes->begin() + lastLength, ChunkSize); 7029 if (readCount >= 0) { 7030 break; 7031 } 7032 if (errno != EINTR) { 7033 return false; 7034 } 7035 } 7036 7037 if (readCount < ChunkSize) { 7038 bytes->shrinkTo(lastLength + readCount); 7039 if (readCount == 0) { 7040 return true; 7041 } 7042 } 7043 7044 lastLength = bytes->length(); 7045 } 7046 } 7047 7048 static bool WriteAll(int fd, const uint8_t* bytes, size_t length) { 7049 while (length > 0) { 7050 int written = write(fd, bytes, length); 7051 if (written < 0) { 7052 if (errno == EINTR) { 7053 continue; 7054 } 7055 return false; 7056 } 7057 MOZ_ASSERT(unsigned(written) <= length); 7058 length -= written; 7059 bytes += written; 7060 } 7061 7062 return true; 7063 } 7064 7065 class AutoPipe { 7066 int fds_[2]; 7067 7068 public: 7069 AutoPipe() { 7070 fds_[0] = -1; 7071 fds_[1] = -1; 7072 } 7073 7074 ~AutoPipe() { 7075 if (fds_[0] != -1) { 7076 close(fds_[0]); 7077 } 7078 if (fds_[1] != -1) { 7079 close(fds_[1]); 7080 } 7081 } 7082 7083 bool init() { 7084 # ifdef XP_WIN 7085 return !_pipe(fds_, 4096, O_BINARY); 7086 # else 7087 return !pipe(fds_); 7088 # endif 7089 } 7090 7091 int reader() const { 7092 MOZ_ASSERT(fds_[0] != -1); 7093 return fds_[0]; 7094 } 7095 7096 int writer() const { 7097 MOZ_ASSERT(fds_[1] != -1); 7098 return fds_[1]; 7099 } 7100 7101 void closeReader() { 7102 MOZ_ASSERT(fds_[0] != -1); 7103 close(fds_[0]); 7104 fds_[0] = -1; 7105 } 7106 7107 void closeWriter() { 7108 MOZ_ASSERT(fds_[1] != -1); 7109 close(fds_[1]); 7110 fds_[1] = -1; 7111 } 7112 }; 7113 #endif // __wasi__ 7114 7115 int shell::sArgc; 7116 char** shell::sArgv; 7117 7118 #ifndef __wasi__ 7119 static const char sWasmCompileAndSerializeFlag[] = 7120 "--wasm-compile-and-serialize"; 7121 MOZ_RUNINIT static Vector<const char*, 5, js::SystemAllocPolicy> 7122 sCompilerProcessFlags; 7123 7124 static bool CompileAndSerializeInSeparateProcess(JSContext* cx, 7125 const uint8_t* bytecode, 7126 size_t bytecodeLength, 7127 wasm::Bytes* serialized) { 7128 AutoPipe stdIn, stdOut; 7129 if (!stdIn.init() || !stdOut.init()) { 7130 return false; 7131 } 7132 7133 AutoCStringVector argv(cx); 7134 7135 UniqueChars argv0 = DuplicateString(cx, sArgv[0]); 7136 if (!argv0 || !argv.append(std::move(argv0))) { 7137 return false; 7138 } 7139 7140 // Put compiler flags first since they must precede the non-option 7141 // file-descriptor args (passed on Windows, below). 7142 for (unsigned i = 0; i < sCompilerProcessFlags.length(); i++) { 7143 UniqueChars flags = DuplicateString(cx, sCompilerProcessFlags[i]); 7144 if (!flags || !argv.append(std::move(flags))) { 7145 return false; 7146 } 7147 } 7148 7149 UniqueChars arg; 7150 7151 arg = DuplicateString(sWasmCompileAndSerializeFlag); 7152 if (!arg || !argv.append(std::move(arg))) { 7153 return false; 7154 } 7155 7156 # ifdef XP_WIN 7157 // The spawned process will have all the stdIn/stdOut file handles open, but 7158 // without the power of fork, we need some other way to communicate the 7159 // integer fd values so we encode them in argv and WasmCompileAndSerialize() 7160 // has a matching #ifdef XP_WIN to parse them out. Communicate both ends of 7161 // both pipes so the child process can closed the unused ends. 7162 7163 arg = JS_smprintf("%d", stdIn.reader()); 7164 if (!arg || !argv.append(std::move(arg))) { 7165 return false; 7166 } 7167 7168 arg = JS_smprintf("%d", stdIn.writer()); 7169 if (!arg || !argv.append(std::move(arg))) { 7170 return false; 7171 } 7172 7173 arg = JS_smprintf("%d", stdOut.reader()); 7174 if (!arg || !argv.append(std::move(arg))) { 7175 return false; 7176 } 7177 7178 arg = JS_smprintf("%d", stdOut.writer()); 7179 if (!arg || !argv.append(std::move(arg))) { 7180 return false; 7181 } 7182 # endif 7183 7184 // Required by both _spawnv and exec. 7185 if (!argv.append(nullptr)) { 7186 return false; 7187 } 7188 7189 # ifdef XP_WIN 7190 if (!EscapeForShell(cx, argv)) { 7191 return false; 7192 } 7193 7194 int childPid = _spawnv(P_NOWAIT, sArgv[0], argv.get()); 7195 if (childPid == -1) { 7196 return false; 7197 } 7198 # else 7199 pid_t childPid = fork(); 7200 switch (childPid) { 7201 case -1: 7202 return false; 7203 case 0: 7204 // In the child process. Redirect stdin/stdout to the respective ends of 7205 // the pipes. Closing stdIn.writer() is necessary for stdin to hit EOF. 7206 // This case statement must not return before exec() takes over. Rather, 7207 // exit(-1) is used to return failure to the parent process. 7208 if (dup2(stdIn.reader(), STDIN_FILENO) == -1) { 7209 exit(-1); 7210 } 7211 if (dup2(stdOut.writer(), STDOUT_FILENO) == -1) { 7212 exit(-1); 7213 } 7214 close(stdIn.reader()); 7215 close(stdIn.writer()); 7216 close(stdOut.reader()); 7217 close(stdOut.writer()); 7218 execv(sArgv[0], argv.get()); 7219 exit(-1); 7220 } 7221 # endif 7222 7223 // In the parent process. Closing stdOut.writer() is necessary for 7224 // stdOut.reader() below to hit EOF. 7225 stdIn.closeReader(); 7226 stdOut.closeWriter(); 7227 7228 if (!WriteAll(stdIn.writer(), bytecode, bytecodeLength)) { 7229 return false; 7230 } 7231 7232 stdIn.closeWriter(); 7233 7234 if (!ReadAll(stdOut.reader(), serialized)) { 7235 return false; 7236 } 7237 7238 stdOut.closeReader(); 7239 7240 int status; 7241 # ifdef XP_WIN 7242 if (_cwait(&status, childPid, WAIT_CHILD) == -1) { 7243 return false; 7244 } 7245 # else 7246 while (true) { 7247 if (waitpid(childPid, &status, 0) >= 0) { 7248 break; 7249 } 7250 if (errno != EINTR) { 7251 return false; 7252 } 7253 } 7254 # endif 7255 7256 return status == 0; 7257 } 7258 7259 static bool WasmCompileAndSerialize(JSContext* cx) { 7260 MOZ_ASSERT(wasm::CodeCachingAvailable(cx)); 7261 7262 # ifdef XP_WIN 7263 // See CompileAndSerializeInSeparateProcess for why we've had to smuggle 7264 // these fd values through argv. Closing the writing ends is necessary for 7265 // the reading ends to hit EOF. 7266 int flagIndex = 0; 7267 for (; flagIndex < sArgc; flagIndex++) { 7268 if (!strcmp(sArgv[flagIndex], sWasmCompileAndSerializeFlag)) { 7269 break; 7270 } 7271 } 7272 MOZ_RELEASE_ASSERT(flagIndex < sArgc); 7273 7274 int fdsIndex = flagIndex + 1; 7275 MOZ_RELEASE_ASSERT(fdsIndex + 4 == sArgc); 7276 7277 int stdInReader = atoi(sArgv[fdsIndex + 0]); 7278 int stdInWriter = atoi(sArgv[fdsIndex + 1]); 7279 int stdOutReader = atoi(sArgv[fdsIndex + 2]); 7280 int stdOutWriter = atoi(sArgv[fdsIndex + 3]); 7281 7282 int stdIn = stdInReader; 7283 close(stdInWriter); 7284 close(stdOutReader); 7285 int stdOut = stdOutWriter; 7286 # else 7287 int stdIn = STDIN_FILENO; 7288 int stdOut = STDOUT_FILENO; 7289 # endif 7290 7291 wasm::MutableBytes bytecode = js_new<wasm::ShareableBytes>(); 7292 if (!ReadAll(stdIn, &bytecode->vector)) { 7293 return false; 7294 } 7295 7296 wasm::BytecodeSource bytecodeSource(bytecode->begin(), bytecode->length()); 7297 wasm::Bytes serialized; 7298 if (!wasm::CompileAndSerialize(cx, bytecodeSource, &serialized)) { 7299 return false; 7300 } 7301 7302 if (!WriteAll(stdOut, serialized.begin(), serialized.length())) { 7303 return false; 7304 } 7305 7306 return true; 7307 } 7308 7309 static bool WasmCompileInSeparateProcess(JSContext* cx, unsigned argc, 7310 Value* vp) { 7311 if (!wasm::CodeCachingAvailable(cx)) { 7312 JS_ReportErrorASCII(cx, "WebAssembly caching not supported"); 7313 return false; 7314 } 7315 7316 CallArgs args = CallArgsFromVp(argc, vp); 7317 if (!args.requireAtLeast(cx, "wasmCompileInSeparateProcess", 1)) { 7318 return false; 7319 } 7320 7321 SharedMem<uint8_t*> bytecode; 7322 size_t numBytes; 7323 if (!args[0].isObject() || 7324 !IsBufferSource(cx, &args[0].toObject(), /*allowShared*/ false, 7325 /*allowResizable*/ false, &bytecode, &numBytes)) { 7326 RootedObject callee(cx, &args.callee()); 7327 ReportUsageErrorASCII(cx, callee, "Argument must be a buffer source"); 7328 return false; 7329 } 7330 7331 wasm::Bytes serialized; 7332 if (!CompileAndSerializeInSeparateProcess(cx, bytecode.unwrap(), numBytes, 7333 &serialized)) { 7334 if (!cx->isExceptionPending()) { 7335 JS_ReportErrorASCII(cx, "creating and executing child process"); 7336 } 7337 return false; 7338 } 7339 7340 RootedObject module(cx); 7341 if (!wasm::DeserializeModule(cx, serialized, &module)) { 7342 return false; 7343 } 7344 7345 args.rval().setObject(*module); 7346 return true; 7347 } 7348 #endif // __wasi__ 7349 7350 static bool DecompileFunction(JSContext* cx, unsigned argc, Value* vp) { 7351 CallArgs args = CallArgsFromVp(argc, vp); 7352 if (args.length() < 1 || !args[0].isObject() || 7353 !args[0].toObject().is<JSFunction>()) { 7354 args.rval().setUndefined(); 7355 return true; 7356 } 7357 RootedFunction fun(cx, &args[0].toObject().as<JSFunction>()); 7358 JSString* result = JS_DecompileFunction(cx, fun); 7359 if (!result) { 7360 return false; 7361 } 7362 args.rval().setString(result); 7363 return true; 7364 } 7365 7366 static bool DecompileThisScript(JSContext* cx, unsigned argc, Value* vp) { 7367 CallArgs args = CallArgsFromVp(argc, vp); 7368 7369 NonBuiltinScriptFrameIter iter(cx); 7370 if (iter.done()) { 7371 args.rval().setString(cx->runtime()->emptyString); 7372 return true; 7373 } 7374 7375 { 7376 JSAutoRealm ar(cx, iter.script()); 7377 7378 RootedScript script(cx, iter.script()); 7379 JSString* result = JS_DecompileScript(cx, script); 7380 if (!result) { 7381 return false; 7382 } 7383 7384 args.rval().setString(result); 7385 } 7386 7387 return JS_WrapValue(cx, args.rval()); 7388 } 7389 7390 static bool ValueToSource(JSContext* cx, unsigned argc, Value* vp) { 7391 CallArgs args = CallArgsFromVp(argc, vp); 7392 7393 JSString* str = ValueToSource(cx, args.get(0)); 7394 if (!str) { 7395 return false; 7396 } 7397 7398 args.rval().setString(str); 7399 return true; 7400 } 7401 7402 static bool ThisFilename(JSContext* cx, unsigned argc, Value* vp) { 7403 CallArgs args = CallArgsFromVp(argc, vp); 7404 7405 JS::AutoFilename filename; 7406 if (!DescribeScriptedCaller(&filename, cx) || !filename.get()) { 7407 args.rval().setString(cx->runtime()->emptyString); 7408 return true; 7409 } 7410 7411 JSString* str = NewStringCopyUTF8(cx, filename.get()); 7412 if (!str) { 7413 return false; 7414 } 7415 7416 args.rval().setString(str); 7417 return true; 7418 } 7419 7420 static bool WrapWithProto(JSContext* cx, unsigned argc, Value* vp) { 7421 CallArgs args = CallArgsFromVp(argc, vp); 7422 Value obj = args.get(0); 7423 Value proto = args.get(1); 7424 if (!obj.isObject() || !proto.isObjectOrNull()) { 7425 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, 7426 JSSMSG_INVALID_ARGS, "wrapWithProto"); 7427 return false; 7428 } 7429 7430 // Disallow constructing (deeply) nested wrapper chains, to avoid running 7431 // out of stack space in isCallable/isConstructor. See bug 1126105. 7432 if (IsWrapper(&obj.toObject())) { 7433 JS_ReportErrorASCII(cx, "wrapWithProto cannot wrap a wrapper"); 7434 return false; 7435 } 7436 7437 WrapperOptions options(cx); 7438 options.setProto(proto.toObjectOrNull()); 7439 JSObject* wrapped = Wrapper::New(cx, &obj.toObject(), 7440 &Wrapper::singletonWithPrototype, options); 7441 if (!wrapped) { 7442 return false; 7443 } 7444 7445 args.rval().setObject(*wrapped); 7446 return true; 7447 } 7448 7449 static bool NewGlobal(JSContext* cx, unsigned argc, Value* vp) { 7450 CallArgs args = CallArgsFromVp(argc, vp); 7451 RootedObject callee(cx, &args.callee()); 7452 7453 JS::RealmOptions options; 7454 JS::RealmCreationOptions& creationOptions = options.creationOptions(); 7455 JS::RealmBehaviors& behaviors = options.behaviors(); 7456 ShellGlobalKind kind = ShellGlobalKind::WindowProxy; 7457 bool immutablePrototype = true; 7458 7459 SetStandardRealmOptions(options); 7460 7461 // Default to creating the global in the current compartment unless 7462 // --more-compartments is used. 7463 if (defaultToSameCompartment) { 7464 creationOptions.setExistingCompartment(cx->global()); 7465 } else { 7466 creationOptions.setNewCompartmentAndZone(); 7467 } 7468 7469 // Ensure the target compartment/zone is kept alive when sameCompartmentAs or 7470 // sameZoneAs is used. 7471 Rooted<JSObject*> compartmentRoot(cx); 7472 7473 JS::AutoHoldPrincipals principals(cx); 7474 7475 if (args.length() == 1 && args[0].isObject()) { 7476 RootedObject opts(cx, &args[0].toObject()); 7477 RootedValue v(cx); 7478 7479 if (!JS_GetProperty(cx, opts, "invisibleToDebugger", &v)) { 7480 return false; 7481 } 7482 if (v.isBoolean()) { 7483 creationOptions.setInvisibleToDebugger(v.toBoolean()); 7484 } 7485 7486 if (!JS_GetProperty(cx, opts, "sameZoneAs", &v)) { 7487 return false; 7488 } 7489 if (v.isObject()) { 7490 compartmentRoot = UncheckedUnwrap(&v.toObject()); 7491 creationOptions.setNewCompartmentInExistingZone(compartmentRoot); 7492 } 7493 7494 if (!JS_GetProperty(cx, opts, "sameCompartmentAs", &v)) { 7495 return false; 7496 } 7497 if (v.isObject()) { 7498 compartmentRoot = UncheckedUnwrap(&v.toObject()); 7499 creationOptions.setExistingCompartment(compartmentRoot); 7500 } 7501 7502 if (!JS_GetProperty(cx, opts, "newCompartment", &v)) { 7503 return false; 7504 } 7505 if (v.isBoolean()) { 7506 if (v.toBoolean()) { 7507 creationOptions.setNewCompartmentAndZone(); 7508 } else { 7509 creationOptions.setExistingCompartment(cx->global()); 7510 } 7511 } 7512 7513 if (!JS_GetProperty(cx, opts, "discardSource", &v)) { 7514 return false; 7515 } 7516 if (v.isBoolean()) { 7517 behaviors.setDiscardSource(v.toBoolean()); 7518 } 7519 7520 if (!JS_GetProperty(cx, opts, "useWindowProxy", &v)) { 7521 return false; 7522 } 7523 if (v.isBoolean()) { 7524 kind = v.toBoolean() ? ShellGlobalKind::WindowProxy 7525 : ShellGlobalKind::GlobalObject; 7526 } 7527 7528 if (!JS_GetProperty(cx, opts, "immutablePrototype", &v)) { 7529 return false; 7530 } 7531 if (v.isBoolean()) { 7532 immutablePrototype = v.toBoolean(); 7533 } 7534 7535 if (!JS_GetProperty(cx, opts, "systemPrincipal", &v)) { 7536 return false; 7537 } 7538 if (v.isBoolean()) { 7539 principals.reset(&ShellPrincipals::fullyTrusted); 7540 } 7541 7542 if (!JS_GetProperty(cx, opts, "principal", &v)) { 7543 return false; 7544 } 7545 if (!v.isUndefined()) { 7546 uint32_t bits; 7547 if (!ToUint32(cx, v, &bits)) { 7548 return false; 7549 } 7550 JSPrincipals* newPrincipals = cx->new_<ShellPrincipals>(bits); 7551 if (!newPrincipals) { 7552 return false; 7553 } 7554 principals.reset(newPrincipals); 7555 } 7556 7557 if (!JS_GetProperty(cx, opts, "enableCoopAndCoep", &v)) { 7558 return false; 7559 } 7560 if (v.isBoolean()) { 7561 creationOptions.setCoopAndCoepEnabled(v.toBoolean()); 7562 } 7563 7564 if (!JS_GetProperty(cx, opts, "freezeBuiltins", &v)) { 7565 return false; 7566 } 7567 if (v.isBoolean()) { 7568 creationOptions.setFreezeBuiltins(v.toBoolean()); 7569 } 7570 7571 // On the web, the SharedArrayBuffer constructor is not installed as a 7572 // global property in pages that aren't isolated in a separate process (and 7573 // thus can't allow the structured cloning of shared memory). Specify false 7574 // for this option to reproduce this behavior. 7575 if (!JS_GetProperty(cx, opts, "defineSharedArrayBufferConstructor", &v)) { 7576 return false; 7577 } 7578 if (v.isBoolean()) { 7579 creationOptions.setDefineSharedArrayBufferConstructor(v.toBoolean()); 7580 } 7581 7582 if (!JS_GetProperty(cx, opts, "timeZone", &v)) { 7583 return false; 7584 } 7585 if (v.isString()) { 7586 RootedString str(cx, v.toString()); 7587 UniqueChars timeZone = 7588 StringToTimeZone(cx, callee, str, AllowTimeZoneLink::No); 7589 if (!timeZone) { 7590 return false; 7591 } 7592 behaviors.setTimeZoneOverride(timeZone.get()); 7593 } 7594 7595 if (!JS_GetProperty(cx, opts, "alwaysUseFdlibm", &v)) { 7596 return false; 7597 } 7598 if (v.isBoolean()) { 7599 creationOptions.setAlwaysUseFdlibm(v.toBoolean()); 7600 } 7601 7602 if (!JS_GetProperty(cx, opts, "locale", &v)) { 7603 return false; 7604 } 7605 if (v.isString()) { 7606 RootedString str(cx, v.toString()); 7607 UniqueChars locale = StringToLocale(cx, callee, str); 7608 if (!locale) { 7609 return false; 7610 } 7611 behaviors.setLocaleOverride(locale.get()); 7612 } 7613 } 7614 7615 if (!CheckRealmOptions(cx, options, principals.get())) { 7616 return false; 7617 } 7618 7619 RootedObject global(cx, NewGlobalObject(cx, options, principals.get(), kind, 7620 immutablePrototype)); 7621 if (!global) { 7622 return false; 7623 } 7624 7625 RootedObject wrapped(cx, ToWindowProxyIfWindow(global)); 7626 if (!JS_WrapObject(cx, &wrapped)) { 7627 return false; 7628 } 7629 7630 args.rval().setObject(*wrapped); 7631 return true; 7632 } 7633 7634 static bool NukeAllCCWs(JSContext* cx, unsigned argc, Value* vp) { 7635 CallArgs args = CallArgsFromVp(argc, vp); 7636 7637 if (args.length() != 0) { 7638 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, 7639 JSSMSG_INVALID_ARGS, "nukeAllCCWs"); 7640 return false; 7641 } 7642 7643 NukeCrossCompartmentWrappers(cx, AllCompartments(), cx->realm(), 7644 NukeWindowReferences, NukeAllReferences); 7645 args.rval().setUndefined(); 7646 return true; 7647 } 7648 7649 static bool RecomputeWrappers(JSContext* cx, unsigned argc, Value* vp) { 7650 CallArgs args = CallArgsFromVp(argc, vp); 7651 7652 if (args.length() > 2) { 7653 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, 7654 JSSMSG_INVALID_ARGS, "recomputeWrappers"); 7655 return false; 7656 } 7657 7658 JS::Compartment* sourceComp = nullptr; 7659 if (args.get(0).isObject()) { 7660 sourceComp = JS::GetCompartment(UncheckedUnwrap(&args[0].toObject())); 7661 } 7662 7663 JS::Compartment* targetComp = nullptr; 7664 if (args.get(1).isObject()) { 7665 targetComp = JS::GetCompartment(UncheckedUnwrap(&args[1].toObject())); 7666 } 7667 7668 struct SingleOrAllCompartments final : public CompartmentFilter { 7669 JS::Compartment* comp; 7670 explicit SingleOrAllCompartments(JS::Compartment* c) : comp(c) {} 7671 virtual bool match(JS::Compartment* c) const override { 7672 return !comp || comp == c; 7673 } 7674 }; 7675 7676 if (!js::RecomputeWrappers(cx, SingleOrAllCompartments(sourceComp), 7677 SingleOrAllCompartments(targetComp))) { 7678 return false; 7679 } 7680 7681 args.rval().setUndefined(); 7682 return true; 7683 } 7684 7685 static bool DumpObjectWrappers(JSContext* cx, unsigned argc, Value* vp) { 7686 CallArgs args = CallArgsFromVp(argc, vp); 7687 7688 bool printedHeader = false; 7689 for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) { 7690 bool printedZoneInfo = false; 7691 for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) { 7692 bool printedCompartmentInfo = false; 7693 for (Compartment::ObjectWrapperEnum e(comp); !e.empty(); e.popFront()) { 7694 JSObject* wrapper = e.front().value().unbarrieredGet(); 7695 JSObject* wrapped = e.front().key(); 7696 if (!printedHeader) { 7697 fprintf(stderr, "Cross-compartment object wrappers:\n"); 7698 printedHeader = true; 7699 } 7700 if (!printedZoneInfo) { 7701 fprintf(stderr, " Zone %p:\n", zone.get()); 7702 printedZoneInfo = true; 7703 } 7704 if (!printedCompartmentInfo) { 7705 fprintf(stderr, " Compartment %p:\n", comp.get()); 7706 printedCompartmentInfo = true; 7707 } 7708 fprintf(stderr, 7709 " Object wrapper %p -> %p in zone %p compartment %p\n", 7710 wrapper, wrapped, wrapped->zone(), wrapped->compartment()); 7711 } 7712 } 7713 } 7714 7715 if (!printedHeader) { 7716 fprintf(stderr, "No cross-compartment object wrappers.\n"); 7717 } 7718 7719 args.rval().setUndefined(); 7720 return true; 7721 } 7722 7723 static bool GetMaxArgs(JSContext* cx, unsigned argc, Value* vp) { 7724 CallArgs args = CallArgsFromVp(argc, vp); 7725 args.rval().setInt32(ARGS_LENGTH_MAX); 7726 return true; 7727 } 7728 7729 static bool IsHTMLDDA_Call(JSContext* cx, unsigned argc, Value* vp) { 7730 CallArgs args = CallArgsFromVp(argc, vp); 7731 7732 // These are the required conditions under which this object may be called 7733 // by test262 tests, and the required behavior under those conditions. 7734 if (args.length() == 0 || 7735 (args[0].isString() && args[0].toString()->length() == 0)) { 7736 args.rval().setNull(); 7737 return true; 7738 } 7739 7740 JS_ReportErrorASCII( 7741 cx, "IsHTMLDDA object is being called in an impermissible manner"); 7742 return false; 7743 } 7744 7745 static bool CreateIsHTMLDDA(JSContext* cx, unsigned argc, Value* vp) { 7746 CallArgs args = CallArgsFromVp(argc, vp); 7747 7748 static const JSClassOps classOps = { 7749 nullptr, // addProperty 7750 nullptr, // delProperty 7751 nullptr, // enumerate 7752 nullptr, // newEnumerate 7753 nullptr, // resolve 7754 nullptr, // mayResolve 7755 nullptr, // finalize 7756 IsHTMLDDA_Call, // call 7757 nullptr, // construct 7758 nullptr, // trace 7759 }; 7760 7761 static const JSClass cls = { 7762 "IsHTMLDDA", 7763 JSCLASS_EMULATES_UNDEFINED, 7764 &classOps, 7765 }; 7766 7767 JSObject* obj = JS_NewObject(cx, &cls); 7768 if (!obj) { 7769 return false; 7770 } 7771 args.rval().setObject(*obj); 7772 return true; 7773 } 7774 7775 static bool GetSelfHostedValue(JSContext* cx, unsigned argc, Value* vp) { 7776 CallArgs args = CallArgsFromVp(argc, vp); 7777 7778 if (args.length() != 1 || !args[0].isString()) { 7779 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, 7780 JSSMSG_INVALID_ARGS, "getSelfHostedValue"); 7781 return false; 7782 } 7783 Rooted<JSAtom*> srcAtom(cx, ToAtom<CanGC>(cx, args[0])); 7784 if (!srcAtom) { 7785 return false; 7786 } 7787 Rooted<PropertyName*> srcName(cx, srcAtom->asPropertyName()); 7788 return GlobalObject::getIntrinsicValue(cx, cx->global(), srcName, 7789 args.rval()); 7790 } 7791 7792 class ShellSourceHook : public SourceHook { 7793 // The function we should call to lazily retrieve source code. 7794 PersistentRootedFunction fun; 7795 7796 public: 7797 ShellSourceHook(JSContext* cx, JSFunction& fun) : fun(cx, &fun) {} 7798 7799 bool load(JSContext* cx, const char* filename, char16_t** twoByteSource, 7800 char** utf8Source, size_t* length) override { 7801 MOZ_ASSERT((twoByteSource != nullptr) != (utf8Source != nullptr), 7802 "must be called requesting only one of UTF-8 or UTF-16 source"); 7803 7804 RootedString str(cx); 7805 if (filename) { 7806 str = NewStringCopyUTF8(cx, filename); 7807 if (!str) { 7808 return false; 7809 } 7810 } else { 7811 str = JS_GetEmptyString(cx); 7812 } 7813 RootedValue filenameValue(cx, StringValue(str)); 7814 7815 RootedValue result(cx); 7816 if (!Call(cx, UndefinedHandleValue, fun, HandleValueArray(filenameValue), 7817 &result)) { 7818 return false; 7819 } 7820 7821 str = JS::ToString(cx, result); 7822 if (!str) { 7823 return false; 7824 } 7825 7826 Rooted<JSLinearString*> linear(cx, str->ensureLinear(cx)); 7827 if (!linear) { 7828 return false; 7829 } 7830 7831 if (twoByteSource) { 7832 *length = JS_GetStringLength(linear); 7833 7834 *twoByteSource = cx->pod_malloc<char16_t>(*length); 7835 if (!*twoByteSource) { 7836 return false; 7837 } 7838 7839 CopyChars(*twoByteSource, *linear); 7840 } else { 7841 MOZ_ASSERT(utf8Source != nullptr); 7842 7843 *length = JS::GetDeflatedUTF8StringLength(linear); 7844 7845 *utf8Source = cx->pod_malloc<char>(*length); 7846 if (!*utf8Source) { 7847 return false; 7848 } 7849 7850 mozilla::DebugOnly<size_t> dstLen = JS::DeflateStringToUTF8Buffer( 7851 linear, mozilla::Span(*utf8Source, *length)); 7852 MOZ_ASSERT(dstLen == *length); 7853 } 7854 7855 return true; 7856 } 7857 }; 7858 7859 static bool WithSourceHook(JSContext* cx, unsigned argc, Value* vp) { 7860 CallArgs args = CallArgsFromVp(argc, vp); 7861 RootedObject callee(cx, &args.callee()); 7862 7863 if (args.length() != 2) { 7864 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments."); 7865 return false; 7866 } 7867 7868 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>() || 7869 !args[1].isObject() || !args[1].toObject().is<JSFunction>()) { 7870 ReportUsageErrorASCII(cx, callee, 7871 "First and second arguments must be functions."); 7872 return false; 7873 } 7874 7875 mozilla::UniquePtr<ShellSourceHook> hook = 7876 mozilla::MakeUnique<ShellSourceHook>(cx, 7877 args[0].toObject().as<JSFunction>()); 7878 if (!hook) { 7879 return false; 7880 } 7881 7882 mozilla::UniquePtr<SourceHook> savedHook = js::ForgetSourceHook(cx); 7883 js::SetSourceHook(cx, std::move(hook)); 7884 7885 RootedObject fun(cx, &args[1].toObject()); 7886 bool result = Call(cx, UndefinedHandleValue, fun, 7887 JS::HandleValueArray::empty(), args.rval()); 7888 js::SetSourceHook(cx, std::move(savedHook)); 7889 return result; 7890 } 7891 7892 static void PrintProfilerEvents_Callback(mozilla::MarkerCategory, 7893 const char* msg, const char* details) { 7894 fprintf(stderr, "PROFILER EVENT: %s %s\n", msg, details); 7895 } 7896 7897 static void PrintProfilerIntervals_Callback(mozilla::MarkerCategory, 7898 const char* msg, 7899 mozilla::TimeStamp start, 7900 const char* details) { 7901 fprintf(stderr, "PROFILER INTERVAL (%.2fms): %s %s\n", 7902 (TimeStamp::Now() - start).ToMilliseconds(), msg, details); 7903 } 7904 7905 static void PrintProfilerFlow_Callback(mozilla::MarkerCategory, 7906 const char* markerName, 7907 uint64_t flowId) { 7908 fprintf(stderr, "PROFILER FLOW: %s (flowId=%" PRIu64 ")\n", markerName, 7909 flowId); 7910 } 7911 7912 static void PrintProfilerTerminatingFlow_Callback(mozilla::MarkerCategory, 7913 const char* markerName, 7914 uint64_t flowId) { 7915 fprintf(stderr, "PROFILER TERMINATING FLOW: %s (flowId=%" PRIu64 ")\n", 7916 markerName, flowId); 7917 } 7918 7919 static bool PrintProfilerEvents(JSContext* cx, unsigned argc, Value* vp) { 7920 CallArgs args = CallArgsFromVp(argc, vp); 7921 if (cx->runtime()->geckoProfiler().enabled()) { 7922 js::RegisterContextProfilerMarkers( 7923 cx, &PrintProfilerEvents_Callback, &PrintProfilerIntervals_Callback, 7924 &PrintProfilerFlow_Callback, &PrintProfilerTerminatingFlow_Callback); 7925 } 7926 args.rval().setUndefined(); 7927 return true; 7928 } 7929 7930 #ifdef SINGLESTEP_PROFILING 7931 static void SingleStepCallback(void* arg, jit::Simulator* sim, void* pc) { 7932 JSContext* cx = reinterpret_cast<JSContext*>(arg); 7933 7934 // If profiling is not enabled, don't do anything. 7935 if (!cx->runtime()->geckoProfiler().enabled()) { 7936 return; 7937 } 7938 7939 JS::ProfilingFrameIterator::RegisterState state; 7940 state.pc = pc; 7941 # if defined(JS_SIMULATOR_ARM) 7942 state.sp = (void*)sim->get_register(jit::Simulator::sp); 7943 state.lr = (void*)sim->get_register(jit::Simulator::lr); 7944 state.fp = (void*)sim->get_register(jit::Simulator::fp); 7945 state.tempFP = (void*)sim->get_register(jit::Simulator::r7); 7946 # elif defined(JS_SIMULATOR_ARM64) 7947 state.sp = (void*)sim->get_sp(); 7948 state.lr = (void*)sim->get_lr(); 7949 state.fp = (void*)sim->get_fp(); 7950 state.tempFP = (void*)sim->xreg(11); 7951 # elif defined(JS_SIMULATOR_MIPS64) 7952 state.sp = (void*)sim->getRegister(jit::Simulator::sp); 7953 state.lr = (void*)sim->getRegister(jit::Simulator::ra); 7954 state.fp = (void*)sim->getRegister(jit::Simulator::fp); 7955 // see WasmTailCallFPScratchReg and CollapseWasmFrameFast 7956 state.tempFP = (void*)sim->getRegister(jit::Simulator::t7); 7957 # elif defined(JS_SIMULATOR_LOONG64) 7958 state.sp = (void*)sim->getRegister(jit::Simulator::sp); 7959 state.lr = (void*)sim->getRegister(jit::Simulator::ra); 7960 state.fp = (void*)sim->getRegister(jit::Simulator::fp); 7961 // see WasmTailCallFPScratchReg and CollapseWasmFrameFast 7962 state.tempFP = (void*)sim->getRegister(jit::Simulator::t3); 7963 # else 7964 # error "NYI: Single-step profiling support" 7965 # endif 7966 7967 mozilla::DebugOnly<void*> lastStackAddress = nullptr; 7968 StackChars stack; 7969 uint32_t frameNo = 0; 7970 AutoEnterOOMUnsafeRegion oomUnsafe; 7971 for (JS::ProfilingFrameIterator i(cx, state); !i.done(); ++i) { 7972 MOZ_ASSERT(i.stackAddress() != nullptr); 7973 # ifndef ENABLE_WASM_JSPI 7974 // The stack addresses are monotonically increasing, except when 7975 // suspendable stacks are present (e.g. when JS PI is enabled). 7976 MOZ_ASSERT(lastStackAddress <= i.stackAddress()); 7977 # endif 7978 lastStackAddress = i.stackAddress(); 7979 JS::ProfilingFrameIterator::Frame frames[16]; 7980 uint32_t nframes = i.extractStack(frames, 0, 16); 7981 for (uint32_t i = 0; i < nframes; i++) { 7982 # ifndef ENABLE_WASM_JSPI 7983 // Assert endStackAddress never exceeds sp (bug 1782188). 7984 MOZ_ASSERT(frames[i].endStackAddress >= state.sp); 7985 # endif 7986 if (frameNo > 0) { 7987 if (!stack.append(",", 1)) { 7988 oomUnsafe.crash("stack.append"); 7989 } 7990 } 7991 if (!stack.append(frames[i].label, strlen(frames[i].label))) { 7992 oomUnsafe.crash("stack.append"); 7993 } 7994 frameNo++; 7995 } 7996 } 7997 7998 ShellContext* sc = GetShellContext(cx); 7999 8000 // Only append the stack if it differs from the last stack. 8001 if (sc->stacks.empty() || sc->stacks.back().length() != stack.length() || 8002 !ArrayEqual(sc->stacks.back().begin(), stack.begin(), stack.length())) { 8003 if (!sc->stacks.append(std::move(stack))) { 8004 oomUnsafe.crash("stacks.append"); 8005 } 8006 } 8007 } 8008 #endif 8009 8010 static bool EnableSingleStepProfiling(JSContext* cx, unsigned argc, Value* vp) { 8011 #ifdef SINGLESTEP_PROFILING 8012 CallArgs args = CallArgsFromVp(argc, vp); 8013 8014 jit::Simulator* sim = cx->simulator(); 8015 sim->enable_single_stepping(SingleStepCallback, cx); 8016 8017 args.rval().setUndefined(); 8018 return true; 8019 #else 8020 JS_ReportErrorASCII(cx, "single-step profiling not enabled on this platform"); 8021 return false; 8022 #endif 8023 } 8024 8025 static bool DisableSingleStepProfiling(JSContext* cx, unsigned argc, 8026 Value* vp) { 8027 #ifdef SINGLESTEP_PROFILING 8028 CallArgs args = CallArgsFromVp(argc, vp); 8029 8030 jit::Simulator* sim = cx->simulator(); 8031 sim->disable_single_stepping(); 8032 8033 ShellContext* sc = GetShellContext(cx); 8034 8035 RootedValueVector elems(cx); 8036 for (size_t i = 0; i < sc->stacks.length(); i++) { 8037 JSString* stack = 8038 JS_NewUCStringCopyN(cx, sc->stacks[i].begin(), sc->stacks[i].length()); 8039 if (!stack) { 8040 return false; 8041 } 8042 if (!elems.append(StringValue(stack))) { 8043 return false; 8044 } 8045 } 8046 8047 JSObject* array = JS::NewArrayObject(cx, elems); 8048 if (!array) { 8049 return false; 8050 } 8051 8052 sc->stacks.clear(); 8053 args.rval().setObject(*array); 8054 return true; 8055 #else 8056 JS_ReportErrorASCII(cx, "single-step profiling not enabled on this platform"); 8057 return false; 8058 #endif 8059 } 8060 8061 static bool IsLatin1(JSContext* cx, unsigned argc, Value* vp) { 8062 CallArgs args = CallArgsFromVp(argc, vp); 8063 bool isLatin1 = 8064 args.get(0).isString() && args[0].toString()->hasLatin1Chars(); 8065 args.rval().setBoolean(isLatin1); 8066 return true; 8067 } 8068 8069 static bool EnableGeckoProfiling(JSContext* cx, unsigned argc, Value* vp) { 8070 CallArgs args = CallArgsFromVp(argc, vp); 8071 8072 if (!EnsureGeckoProfilingStackInstalled(cx, GetShellContext(cx))) { 8073 return false; 8074 } 8075 8076 cx->runtime()->geckoProfiler().enableSlowAssertions(false); 8077 cx->runtime()->geckoProfiler().enable(true); 8078 8079 args.rval().setUndefined(); 8080 return true; 8081 } 8082 8083 static bool EnableGeckoProfilingWithSlowAssertions(JSContext* cx, unsigned argc, 8084 Value* vp) { 8085 CallArgs args = CallArgsFromVp(argc, vp); 8086 args.rval().setUndefined(); 8087 8088 if (cx->runtime()->geckoProfiler().enabled()) { 8089 // If profiling already enabled with slow assertions disabled, 8090 // this is a no-op. 8091 if (cx->runtime()->geckoProfiler().slowAssertionsEnabled()) { 8092 return true; 8093 } 8094 8095 // Slow assertions are off. Disable profiling before re-enabling 8096 // with slow assertions on. 8097 cx->runtime()->geckoProfiler().enable(false); 8098 } 8099 8100 if (!EnsureGeckoProfilingStackInstalled(cx, GetShellContext(cx))) { 8101 return false; 8102 } 8103 8104 cx->runtime()->geckoProfiler().enableSlowAssertions(true); 8105 cx->runtime()->geckoProfiler().enable(true); 8106 8107 return true; 8108 } 8109 8110 static bool DisableGeckoProfiling(JSContext* cx, unsigned argc, Value* vp) { 8111 CallArgs args = CallArgsFromVp(argc, vp); 8112 args.rval().setUndefined(); 8113 8114 if (!cx->runtime()->geckoProfiler().enabled()) { 8115 return true; 8116 } 8117 8118 cx->runtime()->geckoProfiler().enable(false); 8119 return true; 8120 } 8121 8122 static bool GetGeckoProfilingScriptSourcesCount(JSContext* cx, unsigned argc, 8123 Value* vp) { 8124 CallArgs args = CallArgsFromVp(argc, vp); 8125 size_t count = cx->runtime()->geckoProfiler().scriptSourcesCount(); 8126 args.rval().setNumber(static_cast<double>(count)); 8127 return true; 8128 } 8129 8130 // Global mailbox that is used to communicate a shareable object value from one 8131 // worker to another. 8132 // 8133 // These object types are shareable: 8134 // 8135 // - SharedArrayBuffer 8136 // - WasmMemoryObject (when constructed with shared:true) 8137 // - WasmModuleObject 8138 // 8139 // For the SharedArrayBuffer and WasmMemoryObject we transmit the underlying 8140 // SharedArrayRawBuffer ("SARB"). For the WasmModuleObject we transmit the 8141 // underlying JS::WasmModule. The transmitted types are refcounted. When they 8142 // are in the mailbox their reference counts are at least 1, accounting for the 8143 // reference from the mailbox. 8144 // 8145 // The lock guards the mailbox variable and prevents a race where two workers 8146 // try to set the mailbox at the same time to replace an object that is only 8147 // referenced from the mailbox: the workers will both decrement the reference 8148 // count on the old object, and one of those decrements will be on a garbage 8149 // object. We could implement this with atomics and a CAS loop but it's not 8150 // worth the bother. 8151 // 8152 // Note that if a thread reads the mailbox repeatedly it will get distinct 8153 // objects on each read. The alternatives are to cache created objects locally, 8154 // but this retains storage we don't need to retain, or to somehow clear the 8155 // mailbox locally, but this creates a coordination headache. Buyer beware. 8156 8157 enum class MailboxTag { 8158 Empty, 8159 SharedArrayBuffer, 8160 WasmMemory, 8161 WasmModule, 8162 Number, 8163 }; 8164 8165 struct SharedObjectMailbox { 8166 union Value { 8167 struct { 8168 SharedArrayRawBuffer* buffer; 8169 size_t length; 8170 bool isHugeMemory; // For a WasmMemory tag, otherwise false 8171 bool isGrowable; // For GrowableSharedArrayBuffer, otherwise false 8172 } sarb; 8173 JS::WasmModule* module; 8174 double number; 8175 8176 Value() : number(0.0) {} 8177 }; 8178 8179 MailboxTag tag = MailboxTag::Empty; 8180 Value val; 8181 }; 8182 8183 using SOMailbox = ExclusiveData<SharedObjectMailbox>; 8184 8185 // Never null after successful initialization. 8186 static SOMailbox* sharedObjectMailbox; 8187 8188 static bool InitSharedObjectMailbox() { 8189 sharedObjectMailbox = js_new<SOMailbox>(mutexid::ShellObjectMailbox); 8190 return sharedObjectMailbox != nullptr; 8191 } 8192 8193 static void DestructSharedObjectMailbox() { 8194 // All workers need to have terminated at this point. 8195 8196 { 8197 auto mbx = sharedObjectMailbox->lock(); 8198 switch (mbx->tag) { 8199 case MailboxTag::Empty: 8200 case MailboxTag::Number: 8201 break; 8202 case MailboxTag::SharedArrayBuffer: 8203 case MailboxTag::WasmMemory: 8204 mbx->val.sarb.buffer->dropReference(); 8205 break; 8206 case MailboxTag::WasmModule: 8207 mbx->val.module->Release(); 8208 break; 8209 default: 8210 MOZ_CRASH(); 8211 } 8212 } 8213 8214 js_delete(sharedObjectMailbox); 8215 sharedObjectMailbox = nullptr; 8216 } 8217 8218 static bool GetSharedObject(JSContext* cx, unsigned argc, Value* vp) { 8219 CallArgs args = CallArgsFromVp(argc, vp); 8220 RootedObject newObj(cx); 8221 8222 { 8223 auto mbx = sharedObjectMailbox->lock(); 8224 switch (mbx->tag) { 8225 case MailboxTag::Empty: { 8226 break; 8227 } 8228 case MailboxTag::Number: { 8229 args.rval().setNumber(mbx->val.number); 8230 return true; 8231 } 8232 case MailboxTag::SharedArrayBuffer: 8233 case MailboxTag::WasmMemory: { 8234 // Flag was set in the sender; ensure it is set in the receiver. 8235 MOZ_ASSERT( 8236 cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled()); 8237 8238 // The protocol for creating a SAB requires the refcount to be 8239 // incremented prior to the SAB creation. 8240 8241 SharedArrayRawBuffer* buf = mbx->val.sarb.buffer; 8242 size_t length = mbx->val.sarb.length; 8243 if (!buf->addReference()) { 8244 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 8245 JSMSG_SC_SAB_REFCNT_OFLO); 8246 return false; 8247 } 8248 8249 // If the allocation fails we must decrement the refcount before 8250 // returning. 8251 8252 Rooted<ArrayBufferObjectMaybeShared*> maybesab(cx); 8253 if (!mbx->val.sarb.isGrowable) { 8254 maybesab = SharedArrayBufferObject::New(cx, buf, length); 8255 } else { 8256 maybesab = SharedArrayBufferObject::NewGrowable(cx, buf, length); 8257 } 8258 if (!maybesab) { 8259 buf->dropReference(); 8260 return false; 8261 } 8262 8263 // At this point the SAB was created successfully and it owns the 8264 // refcount-increase on the buffer that we performed above. So even 8265 // if we fail to allocate along any path below we must not decrement 8266 // the refcount; the garbage collector must be allowed to handle 8267 // that via finalization of the orphaned SAB object. 8268 8269 if (mbx->tag == MailboxTag::SharedArrayBuffer) { 8270 newObj = maybesab; 8271 } else { 8272 if (!GlobalObject::ensureConstructor(cx, cx->global(), 8273 JSProto_WebAssembly)) { 8274 return false; 8275 } 8276 RootedObject proto(cx, 8277 &cx->global()->getPrototype(JSProto_WasmMemory)); 8278 newObj = WasmMemoryObject::create(cx, maybesab, 8279 mbx->val.sarb.isHugeMemory, proto); 8280 MOZ_ASSERT_IF(newObj, newObj->as<WasmMemoryObject>().isShared()); 8281 if (!newObj) { 8282 return false; 8283 } 8284 } 8285 8286 break; 8287 } 8288 case MailboxTag::WasmModule: { 8289 if (!GlobalObject::ensureConstructor(cx, cx->global(), 8290 JSProto_WebAssembly)) { 8291 return false; 8292 } 8293 8294 // WasmModuleObject::create() increments the refcount on the module 8295 // and signals an error and returns null if that fails. 8296 newObj = mbx->val.module->createObject(cx); 8297 if (!newObj) { 8298 return false; 8299 } 8300 break; 8301 } 8302 default: { 8303 MOZ_CRASH(); 8304 } 8305 } 8306 } 8307 8308 args.rval().setObjectOrNull(newObj); 8309 return true; 8310 } 8311 8312 static bool SetSharedObject(JSContext* cx, unsigned argc, Value* vp) { 8313 CallArgs args = CallArgsFromVp(argc, vp); 8314 8315 MailboxTag tag = MailboxTag::Empty; 8316 SharedObjectMailbox::Value value; 8317 8318 // Increase refcounts when we obtain the value to avoid operating on dead 8319 // storage during self-assignment. 8320 8321 if (args.get(0).isObject()) { 8322 RootedObject obj(cx, &args[0].toObject()); 8323 if (obj->is<SharedArrayBufferObject>()) { 8324 Rooted<SharedArrayBufferObject*> sab(cx, 8325 &obj->as<SharedArrayBufferObject>()); 8326 tag = MailboxTag::SharedArrayBuffer; 8327 value.sarb.buffer = sab->rawBufferObject(); 8328 value.sarb.length = sab->byteLengthOrMaxByteLength(); 8329 value.sarb.isHugeMemory = false; 8330 value.sarb.isGrowable = sab->isGrowable(); 8331 if (!value.sarb.buffer->addReference()) { 8332 JS_ReportErrorASCII(cx, 8333 "Reference count overflow on SharedArrayBuffer"); 8334 return false; 8335 } 8336 } else if (obj->is<WasmMemoryObject>()) { 8337 // Here we must transmit sab.byteLength() as the length; the SARB has its 8338 // own notion of the length which may be greater, and that's fine. 8339 if (obj->as<WasmMemoryObject>().isShared()) { 8340 Rooted<SharedArrayBufferObject*> sab( 8341 cx, &obj->as<WasmMemoryObject>() 8342 .buffer() 8343 .as<SharedArrayBufferObject>()); 8344 tag = MailboxTag::WasmMemory; 8345 value.sarb.buffer = sab->rawBufferObject(); 8346 value.sarb.length = sab->byteLength(); 8347 value.sarb.isHugeMemory = obj->as<WasmMemoryObject>().isHuge(); 8348 value.sarb.isGrowable = sab->isGrowable(); 8349 if (!value.sarb.buffer->addReference()) { 8350 JS_ReportErrorASCII(cx, 8351 "Reference count overflow on SharedArrayBuffer"); 8352 return false; 8353 } 8354 } else { 8355 JS_ReportErrorASCII(cx, "Invalid argument to SetSharedObject"); 8356 return false; 8357 } 8358 } else if (JS::IsWasmModuleObject(obj)) { 8359 tag = MailboxTag::WasmModule; 8360 value.module = JS::GetWasmModule(obj).forget().take(); 8361 } else { 8362 JS_ReportErrorASCII(cx, "Invalid argument to SetSharedObject"); 8363 return false; 8364 } 8365 } else if (args.get(0).isNumber()) { 8366 tag = MailboxTag::Number; 8367 value.number = args.get(0).toNumber(); 8368 // Nothing 8369 } else if (args.get(0).isNullOrUndefined()) { 8370 // Nothing 8371 } else { 8372 JS_ReportErrorASCII(cx, "Invalid argument to SetSharedObject"); 8373 return false; 8374 } 8375 8376 { 8377 auto mbx = sharedObjectMailbox->lock(); 8378 8379 switch (mbx->tag) { 8380 case MailboxTag::Empty: 8381 case MailboxTag::Number: 8382 break; 8383 case MailboxTag::SharedArrayBuffer: 8384 case MailboxTag::WasmMemory: 8385 mbx->val.sarb.buffer->dropReference(); 8386 break; 8387 case MailboxTag::WasmModule: 8388 mbx->val.module->Release(); 8389 break; 8390 default: 8391 MOZ_CRASH(); 8392 } 8393 8394 mbx->tag = tag; 8395 mbx->val = value; 8396 } 8397 8398 args.rval().setUndefined(); 8399 return true; 8400 } 8401 8402 using Uint8Vector = Vector<uint8_t, 0, SystemAllocPolicy>; 8403 8404 class StreamCacheEntry : public AtomicRefCounted<StreamCacheEntry>, 8405 public JS::OptimizedEncodingListener { 8406 using AtomicBase = AtomicRefCounted<StreamCacheEntry>; 8407 8408 Uint8Vector bytes_; 8409 ExclusiveData<Uint8Vector> optimized_; 8410 8411 public: 8412 explicit StreamCacheEntry(Uint8Vector&& original) 8413 : bytes_(std::move(original)), 8414 optimized_(mutexid::ShellStreamCacheEntryState) {} 8415 8416 // Implement JS::OptimizedEncodingListener: 8417 8418 MozExternalRefCountType MOZ_XPCOM_ABI AddRef() override { 8419 AtomicBase::AddRef(); 8420 return 1; // unused 8421 } 8422 MozExternalRefCountType MOZ_XPCOM_ABI Release() override { 8423 AtomicBase::Release(); 8424 return 0; // unused 8425 } 8426 8427 const Uint8Vector& bytes() const { return bytes_; } 8428 8429 void storeOptimizedEncoding(const uint8_t* srcBytes, 8430 size_t srcLength) override { 8431 MOZ_ASSERT(srcLength > 0); 8432 8433 // Tolerate races since a single StreamCacheEntry object can be used as 8434 // the source of multiple streaming compilations. 8435 auto dstBytes = optimized_.lock(); 8436 if (dstBytes->length() > 0) { 8437 return; 8438 } 8439 8440 if (!dstBytes->resize(srcLength)) { 8441 return; 8442 } 8443 memcpy(dstBytes->begin(), srcBytes, srcLength); 8444 } 8445 8446 bool hasOptimizedEncoding() const { return !optimized_.lock()->empty(); } 8447 const Uint8Vector& optimizedEncoding() const { 8448 return optimized_.lock().get(); 8449 } 8450 }; 8451 8452 using StreamCacheEntryPtr = RefPtr<StreamCacheEntry>; 8453 8454 class StreamCacheEntryObject : public NativeObject { 8455 static const unsigned CACHE_ENTRY_SLOT = 0; 8456 static const JSClassOps classOps_; 8457 static const JSPropertySpec properties_; 8458 8459 static void finalize(JS::GCContext* gcx, JSObject* obj) { 8460 obj->as<StreamCacheEntryObject>().cache().Release(); 8461 } 8462 8463 static bool cachedGetter(JSContext* cx, unsigned argc, Value* vp) { 8464 CallArgs args = CallArgsFromVp(argc, vp); 8465 if (!args.thisv().isObject() || 8466 !args.thisv().toObject().is<StreamCacheEntryObject>()) { 8467 JS_ReportErrorASCII(cx, "Expected StreamCacheEntry object"); 8468 return false; 8469 } 8470 8471 StreamCacheEntryObject& obj = 8472 args.thisv().toObject().as<StreamCacheEntryObject>(); 8473 args.rval().setBoolean(obj.cache().hasOptimizedEncoding()); 8474 return true; 8475 } 8476 static bool getBuffer(JSContext* cx, unsigned argc, Value* vp) { 8477 CallArgs args = CallArgsFromVp(argc, vp); 8478 if (!args.thisv().isObject() || 8479 !args.thisv().toObject().is<StreamCacheEntryObject>()) { 8480 JS_ReportErrorASCII(cx, "Expected StreamCacheEntry object"); 8481 return false; 8482 } 8483 8484 auto& bytes = 8485 args.thisv().toObject().as<StreamCacheEntryObject>().cache().bytes(); 8486 auto* buffer = ArrayBufferObject::createZeroed(cx, bytes.length()); 8487 if (!buffer) { 8488 return false; 8489 } 8490 8491 memcpy(buffer->dataPointer(), bytes.begin(), bytes.length()); 8492 8493 args.rval().setObject(*buffer); 8494 return true; 8495 } 8496 8497 public: 8498 static const unsigned RESERVED_SLOTS = 1; 8499 static const JSClass class_; 8500 static const JSPropertySpec properties[]; 8501 8502 static bool construct(JSContext* cx, unsigned argc, Value* vp) { 8503 CallArgs args = CallArgsFromVp(argc, vp); 8504 if (!args.requireAtLeast(cx, "streamCacheEntry", 1)) { 8505 return false; 8506 } 8507 8508 SharedMem<uint8_t*> ptr; 8509 size_t numBytes; 8510 if (!args[0].isObject() || 8511 !IsBufferSource(cx, &args[0].toObject(), /*allowShared*/ true, 8512 /*allowResizable*/ true, &ptr, &numBytes)) { 8513 RootedObject callee(cx, &args.callee()); 8514 ReportUsageErrorASCII(cx, callee, "Argument must be an ArrayBuffer"); 8515 return false; 8516 } 8517 8518 Uint8Vector bytes; 8519 if (!bytes.resize(numBytes)) { 8520 ReportOutOfMemory(cx); 8521 return false; 8522 } 8523 8524 memcpy(bytes.begin(), ptr.unwrap(), numBytes); 8525 8526 RefPtr<StreamCacheEntry> cache = 8527 cx->new_<StreamCacheEntry>(std::move(bytes)); 8528 if (!cache) { 8529 return false; 8530 } 8531 8532 Rooted<NativeObject*> obj( 8533 cx, NewObjectWithGivenProto<StreamCacheEntryObject>(cx, nullptr)); 8534 if (!obj) { 8535 return false; 8536 } 8537 obj->initReservedSlot(CACHE_ENTRY_SLOT, 8538 PrivateValue(cache.forget().take())); 8539 8540 if (!JS_DefineProperty(cx, obj, "cached", cachedGetter, nullptr, 0)) { 8541 return false; 8542 } 8543 if (!JS_DefineFunction(cx, obj, "getBuffer", getBuffer, 0, 0)) { 8544 return false; 8545 } 8546 8547 args.rval().setObject(*obj); 8548 return true; 8549 } 8550 8551 StreamCacheEntry& cache() const { 8552 return *(StreamCacheEntry*)getReservedSlot(CACHE_ENTRY_SLOT).toPrivate(); 8553 } 8554 }; 8555 8556 const JSClassOps StreamCacheEntryObject::classOps_ = { 8557 nullptr, // addProperty 8558 nullptr, // delProperty 8559 nullptr, // enumerate 8560 nullptr, // newEnumerate 8561 nullptr, // resolve 8562 nullptr, // mayResolve 8563 StreamCacheEntryObject::finalize, // finalize 8564 nullptr, // call 8565 nullptr, // construct 8566 nullptr, // trace 8567 }; 8568 8569 const JSClass StreamCacheEntryObject::class_ = { 8570 "StreamCacheEntryObject", 8571 JSCLASS_HAS_RESERVED_SLOTS(StreamCacheEntryObject::RESERVED_SLOTS) | 8572 JSCLASS_BACKGROUND_FINALIZE, 8573 &StreamCacheEntryObject::classOps_, 8574 }; 8575 8576 struct BufferStreamJob { 8577 Variant<Uint8Vector, StreamCacheEntryPtr> source; 8578 Thread thread; 8579 JS::StreamConsumer* consumer; 8580 8581 BufferStreamJob(Uint8Vector&& source, JS::StreamConsumer* consumer) 8582 : source(AsVariant<Uint8Vector>(std::move(source))), consumer(consumer) {} 8583 BufferStreamJob(StreamCacheEntry& source, JS::StreamConsumer* consumer) 8584 : source(AsVariant<StreamCacheEntryPtr>(&source)), consumer(consumer) {} 8585 }; 8586 8587 struct BufferStreamState { 8588 Vector<UniquePtr<BufferStreamJob>, 0, SystemAllocPolicy> jobs; 8589 size_t delayMillis; 8590 size_t chunkSize; 8591 bool shutdown; 8592 8593 BufferStreamState() : delayMillis(1), chunkSize(10), shutdown(false) {} 8594 8595 ~BufferStreamState() { MOZ_ASSERT(jobs.empty()); } 8596 }; 8597 8598 static ExclusiveWaitableData<BufferStreamState>* bufferStreamState; 8599 8600 static void BufferStreamMain(BufferStreamJob* job) { 8601 const uint8_t* bytes; 8602 size_t byteLength; 8603 JS::OptimizedEncodingListener* listener; 8604 if (job->source.is<StreamCacheEntryPtr>()) { 8605 StreamCacheEntry& cache = *job->source.as<StreamCacheEntryPtr>(); 8606 if (cache.hasOptimizedEncoding()) { 8607 const Uint8Vector& optimized = cache.optimizedEncoding(); 8608 job->consumer->consumeOptimizedEncoding(optimized.begin(), 8609 optimized.length()); 8610 goto done; 8611 } 8612 8613 bytes = cache.bytes().begin(); 8614 byteLength = cache.bytes().length(); 8615 listener = &cache; 8616 } else { 8617 bytes = job->source.as<Uint8Vector>().begin(); 8618 byteLength = job->source.as<Uint8Vector>().length(); 8619 listener = nullptr; 8620 } 8621 8622 size_t byteOffset; 8623 byteOffset = 0; 8624 while (true) { 8625 if (byteOffset == byteLength) { 8626 job->consumer->streamEnd(listener); 8627 break; 8628 } 8629 8630 bool shutdown; 8631 size_t delayMillis; 8632 size_t chunkSize; 8633 { 8634 auto state = bufferStreamState->lock(); 8635 shutdown = state->shutdown; 8636 delayMillis = state->delayMillis; 8637 chunkSize = state->chunkSize; 8638 } 8639 8640 if (shutdown) { 8641 job->consumer->streamError(JSMSG_STREAM_CONSUME_ERROR); 8642 break; 8643 } 8644 8645 ThisThread::SleepMilliseconds(delayMillis); 8646 8647 chunkSize = std::min(chunkSize, byteLength - byteOffset); 8648 8649 if (!job->consumer->consumeChunk(bytes + byteOffset, chunkSize)) { 8650 break; 8651 } 8652 8653 byteOffset += chunkSize; 8654 } 8655 8656 done: 8657 auto state = bufferStreamState->lock(); 8658 size_t jobIndex = 0; 8659 while (state->jobs[jobIndex].get() != job) { 8660 jobIndex++; 8661 } 8662 job->thread.detach(); // quiet assert in ~Thread() called by erase(). 8663 state->jobs.erase(state->jobs.begin() + jobIndex); 8664 if (state->jobs.empty()) { 8665 state.notify_all(/* jobs empty */); 8666 } 8667 } 8668 8669 static bool ConsumeBufferSource(JSContext* cx, JS::HandleObject obj, 8670 JS::MimeType, JS::StreamConsumer* consumer) { 8671 { 8672 RootedValue url(cx); 8673 if (!JS_GetProperty(cx, obj, "url", &url)) { 8674 return false; 8675 } 8676 UniqueChars urlChars; 8677 if (url.isString()) { 8678 Rooted<JSString*> str(cx, url.toString()); 8679 urlChars = JS_EncodeStringToUTF8(cx, str); 8680 if (!urlChars) { 8681 return false; 8682 } 8683 } 8684 8685 RootedValue mapUrl(cx); 8686 if (!JS_GetProperty(cx, obj, "sourceMappingURL", &mapUrl)) { 8687 return false; 8688 } 8689 UniqueChars mapUrlChars; 8690 if (mapUrl.isString()) { 8691 Rooted<JSString*> str(cx, mapUrl.toString()); 8692 mapUrlChars = JS_EncodeStringToUTF8(cx, str); 8693 if (!mapUrlChars) { 8694 return false; 8695 } 8696 } 8697 8698 consumer->noteResponseURLs(urlChars.get(), mapUrlChars.get()); 8699 } 8700 8701 UniquePtr<BufferStreamJob> job; 8702 8703 SharedMem<uint8_t*> dataPointer; 8704 size_t byteLength; 8705 if (IsBufferSource(cx, obj, /*allowShared*/ true, /*allowResizable*/ true, 8706 &dataPointer, &byteLength)) { 8707 Uint8Vector bytes; 8708 if (!bytes.resize(byteLength)) { 8709 JS_ReportOutOfMemory(cx); 8710 return false; 8711 } 8712 8713 memcpy(bytes.begin(), dataPointer.unwrap(), byteLength); 8714 job = cx->make_unique<BufferStreamJob>(std::move(bytes), consumer); 8715 } else if (obj->is<StreamCacheEntryObject>()) { 8716 job = cx->make_unique<BufferStreamJob>( 8717 obj->as<StreamCacheEntryObject>().cache(), consumer); 8718 } else { 8719 JS_ReportErrorASCII( 8720 cx, 8721 "shell streaming consumes a buffer source (buffer or view) " 8722 "or StreamCacheEntryObject"); 8723 return false; 8724 } 8725 if (!job) { 8726 return false; 8727 } 8728 8729 BufferStreamJob* jobPtr = job.get(); 8730 8731 { 8732 auto state = bufferStreamState->lock(); 8733 MOZ_ASSERT(!state->shutdown); 8734 if (!state->jobs.append(std::move(job))) { 8735 JS_ReportOutOfMemory(cx); 8736 return false; 8737 } 8738 } 8739 8740 { 8741 AutoEnterOOMUnsafeRegion oomUnsafe; 8742 if (!jobPtr->thread.init(BufferStreamMain, jobPtr)) { 8743 oomUnsafe.crash("ConsumeBufferSource"); 8744 } 8745 } 8746 8747 return true; 8748 } 8749 8750 static void ReportStreamError(JSContext* cx, size_t errorNumber) { 8751 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber); 8752 } 8753 8754 static bool SetBufferStreamParams(JSContext* cx, unsigned argc, Value* vp) { 8755 CallArgs args = CallArgsFromVp(argc, vp); 8756 if (!args.requireAtLeast(cx, "setBufferStreamParams", 2)) { 8757 return false; 8758 } 8759 8760 double delayMillis; 8761 if (!ToNumber(cx, args[0], &delayMillis)) { 8762 return false; 8763 } 8764 8765 double chunkSize; 8766 if (!ToNumber(cx, args[1], &chunkSize)) { 8767 return false; 8768 } 8769 8770 { 8771 auto state = bufferStreamState->lock(); 8772 state->delayMillis = delayMillis; 8773 state->chunkSize = chunkSize; 8774 } 8775 8776 args.rval().setUndefined(); 8777 return true; 8778 } 8779 8780 static void ShutdownBufferStreams() { 8781 auto state = bufferStreamState->lock(); 8782 state->shutdown = true; 8783 while (!state->jobs.empty()) { 8784 state.wait(/* jobs empty */); 8785 } 8786 state->jobs.clearAndFree(); 8787 } 8788 8789 static bool DumpScopeChain(JSContext* cx, unsigned argc, Value* vp) { 8790 CallArgs args = CallArgsFromVp(argc, vp); 8791 RootedObject callee(cx, &args.callee()); 8792 8793 if (js::SupportDifferentialTesting()) { 8794 ReportUsageErrorASCII( 8795 cx, callee, "Function not available in differential testing mode."); 8796 return false; 8797 } 8798 8799 if (args.length() != 1) { 8800 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 8801 return false; 8802 } 8803 8804 if (!args[0].isObject() || 8805 !(args[0].toObject().is<JSFunction>() || 8806 args[0].toObject().is<ShellModuleObjectWrapper>())) { 8807 ReportUsageErrorASCII( 8808 cx, callee, "Argument must be an interpreted function or a module"); 8809 return false; 8810 } 8811 8812 RootedObject obj(cx, &args[0].toObject()); 8813 RootedScript script(cx); 8814 8815 if (obj->is<JSFunction>()) { 8816 RootedFunction fun(cx, &obj->as<JSFunction>()); 8817 if (!fun->isInterpreted()) { 8818 ReportUsageErrorASCII(cx, callee, 8819 "Argument must be an interpreted function"); 8820 return false; 8821 } 8822 script = JSFunction::getOrCreateScript(cx, fun); 8823 if (!script) { 8824 return false; 8825 } 8826 } else { 8827 script = obj->as<ShellModuleObjectWrapper>().get()->maybeScript(); 8828 if (!script) { 8829 JS_ReportErrorASCII(cx, "module does not have an associated script"); 8830 return false; 8831 } 8832 } 8833 8834 script->bodyScope()->dump(); 8835 8836 args.rval().setUndefined(); 8837 return true; 8838 } 8839 8840 // For testing GC marking, blackRoot() and grayRoot() will heap-allocate an 8841 // array whose elements (as well as the array itself) will be marked as roots in 8842 // subsequent GCs. 8843 // 8844 // Note that EnsureGrayRoot() will blacken the returned object, so it will not 8845 // actually end up marked gray until the following GC clears the black bit 8846 // (assuming nothing is holding onto it.) 8847 // 8848 // The idea is that you can set up a whole graph of objects to be marked gray, 8849 // hanging off of the object returned from grayRoot(). Then you GC to clear the 8850 // black bits and set the gray bits. 8851 // 8852 // To test grayness, register the objects of interest with addMarkObservers(), 8853 // which takes an Array of objects (which will be marked black at the time 8854 // they're passed in). Their mark bits may be retrieved at any time with 8855 // getMarks(), in the form of an array of strings with each index corresponding 8856 // to the original objects passed to addMarkObservers(). 8857 8858 static bool EnsureRootArray(JSContext* cx, gc::MarkColor color, unsigned argc, 8859 Value* vp) { 8860 CallArgs args = CallArgsFromVp(argc, vp); 8861 8862 auto priv = EnsureShellCompartmentPrivate(cx); 8863 if (!priv) { 8864 return false; 8865 } 8866 8867 GCPtr<ArrayObject*>& root = 8868 (color == gc::MarkColor::Black) ? priv->blackRoot : priv->grayRoot; 8869 8870 if (!root && !(root = NewTenuredDenseEmptyArray(cx))) { 8871 return false; 8872 } 8873 8874 // Barrier to enforce the invariant that JS does not touch gray objects. 8875 JSObject* obj = root; 8876 JS::ExposeObjectToActiveJS(obj); 8877 8878 args.rval().setObject(*obj); 8879 return true; 8880 } 8881 8882 static bool EnsureBlackRoot(JSContext* cx, unsigned argc, Value* vp) { 8883 return EnsureRootArray(cx, gc::MarkColor::Black, argc, vp); 8884 } 8885 8886 static bool EnsureGrayRoot(JSContext* cx, unsigned argc, Value* vp) { 8887 return EnsureRootArray(cx, gc::MarkColor::Gray, argc, vp); 8888 } 8889 8890 static MarkBitObservers* EnsureMarkBitObservers(JSContext* cx) { 8891 ShellContext* sc = GetShellContext(cx); 8892 if (!sc->markObservers) { 8893 auto* observers = 8894 cx->new_<MarkBitObservers>(cx->runtime(), NonShrinkingValueVector()); 8895 if (!observers) { 8896 return nullptr; 8897 } 8898 sc->markObservers.reset(observers); 8899 } 8900 return sc->markObservers.get(); 8901 } 8902 8903 static bool ClearMarkObservers(JSContext* cx, unsigned argc, Value* vp) { 8904 CallArgs args = CallArgsFromVp(argc, vp); 8905 8906 auto markObservers = EnsureMarkBitObservers(cx); 8907 if (!markObservers) { 8908 return false; 8909 } 8910 8911 markObservers->get().clear(); 8912 8913 args.rval().setUndefined(); 8914 return true; 8915 } 8916 8917 static bool AddMarkObservers(JSContext* cx, unsigned argc, Value* vp) { 8918 CallArgs args = CallArgsFromVp(argc, vp); 8919 8920 auto markObservers = EnsureMarkBitObservers(cx); 8921 if (!markObservers) { 8922 return false; 8923 } 8924 8925 if (!args.get(0).isObject()) { 8926 JS_ReportErrorASCII(cx, "argument must be an Array of objects"); 8927 return false; 8928 } 8929 8930 RootedObject observersArg(cx, &args[0].toObject()); 8931 uint64_t length; 8932 if (!GetLengthProperty(cx, observersArg, &length)) { 8933 return false; 8934 } 8935 8936 if (length > UINT32_MAX) { 8937 JS_ReportErrorASCII(cx, "Invalid length for observers array"); 8938 return false; 8939 } 8940 8941 RootedValue value(cx); 8942 for (uint32_t i = 0; i < length; i++) { 8943 if (!JS_GetElement(cx, observersArg, i, &value)) { 8944 return false; 8945 } 8946 8947 if (!value.isObject() && !value.isSymbol()) { 8948 JS_ReportErrorASCII(cx, "Can only observe objects and symbols"); 8949 return false; 8950 } 8951 8952 if (gc::IsInsideNursery(value.toGCThing())) { 8953 // WeakCaches are not swept during a minor GC. To prevent 8954 // nursery-allocated contents from having the mark bits be deceptively 8955 // black until the second GC, they would need to be marked weakly (cf 8956 // NurseryAwareHashMap). It is simpler to evict the nursery to prevent 8957 // nursery objects from being observed. 8958 cx->runtime()->gc.evictNursery(); 8959 } 8960 8961 if (!markObservers->get().append(value)) { 8962 ReportOutOfMemory(cx); 8963 return false; 8964 } 8965 } 8966 8967 args.rval().setInt32(length); 8968 return true; 8969 } 8970 8971 static const char* ObserveMarkColor(const Value& value) { 8972 if (value.isUndefined()) { 8973 return "dead"; 8974 } 8975 8976 gc::Cell* cell = value.toGCThing(); 8977 Zone* zone = cell->zone(); 8978 if (zone->isGCPreparing()) { 8979 // The mark bits are not valid during unmarking. 8980 return "unmarked"; 8981 } 8982 8983 gc::TenuredCell* tc = &cell->asTenured(); 8984 if (tc->isMarkedGray()) { 8985 return "gray"; 8986 } 8987 8988 if (tc->isMarkedBlack()) { 8989 return "black"; 8990 } 8991 8992 return "unmarked"; 8993 } 8994 8995 static bool GetMarks(JSContext* cx, unsigned argc, Value* vp) { 8996 CallArgs args = CallArgsFromVp(argc, vp); 8997 8998 auto& observers = GetShellContext(cx)->markObservers; 8999 if (!observers) { 9000 args.rval().setUndefined(); 9001 return true; 9002 } 9003 9004 size_t length = observers->get().length(); 9005 Rooted<ArrayObject*> ret(cx, js::NewDenseEmptyArray(cx)); 9006 if (!ret) { 9007 return false; 9008 } 9009 9010 for (uint32_t i = 0; i < length; i++) { 9011 Value value = observers->get()[i]; 9012 const char* color = ObserveMarkColor(value); 9013 JSString* s = JS_NewStringCopyZ(cx, color); 9014 if (!s || !NewbornArrayPush(cx, ret, StringValue(s))) { 9015 return false; 9016 } 9017 } 9018 9019 args.rval().setObject(*ret); 9020 return true; 9021 } 9022 9023 #ifndef __wasi__ 9024 static bool WasmTextToBinary(JSContext* cx, unsigned argc, Value* vp) { 9025 CallArgs args = CallArgsFromVp(argc, vp); 9026 RootedObject callee(cx, &args.callee()); 9027 9028 if (!args.requireAtLeast(cx, "wasmTextToBinary", 1)) { 9029 return false; 9030 } 9031 9032 if (!args[0].isString()) { 9033 ReportUsageErrorASCII(cx, callee, "First argument must be a String"); 9034 return false; 9035 } 9036 9037 size_t textLen = args[0].toString()->length(); 9038 9039 AutoStableStringChars twoByteChars(cx); 9040 if (!twoByteChars.initTwoByte(cx, args[0].toString())) { 9041 return false; 9042 } 9043 9044 wasm::Bytes bytes; 9045 UniqueChars error; 9046 if (!wasm::TextToBinary(twoByteChars.twoByteChars(), textLen, &bytes, 9047 &error)) { 9048 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_TEXT_FAIL, 9049 error.get() ? error.get() : "out of memory"); 9050 return false; 9051 } 9052 9053 RootedObject binary(cx, JS_NewUint8Array(cx, bytes.length())); 9054 if (!binary) { 9055 return false; 9056 } 9057 9058 memcpy(binary->as<TypedArrayObject>().dataPointerUnshared(), bytes.begin(), 9059 bytes.length()); 9060 9061 args.rval().setObject(*binary); 9062 return true; 9063 } 9064 9065 # ifndef __AFL_HAVE_MANUAL_CONTROL 9066 # define __AFL_LOOP(x) true 9067 # endif 9068 9069 static bool WasmLoop(JSContext* cx, unsigned argc, Value* vp) { 9070 CallArgs args = CallArgsFromVp(argc, vp); 9071 RootedObject callee(cx, &args.callee()); 9072 9073 if (args.length() < 1 || args.length() > 2) { 9074 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 9075 return false; 9076 } 9077 9078 if (!args[0].isString()) { 9079 ReportUsageErrorASCII(cx, callee, "First argument must be a String"); 9080 return false; 9081 } 9082 9083 RootedObject importObj(cx); 9084 if (!args.get(1).isUndefined()) { 9085 if (!args.get(1).isObject()) { 9086 ReportUsageErrorASCII(cx, callee, 9087 "Second argument, if present, must be an Object"); 9088 return false; 9089 } 9090 importObj = &args[1].toObject(); 9091 } 9092 9093 RootedString givenPath(cx, args[0].toString()); 9094 RootedString filename(cx, ResolvePath(cx, givenPath, RootRelative)); 9095 if (!filename) { 9096 return false; 9097 } 9098 9099 while (__AFL_LOOP(1000)) { 9100 Rooted<JSObject*> ret(cx, FileAsTypedArray(cx, filename)); 9101 if (!ret) { 9102 return false; 9103 } 9104 9105 Rooted<TypedArrayObject*> typedArray(cx, &ret->as<TypedArrayObject>()); 9106 Rooted<WasmInstanceObject*> instanceObj(cx); 9107 if (!wasm::Eval(cx, typedArray, importObj, &instanceObj)) { 9108 // Clear any pending exceptions, we don't care about them 9109 cx->clearPendingException(); 9110 } 9111 } 9112 9113 # ifdef __AFL_HAVE_MANUAL_CONTROL // to silence unreachable code warning 9114 return true; 9115 # endif 9116 } 9117 #endif // __wasi__ 9118 9119 static constexpr uint32_t DOM_OBJECT_SLOT = 0; 9120 static constexpr uint32_t DOM_OBJECT_SLOT2 = 1; 9121 9122 static const JSClass* GetDomClass(); 9123 9124 static JSObject* GetDOMPrototype(JSContext* cx, JSObject* global); 9125 9126 static void TransplantableDOMObject_finalize(JS::GCContext* gcx, 9127 JSObject* obj) { 9128 // Dummy finalize method so we can swap with background finalized object. 9129 } 9130 9131 static const JSClassOps TransplantableDOMObjectClassOps = { 9132 nullptr, // addProperty 9133 nullptr, // delProperty 9134 nullptr, // enumerate 9135 nullptr, // newEnumerate 9136 nullptr, // resolve 9137 nullptr, // mayResolve 9138 TransplantableDOMObject_finalize, 9139 nullptr, // call 9140 nullptr, // construct 9141 nullptr, 9142 }; 9143 9144 static const JSClass TransplantableDOMObjectClass = { 9145 "TransplantableDOMObject", 9146 JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(1) | 9147 JSCLASS_BACKGROUND_FINALIZE, 9148 &TransplantableDOMObjectClassOps}; 9149 9150 static const JSClass TransplantableDOMProxyObjectClass = 9151 PROXY_CLASS_DEF("TransplantableDOMProxyObject", 9152 JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(1)); 9153 9154 class TransplantableDOMProxyHandler final : public ForwardingProxyHandler { 9155 public: 9156 static const TransplantableDOMProxyHandler singleton; 9157 static const char family; 9158 9159 constexpr TransplantableDOMProxyHandler() : ForwardingProxyHandler(&family) {} 9160 9161 // These two proxy traps are called in |js::DeadProxyTargetValue|, which in 9162 // turn is called when nuking proxies. Because this proxy can temporarily be 9163 // without an object in its private slot, see |EnsureExpandoObject|, the 9164 // default implementation inherited from ForwardingProxyHandler can't be used, 9165 // since it tries to derive the callable/constructible value from the target. 9166 bool isCallable(JSObject* obj) const override { return false; } 9167 bool isConstructor(JSObject* obj) const override { return false; } 9168 9169 // Simplified implementation of |DOMProxyHandler::GetAndClearExpandoObject|. 9170 static JSObject* GetAndClearExpandoObject(JSObject* obj) { 9171 const Value& v = GetProxyPrivate(obj); 9172 if (v.isUndefined()) { 9173 return nullptr; 9174 } 9175 9176 JSObject* expandoObject = &v.toObject(); 9177 SetProxyPrivate(obj, UndefinedValue()); 9178 return expandoObject; 9179 } 9180 9181 // Simplified implementation of |DOMProxyHandler::EnsureExpandoObject|. 9182 static JSObject* EnsureExpandoObject(JSContext* cx, JS::HandleObject obj) { 9183 const Value& v = GetProxyPrivate(obj); 9184 if (v.isObject()) { 9185 return &v.toObject(); 9186 } 9187 MOZ_ASSERT(v.isUndefined()); 9188 9189 JSObject* expando = JS_NewObjectWithGivenProto(cx, nullptr, nullptr); 9190 if (!expando) { 9191 return nullptr; 9192 } 9193 SetProxyPrivate(obj, ObjectValue(*expando)); 9194 return expando; 9195 } 9196 }; 9197 9198 const TransplantableDOMProxyHandler TransplantableDOMProxyHandler::singleton; 9199 const char TransplantableDOMProxyHandler::family = 0; 9200 9201 enum TransplantObjectSlots { 9202 TransplantSourceObject = 0, 9203 }; 9204 9205 static bool TransplantObject(JSContext* cx, unsigned argc, Value* vp) { 9206 CallArgs args = CallArgsFromVp(argc, vp); 9207 RootedFunction callee(cx, &args.callee().as<JSFunction>()); 9208 9209 if (args.length() != 1 || !args[0].isObject()) { 9210 JS_ReportErrorASCII(cx, "transplant() must be called with an object"); 9211 return false; 9212 } 9213 9214 // |newGlobal| needs to be a GlobalObject. 9215 RootedObject newGlobal( 9216 cx, js::CheckedUnwrapDynamic(&args[0].toObject(), cx, 9217 /* stopAtWindowProxy = */ false)); 9218 if (!newGlobal) { 9219 ReportAccessDenied(cx); 9220 return false; 9221 } 9222 if (!JS_IsGlobalObject(newGlobal)) { 9223 JS_ReportErrorNumberASCII( 9224 cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE, 9225 "\"global\" passed to transplant()", "not a global object"); 9226 return false; 9227 } 9228 9229 const Value& reserved = 9230 GetFunctionNativeReserved(callee, TransplantSourceObject); 9231 RootedObject source(cx, CheckedUnwrapStatic(&reserved.toObject())); 9232 if (!source) { 9233 ReportAccessDenied(cx); 9234 return false; 9235 } 9236 if (JS_IsDeadWrapper(source)) { 9237 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); 9238 return false; 9239 } 9240 MOZ_ASSERT(source->getClass()->isDOMClass()); 9241 9242 // The following steps aim to replicate the behavior of UpdateReflectorGlobal 9243 // in dom/bindings/BindingUtils.cpp. In detail: 9244 // 1. Check the recursion depth using checkConservative. 9245 // 2. Enter the target compartment. 9246 // 3. Clone the source object using JS_CloneObject. 9247 // 4. Check if new wrappers can be created if source and target are in 9248 // different compartments. 9249 // 5. Copy all properties from source to a temporary holder object. 9250 // 6. Actually transplant the object. 9251 // 7. And finally copy the properties back to the source object. 9252 // 9253 // As an extension to the algorithm in UpdateReflectorGlobal, we also allow 9254 // to transplant an object into the same compartment as the source object to 9255 // cover all operations supported by JS_TransplantObject. 9256 9257 AutoCheckRecursionLimit recursion(cx); 9258 if (!recursion.checkConservative(cx)) { 9259 return false; 9260 } 9261 9262 bool isProxy = IsProxy(source); 9263 RootedObject expandoObject(cx); 9264 if (isProxy) { 9265 expandoObject = 9266 TransplantableDOMProxyHandler::GetAndClearExpandoObject(source); 9267 } 9268 9269 JSAutoRealm ar(cx, newGlobal); 9270 9271 RootedObject proto(cx); 9272 if (JS::GetClass(source) == GetDomClass()) { 9273 proto = GetDOMPrototype(cx, newGlobal); 9274 } else { 9275 proto = JS::GetRealmObjectPrototype(cx); 9276 } 9277 if (!proto) { 9278 return false; 9279 } 9280 9281 RootedObject target(cx, JS_CloneObject(cx, source, proto)); 9282 if (!target) { 9283 return false; 9284 } 9285 9286 if (JS::GetCompartment(source) != JS::GetCompartment(target) && 9287 !AllowNewWrapper(JS::GetCompartment(source), target)) { 9288 JS_ReportErrorASCII(cx, "Cannot transplant into nuked compartment"); 9289 return false; 9290 } 9291 9292 RootedObject copyFrom(cx, isProxy ? expandoObject : source); 9293 RootedObject propertyHolder(cx, 9294 JS_NewObjectWithGivenProto(cx, nullptr, nullptr)); 9295 if (!propertyHolder) { 9296 return false; 9297 } 9298 9299 if (!JS_CopyOwnPropertiesAndPrivateFields(cx, propertyHolder, copyFrom)) { 9300 return false; 9301 } 9302 9303 JS::SetReservedSlot(target, DOM_OBJECT_SLOT, 9304 JS::GetReservedSlot(source, DOM_OBJECT_SLOT)); 9305 JS::SetReservedSlot(source, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr)); 9306 if (JS::GetClass(source) == GetDomClass()) { 9307 JS::SetReservedSlot(target, DOM_OBJECT_SLOT2, 9308 JS::GetReservedSlot(source, DOM_OBJECT_SLOT2)); 9309 JS::SetReservedSlot(source, DOM_OBJECT_SLOT2, UndefinedValue()); 9310 } 9311 9312 source = JS_TransplantObject(cx, source, target); 9313 if (!source) { 9314 return false; 9315 } 9316 9317 RootedObject copyTo(cx); 9318 if (isProxy) { 9319 copyTo = TransplantableDOMProxyHandler::EnsureExpandoObject(cx, source); 9320 if (!copyTo) { 9321 return false; 9322 } 9323 } else { 9324 copyTo = source; 9325 } 9326 if (!JS_CopyOwnPropertiesAndPrivateFields(cx, copyTo, propertyHolder)) { 9327 return false; 9328 } 9329 9330 args.rval().setUndefined(); 9331 return true; 9332 } 9333 9334 static bool TransplantableObject(JSContext* cx, unsigned argc, Value* vp) { 9335 CallArgs args = CallArgsFromVp(argc, vp); 9336 RootedObject callee(cx, &args.callee()); 9337 9338 if (args.length() > 1) { 9339 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 9340 return false; 9341 } 9342 9343 bool createProxy = false; 9344 RootedObject source(cx); 9345 if (args.length() == 1 && !args[0].isUndefined()) { 9346 if (!args[0].isObject()) { 9347 ReportUsageErrorASCII(cx, callee, "Argument must be an object"); 9348 return false; 9349 } 9350 9351 RootedObject options(cx, &args[0].toObject()); 9352 RootedValue value(cx); 9353 9354 if (!JS_GetProperty(cx, options, "proxy", &value)) { 9355 return false; 9356 } 9357 createProxy = JS::ToBoolean(value); 9358 9359 if (!JS_GetProperty(cx, options, "object", &value)) { 9360 return false; 9361 } 9362 if (!value.isUndefined()) { 9363 if (!value.isObject()) { 9364 ReportUsageErrorASCII(cx, callee, "'object' option must be an object"); 9365 return false; 9366 } 9367 9368 source = &value.toObject(); 9369 if (JS::GetClass(source) != GetDomClass()) { 9370 ReportUsageErrorASCII(cx, callee, "Object not a FakeDOMObject"); 9371 return false; 9372 } 9373 9374 // |source| must be a tenured object to be transplantable. 9375 if (gc::IsInsideNursery(source)) { 9376 JS_GC(cx); 9377 9378 MOZ_ASSERT(!gc::IsInsideNursery(source), 9379 "Live objects should be tenured after one GC, because " 9380 "the nursery has only a single generation"); 9381 } 9382 } 9383 } 9384 9385 if (!source) { 9386 if (!createProxy) { 9387 source = NewBuiltinClassInstance(cx, &TransplantableDOMObjectClass, 9388 TenuredObject); 9389 if (!source) { 9390 return false; 9391 } 9392 9393 JS::SetReservedSlot(source, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr)); 9394 } else { 9395 JSObject* expando = JS_NewPlainObject(cx); 9396 if (!expando) { 9397 return false; 9398 } 9399 RootedValue expandoVal(cx, ObjectValue(*expando)); 9400 9401 ProxyOptions options; 9402 options.setClass(&TransplantableDOMProxyObjectClass); 9403 options.setLazyProto(true); 9404 9405 source = NewProxyObject(cx, &TransplantableDOMProxyHandler::singleton, 9406 expandoVal, nullptr, options); 9407 if (!source) { 9408 return false; 9409 } 9410 9411 SetProxyReservedSlot(source, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr)); 9412 } 9413 } 9414 9415 jsid emptyId = NameToId(cx->names().empty_); 9416 RootedObject transplant( 9417 cx, NewFunctionByIdWithReserved(cx, TransplantObject, 0, 0, emptyId)); 9418 if (!transplant) { 9419 return false; 9420 } 9421 9422 SetFunctionNativeReserved(transplant, TransplantSourceObject, 9423 ObjectValue(*source)); 9424 9425 RootedObject result(cx, JS_NewPlainObject(cx)); 9426 if (!result) { 9427 return false; 9428 } 9429 9430 RootedValue sourceVal(cx, ObjectValue(*source)); 9431 RootedValue transplantVal(cx, ObjectValue(*transplant)); 9432 if (!JS_DefineProperty(cx, result, "object", sourceVal, 0) || 9433 !JS_DefineProperty(cx, result, "transplant", transplantVal, 0)) { 9434 return false; 9435 } 9436 9437 args.rval().setObject(*result); 9438 return true; 9439 } 9440 9441 #ifdef DEBUG 9442 static bool DebugGetQueuedJobs(JSContext* cx, unsigned argc, Value* vp) { 9443 CallArgs args = CallArgsFromVp(argc, vp); 9444 9445 JSObject* jobs = js::GetJobsInInternalJobQueue(cx); 9446 if (!jobs) { 9447 return false; 9448 } 9449 9450 args.rval().setObject(*jobs); 9451 return true; 9452 } 9453 #endif 9454 9455 #ifdef FUZZING_INTERFACES 9456 extern "C" { 9457 size_t gluesmith(uint8_t* data, size_t size, uint8_t* out, size_t maxsize); 9458 } 9459 9460 static bool GetWasmSmithModule(JSContext* cx, unsigned argc, Value* vp) { 9461 CallArgs args = CallArgsFromVp(argc, vp); 9462 RootedObject callee(cx, &args.callee()); 9463 9464 if (args.length() != 1) { 9465 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments"); 9466 return false; 9467 } 9468 9469 if (!args[0].isObject() || !args[0].toObject().is<ArrayBufferObject>()) { 9470 ReportUsageErrorASCII(cx, callee, "Argument must be ArrayBuffer."); 9471 return false; 9472 } 9473 9474 ArrayBufferObject* arrayBuffer = &args[0].toObject().as<ArrayBufferObject>(); 9475 size_t length = arrayBuffer->byteLength(); 9476 uint8_t* data = arrayBuffer->dataPointer(); 9477 9478 const size_t maxModuleSize = 4096; 9479 uint8_t tmp[maxModuleSize]; 9480 9481 size_t outSize = gluesmith(data, length, tmp, maxModuleSize); 9482 if (!outSize) { 9483 JS_ReportErrorASCII(cx, "Generated module is too large."); 9484 return false; 9485 } 9486 9487 JS::Rooted<JSObject*> outArr(cx, JS_NewUint8ClampedArray(cx, outSize)); 9488 if (!outArr) { 9489 return false; 9490 } 9491 9492 { 9493 JS::AutoCheckCannotGC nogc; 9494 bool isShared; 9495 uint8_t* data = JS_GetUint8ClampedArrayData(outArr, &isShared, nogc); 9496 MOZ_RELEASE_ASSERT(!isShared); 9497 memcpy(data, tmp, outSize); 9498 } 9499 9500 args.rval().setObject(*outArr); 9501 return true; 9502 } 9503 9504 #endif 9505 9506 static bool IsValidJSON(JSContext* cx, unsigned argc, Value* vp) { 9507 CallArgs args = CallArgsFromVp(argc, vp); 9508 RootedObject callee(cx, &args.callee()); 9509 9510 if (!args.get(0).isString()) { 9511 ReportUsageErrorASCII(cx, callee, "First argument must be a String"); 9512 return false; 9513 } 9514 9515 JS::Rooted<JSLinearString*> input(cx, args[0].toString()->ensureLinear(cx)); 9516 if (!input) { 9517 return false; 9518 } 9519 9520 bool result; 9521 if (input->hasLatin1Chars()) { 9522 JS::AutoCheckCannotGC nogc; 9523 result = JS::IsValidJSON(input->latin1Chars(nogc), input->length()); 9524 } else { 9525 JS::AutoCheckCannotGC nogc; 9526 result = JS::IsValidJSON(input->twoByteChars(nogc), input->length()); 9527 } 9528 9529 args.rval().setBoolean(result); 9530 return true; 9531 } 9532 9533 // Quick file format for a LZ4 compressed file 9534 static constexpr uint32_t LZ4MagicHeader = -1; 9535 // A magic word and a length field 9536 static constexpr size_t LZ4HeaderSize = sizeof(uint32_t) * 2; 9537 static constexpr size_t LZ4MaxSize = UINT32_MAX; 9538 9539 static bool CompressLZ4(JSContext* cx, unsigned argc, Value* vp) { 9540 CallArgs args = CallArgsFromVp(argc, vp); 9541 RootedObject callee(cx, &args.callee()); 9542 9543 if (!args.get(0).isObject() || 9544 !args.get(0).toObject().is<ArrayBufferObject>()) { 9545 ReportUsageErrorASCII(cx, callee, "First argument must be an ArrayBuffer"); 9546 return false; 9547 } 9548 9549 JS::Rooted<ArrayBufferObject*> bytes( 9550 cx, &args.get(0).toObject().as<ArrayBufferObject>()); 9551 size_t byteLength = bytes->byteLength(); 9552 #ifdef JS_64BIT 9553 if (byteLength > LZ4MaxSize) { 9554 ReportOutOfMemory(cx); 9555 return false; 9556 } 9557 #else 9558 static_assert(LZ4MaxSize == UINT32_MAX, "don't need to check max on 32-bit"); 9559 #endif 9560 9561 // Create a buffer big enough for the header and the max amount of compressed 9562 // bytes. 9563 size_t outputCapacity = 9564 LZ4HeaderSize + mozilla::Compression::LZ4::maxCompressedSize(byteLength); 9565 9566 mozilla::UniquePtr<void, JS::FreePolicy> output(js_malloc(outputCapacity)); 9567 if (!output) { 9568 ReportOutOfMemory(cx); 9569 return false; 9570 } 9571 9572 // Write the magic header word and decompressed size in bytes. 9573 ((uint32_t*)(output.get()))[0] = LZ4MagicHeader; 9574 ((uint32_t*)(output.get()))[1] = byteLength; 9575 9576 // Compress the bytes into the output 9577 char* compressedBytesStart = ((char*)output.get()) + LZ4HeaderSize; 9578 size_t compressedBytesLength = mozilla::Compression::LZ4::compress( 9579 (const char*)bytes->dataPointer(), byteLength, compressedBytesStart); 9580 size_t outputLength = compressedBytesLength + LZ4HeaderSize; 9581 9582 // Create an ArrayBuffer wrapping the compressed bytes 9583 JSObject* outputArrayBuffer = 9584 NewArrayBufferWithContents(cx, outputLength, std::move(output)); 9585 if (!outputArrayBuffer) { 9586 return false; 9587 } 9588 9589 args.rval().setObject(*outputArrayBuffer); 9590 return true; 9591 } 9592 9593 static bool DecompressLZ4(JSContext* cx, unsigned argc, Value* vp) { 9594 CallArgs args = CallArgsFromVp(argc, vp); 9595 RootedObject callee(cx, &args.callee()); 9596 9597 if (!args.get(0).isObject() || 9598 !args.get(0).toObject().is<ArrayBufferObject>()) { 9599 ReportUsageErrorASCII(cx, callee, "First argument must be an ArrayBuffer"); 9600 return false; 9601 } 9602 9603 JS::Rooted<ArrayBufferObject*> bytes( 9604 cx, &args.get(0).toObject().as<ArrayBufferObject>()); 9605 size_t byteLength = bytes->byteLength(); 9606 if (byteLength < LZ4HeaderSize) { 9607 JS_ReportErrorASCII(cx, "Invalid LZ4 buffer"); 9608 return false; 9609 } 9610 9611 // Check the magic header and get the decompressed byte length. 9612 uint32_t magicHeader = ((uint32_t*)(bytes->dataPointer()))[0]; 9613 uint32_t decompressedBytesLength = ((uint32_t*)(bytes->dataPointer()))[1]; 9614 if (magicHeader != LZ4MagicHeader) { 9615 JS_ReportErrorASCII(cx, "Invalid magic header"); 9616 return false; 9617 } 9618 9619 // Allocate a buffer to store the decompressed bytes. 9620 mozilla::UniquePtr<void, JS::FreePolicy> decompressedBytes( 9621 js_malloc(decompressedBytesLength)); 9622 if (!decompressedBytes) { 9623 ReportOutOfMemory(cx); 9624 return false; 9625 } 9626 9627 // Decompress the bytes into the output 9628 const char* compressedBytesStart = 9629 ((const char*)bytes->dataPointer()) + LZ4HeaderSize; 9630 size_t compressedBytesLength = byteLength - LZ4HeaderSize; 9631 size_t actualDecompressedBytesLength = 0; 9632 if (!mozilla::Compression::LZ4::decompress( 9633 compressedBytesStart, compressedBytesLength, 9634 (char*)decompressedBytes.get(), decompressedBytesLength, 9635 &actualDecompressedBytesLength) || 9636 actualDecompressedBytesLength != decompressedBytesLength) { 9637 JS_ReportErrorASCII(cx, "Invalid LZ4 buffer"); 9638 return false; 9639 } 9640 9641 // Create an ArrayBuffer wrapping the decompressed bytes 9642 JSObject* outputArrayBuffer = NewArrayBufferWithContents( 9643 cx, decompressedBytesLength, std::move(decompressedBytes)); 9644 if (!outputArrayBuffer) { 9645 return false; 9646 } 9647 9648 args.rval().setObject(*outputArrayBuffer); 9649 return true; 9650 } 9651 9652 static bool SideEffectfulResolveObject_enumerate( 9653 JSContext* cx, JS::HandleObject obj, JS::MutableHandleIdVector properties, 9654 bool enumerableOnly) { 9655 return properties.append(NameToId(cx->names().test)); 9656 } 9657 9658 static bool SideEffectfulResolveObject_resolve(JSContext* cx, HandleObject obj, 9659 HandleId id, bool* resolvedp) { 9660 *resolvedp = false; 9661 if (JS::dbg::ShouldAvoidSideEffects(cx)) { 9662 JS::ReportUncatchableException(cx); 9663 return false; 9664 } 9665 9666 if (id == NameToId(cx->names().test)) { 9667 RootedValue value(cx, JS::NumberValue(42)); 9668 if (!JS_DefinePropertyById(cx, obj, id, value, JSPROP_ENUMERATE)) { 9669 return false; 9670 } 9671 *resolvedp = true; 9672 } 9673 9674 return true; 9675 } 9676 9677 static const JSClassOps SideEffectfulResolveObject_classOps = { 9678 nullptr, // addProperty 9679 nullptr, // delProperty 9680 nullptr, // enumerate 9681 SideEffectfulResolveObject_enumerate, // newEnumerate 9682 SideEffectfulResolveObject_resolve, // resolve 9683 nullptr, // mayResolve 9684 nullptr, // finalize 9685 nullptr, // call 9686 nullptr, // construct 9687 nullptr, 9688 }; 9689 9690 static const JSClass SideEffectfulResolveObject_class = { 9691 "SideEffectfulResolveObject", 9692 0, 9693 &SideEffectfulResolveObject_classOps, 9694 }; 9695 9696 static bool CreateSideEffectfulResolveObject(JSContext* cx, unsigned argc, 9697 JS::Value* vp) { 9698 CallArgs args = CallArgsFromVp(argc, vp); 9699 9700 RootedObject obj(cx, JS_NewObject(cx, &SideEffectfulResolveObject_class)); 9701 if (!obj) { 9702 return false; 9703 } 9704 9705 args.rval().setObject(*obj); 9706 return true; 9707 } 9708 9709 #ifdef MOZ_EXECUTION_TRACING 9710 9711 static bool EnableExecutionTracing(JSContext* cx, unsigned argc, 9712 JS::Value* vp) { 9713 CallArgs args = CallArgsFromVp(argc, vp); 9714 9715 if (!JS_TracerBeginTracing(cx)) { 9716 return false; 9717 } 9718 9719 args.rval().setUndefined(); 9720 return true; 9721 } 9722 9723 static bool GetExecutionTrace(JSContext* cx, unsigned argc, JS::Value* vp) { 9724 CallArgs args = CallArgsFromVp(argc, vp); 9725 9726 JS::ExecutionTrace trace; 9727 if (!JS_TracerSnapshotTrace(trace)) { 9728 JS_ReportErrorASCII(cx, "Failed to get the execution trace"); 9729 return false; 9730 } 9731 9732 JS::Rooted<JSString*> functionEnterStr(cx, 9733 JS_AtomizeString(cx, "FunctionEnter")); 9734 if (!functionEnterStr) { 9735 return false; 9736 } 9737 JS::Rooted<JSString*> functionLeaveStr(cx, 9738 JS_AtomizeString(cx, "FunctionLeave")); 9739 if (!functionLeaveStr) { 9740 return false; 9741 } 9742 JS::Rooted<JSString*> labelEnterStr(cx, JS_AtomizeString(cx, "LabelEnter")); 9743 if (!labelEnterStr) { 9744 return false; 9745 } 9746 JS::Rooted<JSString*> labelLeaveStr(cx, JS_AtomizeString(cx, "LabelLeave")); 9747 if (!labelLeaveStr) { 9748 return false; 9749 } 9750 JS::Rooted<JSString*> errorStr(cx, JS_AtomizeString(cx, "Error")); 9751 if (!errorStr) { 9752 return false; 9753 } 9754 9755 JS::Rooted<JSString*> interpreterStr(cx, JS_AtomizeString(cx, "interpreter")); 9756 if (!interpreterStr) { 9757 return false; 9758 } 9759 JS::Rooted<JSString*> baselineStr(cx, JS_AtomizeString(cx, "baseline")); 9760 if (!baselineStr) { 9761 return false; 9762 } 9763 JS::Rooted<JSString*> ionStr(cx, JS_AtomizeString(cx, "ion")); 9764 if (!ionStr) { 9765 return false; 9766 } 9767 JS::Rooted<JSString*> wasmStr(cx, JS_AtomizeString(cx, "wasm")); 9768 if (!wasmStr) { 9769 return false; 9770 } 9771 9772 JS::Rooted<JSString*> kindStr(cx, JS_AtomizeString(cx, "kind")); 9773 if (!kindStr) { 9774 return false; 9775 } 9776 JS::Rooted<JS::PropertyKey> kindId(cx, JS::PropertyKey::NonIntAtom(kindStr)); 9777 9778 JS::Rooted<JSString*> implementationStr( 9779 cx, JS_AtomizeString(cx, "implementation")); 9780 if (!implementationStr) { 9781 return false; 9782 } 9783 JS::Rooted<JS::PropertyKey> implementationId( 9784 cx, JS::PropertyKey::NonIntAtom(implementationStr)); 9785 9786 JS::Rooted<JSString*> timeStr(cx, JS_AtomizeString(cx, "time")); 9787 if (!timeStr) { 9788 return false; 9789 } 9790 JS::Rooted<JS::PropertyKey> timeId(cx, JS::PropertyKey::NonIntAtom(timeStr)); 9791 9792 JS::Rooted<JSString*> eventsStr(cx, JS_AtomizeString(cx, "events")); 9793 if (!eventsStr) { 9794 return false; 9795 } 9796 JS::Rooted<JS::PropertyKey> eventsId(cx, 9797 JS::PropertyKey::NonIntAtom(eventsStr)); 9798 9799 JS::Rooted<JSString*> valueBufferStr(cx, JS_AtomizeString(cx, "valueBuffer")); 9800 if (!valueBufferStr) { 9801 return false; 9802 } 9803 JS::Rooted<JS::PropertyKey> valueBufferId( 9804 cx, JS::PropertyKey::NonIntAtom(valueBufferStr)); 9805 9806 JS::Rooted<JSString*> shapeSummariesStr( 9807 cx, JS_AtomizeString(cx, "shapeSummaries")); 9808 if (!shapeSummariesStr) { 9809 return false; 9810 } 9811 JS::Rooted<JS::PropertyKey> shapeSummariesId( 9812 cx, JS::PropertyKey::NonIntAtom(shapeSummariesStr)); 9813 9814 JS::Rooted<JSString*> numPropertiesStr(cx, 9815 JS_AtomizeString(cx, "numProperties")); 9816 if (!numPropertiesStr) { 9817 return false; 9818 } 9819 JS::Rooted<JS::PropertyKey> numPropertiesId( 9820 cx, JS::PropertyKey::NonIntAtom(numPropertiesStr)); 9821 9822 JS::Rooted<JS::PropertyKey> lineNumberId(cx, 9823 NameToId(cx->names().lineNumber)); 9824 JS::Rooted<JS::PropertyKey> columnNumberId( 9825 cx, NameToId(cx->names().columnNumber)); 9826 JS::Rooted<JS::PropertyKey> scriptId(cx, NameToId(cx->names().script)); 9827 JS::Rooted<JS::PropertyKey> nameId(cx, NameToId(cx->names().name)); 9828 JS::Rooted<JS::PropertyKey> labelId(cx, NameToId(cx->names().label)); 9829 JS::Rooted<JS::PropertyKey> valuesId(cx, NameToId(cx->names().values)); 9830 JS::Rooted<JSString*> realmIDStr(cx, JS_AtomizeString(cx, "realmID")); 9831 if (!realmIDStr) { 9832 return false; 9833 } 9834 JS::Rooted<JS::PropertyKey> realmIDId( 9835 cx, JS::PropertyKey::NonIntAtom(realmIDStr)); 9836 9837 JS::Rooted<JSObject*> contextObj(cx); 9838 JS::Rooted<ArrayObject*> eventsArray(cx); 9839 JS::Rooted<JSObject*> shapeSummariesObj(cx); 9840 JS::Rooted<JSObject*> eventObj(cx); 9841 JS::Rooted<JSObject*> shapeSummaryObj(cx); 9842 JS::Rooted<JSString*> str(cx); 9843 9844 JS::Rooted<ArrayObject*> traceArray(cx, NewDenseEmptyArray(cx)); 9845 if (!traceArray) { 9846 return false; 9847 } 9848 9849 for (const auto& context : trace.contexts) { 9850 contextObj = JS_NewPlainObject(cx); 9851 if (!contextObj) { 9852 return false; 9853 } 9854 9855 shapeSummariesObj = JS_NewPlainObject(cx); 9856 if (!shapeSummariesObj) { 9857 return false; 9858 } 9859 9860 eventsArray = NewDenseEmptyArray(cx); 9861 if (!eventsArray) { 9862 return false; 9863 } 9864 9865 for (const auto& event : context.events) { 9866 eventObj = JS_NewPlainObject(cx); 9867 if (!eventObj) { 9868 return false; 9869 } 9870 9871 switch (event.kind) { 9872 case JS::ExecutionTrace::EventKind::FunctionEnter: 9873 str = functionEnterStr; 9874 break; 9875 case JS::ExecutionTrace::EventKind::FunctionLeave: 9876 str = functionLeaveStr; 9877 break; 9878 case JS::ExecutionTrace::EventKind::LabelEnter: 9879 str = labelEnterStr; 9880 break; 9881 case JS::ExecutionTrace::EventKind::LabelLeave: 9882 str = labelLeaveStr; 9883 break; 9884 case JS::ExecutionTrace::EventKind::Error: 9885 str = errorStr; 9886 break; 9887 default: 9888 MOZ_CRASH("Unexpected EventKind"); 9889 break; 9890 } 9891 if (!JS_DefinePropertyById(cx, eventObj, kindId, str, JSPROP_ENUMERATE)) { 9892 return false; 9893 } 9894 9895 if (event.kind == JS::ExecutionTrace::EventKind::FunctionEnter || 9896 event.kind == JS::ExecutionTrace::EventKind::FunctionLeave) { 9897 switch (event.functionEvent.implementation) { 9898 case JS::ExecutionTrace::ImplementationType::Interpreter: 9899 str = interpreterStr; 9900 break; 9901 case JS::ExecutionTrace::ImplementationType::Baseline: 9902 str = baselineStr; 9903 break; 9904 case JS::ExecutionTrace::ImplementationType::Ion: 9905 str = ionStr; 9906 break; 9907 case JS::ExecutionTrace::ImplementationType::Wasm: 9908 str = wasmStr; 9909 break; 9910 default: 9911 MOZ_CRASH("Unexpected ImplementationType"); 9912 break; 9913 } 9914 if (!JS_DefinePropertyById(cx, eventObj, implementationId, str, 9915 JSPROP_ENUMERATE)) { 9916 return false; 9917 } 9918 9919 if (!JS_DefinePropertyById(cx, eventObj, lineNumberId, 9920 event.functionEvent.lineNumber, 9921 JSPROP_ENUMERATE)) { 9922 return false; 9923 } 9924 9925 if (!JS_DefinePropertyById(cx, eventObj, columnNumberId, 9926 event.functionEvent.column, 9927 JSPROP_ENUMERATE)) { 9928 return false; 9929 } 9930 9931 if (auto p = context.scriptUrls.lookup(event.functionEvent.scriptId)) { 9932 str = JS_NewStringCopyUTF8Z( 9933 cx, JS::ConstUTF8CharsZ(trace.stringBuffer.begin() + p->value())); 9934 if (!str) { 9935 return false; 9936 } 9937 9938 if (!JS_DefinePropertyById(cx, eventObj, scriptId, str, 9939 JSPROP_ENUMERATE)) { 9940 return false; 9941 } 9942 } else { 9943 if (!JS_DefinePropertyById(cx, eventObj, scriptId, 9944 JS::UndefinedHandleValue, 9945 JSPROP_ENUMERATE)) { 9946 return false; 9947 } 9948 } 9949 9950 if (!JS_DefinePropertyById( 9951 cx, eventObj, realmIDId, 9952 // We are converting a uint64_t into double which is lossy. But 9953 // this is okay because Firefox makes sure to only use 53 bits 9954 // so it can be converted to and from a JS value without loss of 9955 // precision. Additionally we don't set the realmID in JS shell. 9956 double(event.functionEvent.realmID), JSPROP_ENUMERATE)) { 9957 return false; 9958 } 9959 9960 if (!JS_DefinePropertyById(cx, eventObj, valuesId, 9961 event.functionEvent.values, 9962 JSPROP_ENUMERATE)) { 9963 return false; 9964 } 9965 9966 if (auto p = context.atoms.lookup(event.functionEvent.functionNameId)) { 9967 str = JS_NewStringCopyUTF8Z( 9968 cx, JS::ConstUTF8CharsZ(trace.stringBuffer.begin() + p->value())); 9969 if (!str) { 9970 return false; 9971 } 9972 9973 if (!JS_DefinePropertyById(cx, eventObj, nameId, str, 9974 JSPROP_ENUMERATE)) { 9975 return false; 9976 } 9977 } else { 9978 if (!JS_DefinePropertyById(cx, eventObj, nameId, 9979 JS::UndefinedHandleValue, 9980 JSPROP_ENUMERATE)) { 9981 return false; 9982 } 9983 } 9984 } else if (event.kind == JS::ExecutionTrace::EventKind::LabelEnter || 9985 event.kind == JS::ExecutionTrace::EventKind::LabelLeave) { 9986 str = JS_NewStringCopyUTF8Z( 9987 cx, JS::ConstUTF8CharsZ(trace.stringBuffer.begin() + 9988 event.labelEvent.label)); 9989 if (!str) { 9990 return false; 9991 } 9992 9993 if (!JS_DefinePropertyById(cx, eventObj, labelId, str, 9994 JSPROP_ENUMERATE)) { 9995 return false; 9996 } 9997 } 9998 9999 if (event.kind != JS::ExecutionTrace::EventKind::Error) { 10000 if (!JS_DefinePropertyById(cx, eventObj, timeId, event.time, 10001 JSPROP_ENUMERATE)) { 10002 return false; 10003 } 10004 } 10005 10006 if (!NewbornArrayPush(cx, eventsArray, JS::ObjectValue(*eventObj))) { 10007 return false; 10008 } 10009 } 10010 10011 if (!JS_DefinePropertyById(cx, contextObj, eventsId, eventsArray, 10012 JSPROP_ENUMERATE)) { 10013 return false; 10014 } 10015 10016 for (const auto& shapeSummary : context.shapeSummaries) { 10017 shapeSummaryObj = NewDenseEmptyArray(cx); 10018 if (!shapeSummaryObj) { 10019 return false; 10020 } 10021 10022 // 1 for the class name + num listed properties 10023 const uint32_t realCount = 10024 1 + std::min(shapeSummary.numProperties, 10025 uint32_t(JS::ValueSummary::MAX_COLLECTION_VALUES)); 10026 size_t accumulatedOffset = 0; 10027 for (uint32_t i = 0; i < realCount; i++) { 10028 const char* entry = trace.stringBuffer.begin() + 10029 shapeSummary.stringBufferOffset + accumulatedOffset; 10030 size_t length = strlen(entry); 10031 str = JS_NewStringCopyUTF8Z(cx, JS::ConstUTF8CharsZ(entry, length)); 10032 if (!str) { 10033 return false; 10034 } 10035 accumulatedOffset += length + 1; 10036 10037 if (!NewbornArrayPush(cx, shapeSummaryObj, JS::StringValue(str))) { 10038 return false; 10039 } 10040 } 10041 10042 if (!JS_DefinePropertyById(cx, shapeSummaryObj, numPropertiesId, 10043 shapeSummary.numProperties, 10044 JSPROP_ENUMERATE)) { 10045 return false; 10046 } 10047 10048 if (!JS_DefineElement(cx, shapeSummariesObj, shapeSummary.id, 10049 shapeSummaryObj, JSPROP_ENUMERATE)) { 10050 return false; 10051 } 10052 } 10053 10054 if (!JS_DefinePropertyById(cx, contextObj, shapeSummariesId, 10055 shapeSummariesObj, JSPROP_ENUMERATE)) { 10056 return false; 10057 } 10058 10059 size_t valuesLength = context.valueBuffer.length(); 10060 mozilla::UniquePtr<uint8_t[], JS::FreePolicy> valueBuffer( 10061 js_pod_malloc<uint8_t>(valuesLength)); 10062 if (!valueBuffer) { 10063 return false; 10064 } 10065 10066 memcpy(valueBuffer.get(), context.valueBuffer.begin(), valuesLength); 10067 JS::Rooted<JSObject*> valuesArrayBuffer( 10068 cx, JS::NewArrayBufferWithContents(cx, valuesLength, 10069 std::move(valueBuffer))); 10070 if (!valuesArrayBuffer) { 10071 return false; 10072 } 10073 10074 if (!JS_DefinePropertyById(cx, contextObj, valueBufferId, valuesArrayBuffer, 10075 JSPROP_ENUMERATE)) { 10076 return false; 10077 } 10078 10079 if (!NewbornArrayPush(cx, traceArray, JS::ObjectValue(*contextObj))) { 10080 return false; 10081 } 10082 } 10083 10084 args.rval().setObject(*traceArray); 10085 return true; 10086 } 10087 10088 static bool DisableExecutionTracing(JSContext* cx, unsigned argc, 10089 JS::Value* vp) { 10090 CallArgs args = CallArgsFromVp(argc, vp); 10091 10092 if (!JS_TracerEndTracing(cx)) { 10093 return false; 10094 } 10095 10096 args.rval().setUndefined(); 10097 return true; 10098 } 10099 10100 #endif // MOZ_EXECUTION_TRACING 10101 10102 // clang-format off 10103 static const JSFunctionSpecWithHelp shell_functions[] = { 10104 JS_FN_HELP("options", Options, 0, 0, 10105 "options([option ...])", 10106 " Get or toggle JavaScript options."), 10107 10108 JS_FN_HELP("load", Load, 1, 0, 10109 "load(['foo.js' ...])", 10110 " Load files named by string arguments. Filename is relative to the\n" 10111 " current working directory."), 10112 10113 JS_FN_HELP("loadRelativeToScript", LoadScriptRelativeToScript, 1, 0, 10114 "loadRelativeToScript(['foo.js' ...])", 10115 " Load files named by string arguments. Filename is relative to the\n" 10116 " calling script."), 10117 10118 JS_FN_HELP("evaluate", Evaluate, 2, 0, 10119 "evaluate(code[, options])", 10120 " Evaluate code as though it were the contents of a file.\n" 10121 " options is an optional object that may have these properties:\n" 10122 " isRunOnce: use the isRunOnce compiler option (default: false)\n" 10123 " noScriptRval: use the no-script-rval compiler option (default: false)\n" 10124 " fileName: filename for error messages and debug info\n" 10125 " skipFileNameValidation: skip the filename-validation callback\n" 10126 " lineNumber: starting line number for error messages and debug info\n" 10127 " columnNumber: starting column number for error messages and debug info\n" 10128 " global: global in which to execute the code\n" 10129 " newContext: if true, create and use a new cx (default: false)\n" 10130 " catchTermination: if true, catch termination (failure without\n" 10131 " an exception value, as for slow scripts or out-of-memory)\n" 10132 " and return 'terminated'\n" 10133 " element: if present with value |v|, convert |v| to an object |o| and\n" 10134 " mark the source as being attached to the DOM element |o|. If the\n" 10135 " property is omitted or |v| is null, don't attribute the source to\n" 10136 " any DOM element.\n" 10137 " elementAttributeName: if present and not undefined, the name of\n" 10138 " property of 'element' that holds this code. This is what\n" 10139 " Debugger.Source.prototype.elementAttributeName returns.\n" 10140 " sourceMapURL: if present with value |v|, convert |v| to a string, and\n" 10141 " provide that as the code's source map URL. If omitted, attach no\n" 10142 " source map URL to the code (although the code may provide one itself,\n" 10143 " via a //#sourceMappingURL comment).\n" 10144 " sourceIsLazy: if present and true, indicates that, after compilation, \n" 10145 " script source should not be cached by the JS engine and should be \n" 10146 " lazily loaded from the embedding as-needed.\n" 10147 " forceFullParse: if present and true, disable syntax-parse.\n" 10148 " loadBytecode: if true, and if the source is a CacheEntryObject,\n" 10149 " the bytecode would be loaded and decoded from the cache entry instead\n" 10150 " of being parsed, then it would be executed as usual.\n" 10151 " saveBytecodeWithDelazifications: if true, and if the source is a\n" 10152 " CacheEntryObject, the delazifications are collected during the\n" 10153 " execution, and encoded after that, and saved into the cache entry.\n" 10154 " execute: if false, do not execute the script, but do parse and/or\n" 10155 " transcode.\n" 10156 " assertEqBytecode: if true, and if both loadBytecode and either\n" 10157 " saveBytecodeWithDelazifications is true, then the loaded\n" 10158 " bytecode and the encoded bytecode are compared.\n" 10159 " and an assertion is raised if they differ.\n" 10160 " envChainObject: object to put on the scope chain, with its fields added\n" 10161 " as var bindings, akin to how elements are added to the environment in\n" 10162 " event handlers in Gecko.\n" 10163 " supportUnscopables: if true, support Symbol.unscopables lookups for\n" 10164 " envChainObject, similar to (syntactic) with-statements.\n" 10165 ), 10166 10167 JS_FN_HELP("run", Run, 1, 0, 10168 "run('foo.js')", 10169 " Run the file named by the first argument, returning the number of\n" 10170 " of milliseconds spent compiling and executing it."), 10171 10172 JS_FN_HELP("readline", ReadLine, 0, 0, 10173 "readline()", 10174 " Read a single line from stdin."), 10175 10176 JS_FN_HELP("readlineBuf", ReadLineBuf, 1, 0, 10177 "readlineBuf([ buf ])", 10178 " Emulate readline() on the specified string. The first call with a string\n" 10179 " argument sets the source buffer. Subsequent calls without an argument\n" 10180 " then read from this buffer line by line.\n"), 10181 10182 JS_FN_HELP("print", Print, 0, 0, 10183 "print([exp ...])", 10184 " Evaluate and print expressions to stdout."), 10185 10186 JS_FN_HELP("printErr", PrintErr, 0, 0, 10187 "printErr([exp ...])", 10188 " Evaluate and print expressions to stderr."), 10189 10190 JS_FN_HELP("putstr", PutStr, 0, 0, 10191 "putstr([exp])", 10192 " Evaluate and print expression without newline."), 10193 10194 JS_FN_HELP("dateNow", Now, 0, 0, 10195 "dateNow()", 10196 " Return the current time with sub-ms precision."), 10197 10198 JS_FN_HELP("help", Help, 0, 0, 10199 "help([function or interface object or /pattern/])", 10200 " Display usage and help messages."), 10201 10202 JS_FN_HELP("quit", Quit, 0, 0, 10203 "quit()", 10204 " Quit the shell."), 10205 10206 JS_FN_HELP("assertEq", AssertEq, 2, 0, 10207 "assertEq(actual, expected[, msg])", 10208 " Throw if the first two arguments are not the same (both +0 or both -0,\n" 10209 " both NaN, or non-zero and ===)."), 10210 10211 JS_FN_HELP("startTimingMutator", StartTimingMutator, 0, 0, 10212 "startTimingMutator()", 10213 " Start accounting time to mutator vs GC."), 10214 10215 JS_FN_HELP("stopTimingMutator", StopTimingMutator, 0, 0, 10216 "stopTimingMutator()", 10217 " Stop accounting time to mutator vs GC and dump the results."), 10218 10219 JS_FN_HELP("throwError", ThrowError, 0, 0, 10220 "throwError()", 10221 " Throw an error from JS_ReportError."), 10222 10223 JS_FN_HELP("createErrorReport", CreateErrorReport, 1, 0, 10224 "createErrorReport(value)", 10225 " Create an JS::ErrorReportBuilder object from the given value and serialize\n" 10226 " to an object."), 10227 10228 #if defined(DEBUG) || defined(JS_JITSPEW) 10229 JS_FN_HELP("disassemble", DisassembleToString, 1, 0, 10230 "disassemble([fun/code])", 10231 " Return the disassembly for the given function or code.\n" 10232 " All disassembly functions take these options as leading string arguments:\n" 10233 " \"-r\" (disassemble recursively)\n" 10234 " \"-l\" (show line numbers)\n" 10235 " \"-S\" (omit source notes)"), 10236 10237 JS_FN_HELP("dis", Disassemble, 1, 0, 10238 "dis([fun/code])", 10239 " Disassemble functions into bytecodes."), 10240 10241 JS_FN_HELP("disfile", DisassFile, 1, 0, 10242 "disfile('foo.js')", 10243 " Disassemble script file into bytecodes.\n"), 10244 10245 JS_FN_HELP("dissrc", DisassWithSrc, 1, 0, 10246 "dissrc([fun/code])", 10247 " Disassemble functions with source lines."), 10248 10249 JS_FN_HELP("notes", Notes, 1, 0, 10250 "notes([fun])", 10251 " Show source notes for functions."), 10252 10253 JS_FN_HELP("stackDump", StackDump, 3, 0, 10254 "stackDump(showArgs, showLocals, showThisProps)", 10255 " Tries to print a lot of information about the current stack. \n" 10256 " Similar to the DumpJSStack() function in the browser."), 10257 10258 #endif 10259 10260 JS_FN_HELP("getslx", GetSLX, 1, 0, 10261 "getslx(obj)", 10262 " Get script line extent."), 10263 10264 JS_FN_HELP("evalcx", EvalInContext, 1, 0, 10265 "evalcx(s[, o])", 10266 " Evaluate s in optional sandbox object o.\n" 10267 " if (s == '' && !o) return new o with eager standard classes\n" 10268 " if (s == 'lazy' && !o) return new o with lazy standard classes"), 10269 10270 JS_FN_HELP("evalInWorker", EvalInWorker, 1, 0, 10271 "evalInWorker(str)", 10272 " Evaluate 'str' in a separate thread with its own runtime.\n"), 10273 10274 JS_FN_HELP("getSharedObject", GetSharedObject, 0, 0, 10275 "getSharedObject()", 10276 " Retrieve the shared object from the cross-worker mailbox.\n" 10277 " The object retrieved may not be identical to the object that was\n" 10278 " installed, but it references the same shared memory.\n" 10279 " getSharedObject performs an ordering memory barrier.\n"), 10280 10281 JS_FN_HELP("setSharedObject", SetSharedObject, 0, 0, 10282 "setSharedObject(obj)", 10283 " Install the shared object in the cross-worker mailbox. The object\n" 10284 " may be null. setSharedObject performs an ordering memory barrier.\n"), 10285 10286 JS_FN_HELP("getSharedArrayBuffer", GetSharedObject, 0, 0, 10287 "getSharedArrayBuffer()", 10288 " Obsolete alias for getSharedObject().\n"), 10289 10290 JS_FN_HELP("setSharedArrayBuffer", SetSharedObject, 0, 0, 10291 "setSharedArrayBuffer(obj)", 10292 " Obsolete alias for setSharedObject(obj).\n"), 10293 10294 JS_FN_HELP("shapeOf", ShapeOf, 1, 0, 10295 "shapeOf(obj)", 10296 " Get the shape of obj (an implementation detail)."), 10297 10298 #ifdef DEBUG 10299 JS_FN_HELP("arrayInfo", ArrayInfo, 1, 0, 10300 "arrayInfo(a1, a2, ...)", 10301 " Report statistics about arrays."), 10302 #endif 10303 10304 JS_FN_HELP("sleep", Sleep_fn, 1, 0, 10305 "sleep(dt)", 10306 " Sleep for dt seconds."), 10307 10308 JS_FN_HELP("parseModule", ParseModule, 3, 0, 10309 "parseModule(code, 'filename', 'js' | 'json' | 'bytes')", 10310 " Parses source text as a JS module ('js', this is the default) or a JSON" 10311 " module ('json') or bytes module ('bytes') and returns a ModuleObject wrapper object."), 10312 10313 JS_FN_HELP("instantiateModuleStencil", InstantiateModuleStencil, 1, 0, 10314 "instantiateModuleStencil(stencil, [options])", 10315 " Instantiates the given stencil as module, and return the module object."), 10316 10317 JS_FN_HELP("instantiateModuleStencilXDR", InstantiateModuleStencilXDR, 1, 0, 10318 "instantiateModuleStencilXDR(stencil, [options])", 10319 " Reads the given stencil XDR object, instantiates the stencil as module, and" 10320 " return the module object."), 10321 10322 JS_FN_HELP("registerModule", RegisterModule, 2, 0, 10323 "registerModule(specifier, module)", 10324 " Register a module with the module loader, so that subsequent import from\n" 10325 " |specifier| will resolve to |module|. Returns |module|."), 10326 10327 JS_FN_HELP("clearModules", ClearModules, 0, 0, 10328 "clearModules()", 10329 " Clear knowledge of all loaded modules."), 10330 10331 JS_FN_HELP("moduleLink", ModuleLink, 1, 0, 10332 "moduleLink(moduleOjbect)", 10333 " Link a module graph, performing the spec's Link method."), 10334 10335 JS_FN_HELP("moduleEvaluate", ModuleEvaluate, 1, 0, 10336 "moduleEvaluate(moduleOjbect)", 10337 " Evaluate a module graph, performing the spec's Evaluate method."), 10338 10339 JS_FN_HELP("getModuleEnvironmentNames", GetModuleEnvironmentNames, 1, 0, 10340 "getModuleEnvironmentNames(module)", 10341 " Get the list of a module environment's bound names for a specified module.\n"), 10342 10343 JS_FN_HELP("getModuleEnvironmentValue", GetModuleEnvironmentValue, 2, 0, 10344 "getModuleEnvironmentValue(module, name)", 10345 " Get the value of a bound name in a module environment.\n"), 10346 10347 JS_FN_HELP("dumpStencil", DumpStencil, 1, 0, 10348 "dumpStencil(code, [options])", 10349 " Parses a string and returns string that represents stencil.\n" 10350 " If present, |options| may have properties saying how the code should be\n" 10351 " compiled:\n" 10352 " module: if present and true, compile the source as module.\n" 10353 " CompileOptions-related properties of evaluate function's option can also\n" 10354 " be used."), 10355 10356 JS_FN_HELP("parse", Parse, 1, 0, 10357 "parse(code, [options])", 10358 " Parses a string, potentially throwing. If present, |options| may\n" 10359 " have properties saying how the code should be compiled:\n" 10360 " module: if present and true, compile the source as module.\n" 10361 " CompileOptions-related properties of evaluate function's option can also\n" 10362 " be used. except forceFullParse. This function always use full parse."), 10363 10364 JS_FN_HELP("syntaxParse", SyntaxParse, 1, 0, 10365 "syntaxParse(code)", 10366 " Check the syntax of a string, returning success value"), 10367 10368 JS_FN_HELP("offThreadCompileModuleToStencil", OffThreadCompileModuleToStencil, 1, 0, 10369 "offThreadCompileModuleToStencil(code[, options])", 10370 " Compile |code| on a helper thread, returning a job ID. To wait for the\n" 10371 " compilation to finish and and get the module stencil object call\n" 10372 " |finishOffThreadStencil| passing the job ID."), 10373 10374 JS_FN_HELP("offThreadDecodeStencil", OffThreadDecodeStencil, 1, 0, 10375 "offThreadDecodeStencil(cacheEntry[, options])", 10376 " Decode |code| on a helper thread, returning a job ID. To wait for the\n" 10377 " decoding to finish and run the code, call |finishOffThreadStencil| passing\n" 10378 " the job ID. If present, |options| may have properties saying how the code\n" 10379 " should be compiled (see also offThreadCompileToStencil)."), 10380 10381 JS_FN_HELP("offThreadCompileToStencil", OffThreadCompileToStencil, 1, 0, 10382 "offThreadCompileToStencil(code[, options])", 10383 " Compile |code| on a helper thread, returning a job ID. To wait for the\n" 10384 " compilation to finish and get the stencil object, call\n" 10385 " |finishOffThreadStencil| passing the job ID. If present, \n" 10386 " |options| may have properties saying how the code should be compiled:\n" 10387 " noScriptRval: use the no-script-rval compiler option (default: false)\n" 10388 " fileName: filename for error messages and debug info\n" 10389 " lineNumber: starting line number for error messages and debug info\n" 10390 " columnNumber: starting column number for error messages and debug info\n" 10391 " element: if present with value |v|, convert |v| to an object |o| and\n" 10392 " mark the source as being attached to the DOM element |o|. If the\n" 10393 " property is omitted or |v| is null, don't attribute the source to\n" 10394 " any DOM element.\n" 10395 " elementAttributeName: if present and not undefined, the name of\n" 10396 " property of 'element' that holds this code. This is what\n" 10397 " Debugger.Source.prototype.elementAttributeName returns."), 10398 10399 JS_FN_HELP("finishOffThreadStencil", FinishOffThreadStencil, 0, 0, 10400 "finishOffThreadStencil([jobID])", 10401 " Wait for an off-thread compilation or decode job to complete. The job ID\n" 10402 " can be ommitted if there is only one job pending. If an error occurred,\n" 10403 " throw the appropriate exception; otherwise, return the stencil object," 10404 " that can be passed to |evalStencil|."), 10405 10406 JS_FN_HELP("timeout", Timeout, 1, 0, 10407 "timeout([seconds], [func])", 10408 " Get/Set the limit in seconds for the execution time for the current context.\n" 10409 " When the timeout expires the current interrupt callback is invoked.\n" 10410 " The timeout is used just once. If the callback returns a falsy value, the\n" 10411 " script is aborted. A negative value for seconds (this is the default) cancels\n" 10412 " any pending timeout.\n" 10413 " If a second argument is provided, it is installed as the interrupt handler,\n" 10414 " exactly as if by |setInterruptCallback|.\n"), 10415 10416 JS_FN_HELP("interruptIf", InterruptIf, 1, 0, 10417 "interruptIf(cond)", 10418 " Requests interrupt callback if cond is true. If a callback function is set via\n" 10419 " |timeout| or |setInterruptCallback|, it will be called. No-op otherwise."), 10420 10421 JS_FN_HELP("invokeInterruptCallback", InvokeInterruptCallbackWrapper, 0, 0, 10422 "invokeInterruptCallback(fun)", 10423 " Forcefully set the interrupt flag and invoke the interrupt handler. If a\n" 10424 " callback function is set via |timeout| or |setInterruptCallback|, it will\n" 10425 " be called. Before returning, fun is called with the return value of the\n" 10426 " interrupt handler."), 10427 10428 JS_FN_HELP("setInterruptCallback", SetInterruptCallback, 1, 0, 10429 "setInterruptCallback(func)", 10430 " Sets func as the interrupt callback function.\n" 10431 " func must be a function or a string (which will be evaluated in a new\n" 10432 " global). Only strings are supported in fuzzing builds.\n" 10433 " Calling this function will replace any callback set by |timeout|.\n" 10434 " If the callback returns a falsy value, the script is aborted.\n"), 10435 10436 JS_FN_HELP("setPrefValue", SetPrefValue, 2, 0, 10437 "setPrefValue(name, value)", 10438 " Set the value of the JS pref with the given name."), 10439 10440 JS_FN_HELP("setJitCompilerOption", SetJitCompilerOption, 2, 0, 10441 "setJitCompilerOption(<option>, <number>)", 10442 " Set a compiler option indexed in JSCompileOption enum to a number.\n"), 10443 10444 #ifdef DEBUG 10445 JS_FN_HELP("interruptRegexp", InterruptRegexp, 2, 0, 10446 "interruptRegexp(<regexp>, <string>)", 10447 " Interrrupt the execution of regular expression.\n"), 10448 #endif 10449 JS_FN_HELP("checkRegExpSyntax", CheckRegExpSyntax, 1, 0, 10450 "checkRegExpSyntax(<string>)", 10451 " Return undefined if the string parses as a RegExp. If the string does not\n" 10452 " parse correctly, return the SyntaxError that occurred."), 10453 10454 JS_FN_HELP("enableLastWarning", EnableLastWarning, 0, 0, 10455 "enableLastWarning()", 10456 " Enable storing the last warning."), 10457 JS_FN_HELP("disableLastWarning", DisableLastWarning, 0, 0, 10458 "disableLastWarning()", 10459 " Disable storing the last warning."), 10460 10461 JS_FN_HELP("getLastWarning", GetLastWarning, 0, 0, 10462 "getLastWarning()", 10463 " Returns an object that represents the last warning."), 10464 10465 JS_FN_HELP("clearLastWarning", ClearLastWarning, 0, 0, 10466 "clearLastWarning()", 10467 " Clear the last warning."), 10468 10469 JS_FN_HELP("elapsed", Elapsed, 0, 0, 10470 "elapsed()", 10471 " Execution time elapsed for the current thread."), 10472 10473 JS_FN_HELP("decompileFunction", DecompileFunction, 1, 0, 10474 "decompileFunction(func)", 10475 " Decompile a function."), 10476 10477 JS_FN_HELP("decompileThis", DecompileThisScript, 0, 0, 10478 "decompileThis()", 10479 " Decompile the currently executing script."), 10480 10481 JS_FN_HELP("valueToSource", ValueToSource, 1, 0, 10482 "valueToSource(value)", 10483 " Format a value for inspection."), 10484 10485 JS_FN_HELP("thisFilename", ThisFilename, 0, 0, 10486 "thisFilename()", 10487 " Return the filename of the current script"), 10488 10489 JS_FN_HELP("newGlobal", NewGlobal, 1, 0, 10490 "newGlobal([options])", 10491 " Return a new global object/realm. The new global is created in the\n" 10492 " 'newGlobal' function object's compartment and zone, unless the\n" 10493 " '--more-compartments' command-line flag was given, in which case new\n" 10494 " globals get a fresh compartment and zone. If options is given, it may\n" 10495 " have any of the following properties:\n" 10496 " sameCompartmentAs: If an object, the global will be in the same\n" 10497 " compartment and zone as the given object.\n" 10498 " sameZoneAs: The global will be in a new compartment in the same zone\n" 10499 " as the given object.\n" 10500 " newCompartment: If true, the global will always be created in a new\n" 10501 " compartment and zone.\n" 10502 " invisibleToDebugger: If true, the global will be invisible to the\n" 10503 " debugger (default false)\n" 10504 " discardSource: If true, discard source after compiling a script\n" 10505 " (default false).\n" 10506 " useWindowProxy: the global will be created with a WindowProxy attached. In this\n" 10507 " case, the WindowProxy will be returned.\n" 10508 " freezeBuiltins: certain builtin constructors will be frozen when created and\n" 10509 " their prototypes will be sealed. These constructors will be defined on the\n" 10510 " global as non-configurable and non-writable.\n" 10511 " immutablePrototype: whether the global's prototype is immutable.\n" 10512 " principal: if present, its value converted to a number must be an\n" 10513 " integer that fits in 32 bits; use that as the new realm's\n" 10514 " principal. Shell principals are toys, meant only for testing; one\n" 10515 " shell principal subsumes another if its set bits are a superset of\n" 10516 " the other's. Thus, a principal of 0 subsumes nothing, while a\n" 10517 " principals of ~0 subsumes all other principals. The absence of a\n" 10518 " principal is treated as if its bits were 0xffff, for subsumption\n" 10519 " purposes. If this property is omitted, supply no principal.\n" 10520 " systemPrincipal: If true, use the shell's trusted principals for the\n" 10521 " new realm. This creates a realm that's marked as a 'system' realm."), 10522 10523 JS_FN_HELP("nukeAllCCWs", NukeAllCCWs, 0, 0, 10524 "nukeAllCCWs()", 10525 " Like nukeCCW, but for all CrossCompartmentWrappers targeting the current realm."), 10526 10527 JS_FN_HELP("recomputeWrappers", RecomputeWrappers, 2, 0, 10528 "recomputeWrappers([src, [target]])", 10529 " Recompute all cross-compartment wrappers. src and target are both optional\n" 10530 " and can be used to filter source or target compartments: the unwrapped\n" 10531 " object's compartment is used as CompartmentFilter.\n"), 10532 10533 JS_FN_HELP("dumpObjectWrappers", DumpObjectWrappers, 2, 0, 10534 "dumpObjectWrappers()", 10535 " Print information about cross-compartment object wrappers.\n"), 10536 10537 JS_FN_HELP("wrapWithProto", WrapWithProto, 2, 0, 10538 "wrapWithProto(obj)", 10539 " Wrap an object into a noop wrapper with prototype semantics."), 10540 10541 JS_FN_HELP("createExternalArrayBuffer", CreateExternalArrayBuffer, 1, 0, 10542 "createExternalArrayBuffer(size)", 10543 " Create an array buffer that has external data of size."), 10544 10545 JS_FN_HELP("createMappedArrayBuffer", CreateMappedArrayBuffer, 1, 0, 10546 "createMappedArrayBuffer(filename, [offset, [size]])", 10547 " Create an array buffer that mmaps the given file."), 10548 10549 JS_FN_HELP("createUserArrayBuffer", CreateUserArrayBuffer, 1, 0, 10550 "createUserArrayBuffer(size)", 10551 " Create an array buffer that uses user-controlled memory."), 10552 10553 JS_FN_HELP("addPromiseReactions", AddPromiseReactions, 3, 0, 10554 "addPromiseReactions(promise, onResolve, onReject)", 10555 " Calls the JS::AddPromiseReactions JSAPI function with the given arguments."), 10556 10557 JS_FN_HELP("ignoreUnhandledRejections", IgnoreUnhandledRejections, 0, 0, 10558 "ignoreUnhandledRejections()", 10559 " By default, js shell tracks unhandled promise rejections and reports\n" 10560 " them at the end of the exectuion. If a testcase isn't interested\n" 10561 " in those rejections, call this to stop tracking and reporting."), 10562 10563 JS_FN_HELP("getMaxArgs", GetMaxArgs, 0, 0, 10564 "getMaxArgs()", 10565 " Return the maximum number of supported args for a call."), 10566 10567 JS_FN_HELP("createIsHTMLDDA", CreateIsHTMLDDA, 0, 0, 10568 "createIsHTMLDDA()", 10569 " Return an object |obj| that \"looks like\" the |document.all| object in\n" 10570 " browsers in certain ways: |typeof obj === \"undefined\"|, |obj == null|\n" 10571 " and |obj == undefined| (vice versa for !=), |ToBoolean(obj) === false|,\n" 10572 " and when called with no arguments or the single argument \"\" returns\n" 10573 " null. (Calling |obj| any other way crashes or throws an exception.)\n" 10574 " This function implements the exact requirements of the $262.IsHTMLDDA\n" 10575 " property in test262."), 10576 10577 JS_FN_HELP("cacheEntry", CacheEntry, 1, 0, 10578 "cacheEntry(code)", 10579 " Return a new opaque object which emulates a cache entry of a script. This\n" 10580 " object encapsulates the code and its cached content. The cache entry is filled\n" 10581 " and read by the \"evaluate\" function by using it in place of the source, and\n" 10582 " by setting \"saveBytecodeWithDelazifications\" and \"loadBytecode\" options."), 10583 10584 JS_FN_HELP("streamCacheEntry", StreamCacheEntryObject::construct, 1, 0, 10585 "streamCacheEntry(buffer)", 10586 " Create a shell-only object that holds wasm bytecode and can be streaming-\n" 10587 " compiled and cached by WebAssembly.{compile,instantiate}Streaming(). On a\n" 10588 " second compilation of the same cache entry, the cached code will be used."), 10589 10590 JS_FN_HELP("printProfilerEvents", PrintProfilerEvents, 0, 0, 10591 "printProfilerEvents()", 10592 " Register a callback with the profiler that prints javascript profiler events\n" 10593 " to stderr. Callback is only registered if profiling is enabled."), 10594 10595 JS_FN_HELP("enableSingleStepProfiling", EnableSingleStepProfiling, 0, 0, 10596 "enableSingleStepProfiling()", 10597 " This function will fail on platforms that don't support single-step profiling\n" 10598 " (currently ARM and MIPS64 support it). When enabled, at every instruction a\n" 10599 " backtrace will be recorded and stored in an array. Adjacent duplicate backtraces\n" 10600 " are discarded."), 10601 10602 JS_FN_HELP("disableSingleStepProfiling", DisableSingleStepProfiling, 0, 0, 10603 "disableSingleStepProfiling()", 10604 " Return the array of backtraces recorded by enableSingleStepProfiling."), 10605 10606 JS_FN_HELP("enableGeckoProfiling", EnableGeckoProfiling, 0, 0, 10607 "enableGeckoProfiling()", 10608 " Enables Gecko Profiler instrumentation and corresponding assertions, with slow\n" 10609 " assertions disabled.\n"), 10610 10611 JS_FN_HELP("enableGeckoProfilingWithSlowAssertions", EnableGeckoProfilingWithSlowAssertions, 0, 0, 10612 "enableGeckoProfilingWithSlowAssertions()", 10613 " Enables Gecko Profiler instrumentation and corresponding assertions, with slow\n" 10614 " assertions enabled.\n"), 10615 10616 JS_FN_HELP("disableGeckoProfiling", DisableGeckoProfiling, 0, 0, 10617 "disableGeckoProfiling()", 10618 " Disables Gecko Profiler instrumentation"), 10619 10620 JS_FN_HELP("getGeckoProfilingScriptSourcesCount", GetGeckoProfilingScriptSourcesCount, 0, 0, 10621 "getGeckoProfilingScriptSourcesCount()", 10622 " Returns the number of script sources registered with the Gecko Profiler"), 10623 10624 JS_FN_HELP("isLatin1", IsLatin1, 1, 0, 10625 "isLatin1(s)", 10626 " Return true iff the string's characters are stored as Latin1."), 10627 10628 JS_FN_HELP("stackPointerInfo", StackPointerInfo, 0, 0, 10629 "stackPointerInfo()", 10630 " Return an int32 value which corresponds to the offset of the latest stack\n" 10631 " pointer, such that one can take the differences of 2 to estimate a frame-size."), 10632 10633 JS_FN_HELP("globalOfFirstJobInQueue", GlobalOfFirstJobInQueue, 0, 0, 10634 "globalOfFirstJobInQueue()", 10635 " Returns the global of the first item in the job queue. Throws an exception\n" 10636 " if the queue is empty.\n"), 10637 10638 JS_FN_HELP("drainJobQueue", DrainJobQueue, 0, 0, 10639 "drainJobQueue()", 10640 "Take jobs from the shell's job queue in FIFO order and run them until the\n" 10641 "queue is empty.\n"), 10642 10643 JS_FN_HELP("setTimeout", SetTimeout, 1, 0, 10644 "setTimeout(functionRef, delay)", 10645 "Executes functionRef after the specified delay, like the Web builtin." 10646 "This is currently restricted to require a delay of 0 and will not accept" 10647 "any extra arguments. No return value is given and there is no clearTimeout."), 10648 10649 JS_FN_HELP("setCSPEnabled", SetCSPEnabled, 1, 0, 10650 "setCSPEnabled(enabled)", 10651 "Enable or disable Content Security Policy restrictions for eval() and Function().\n" 10652 "When enabled (true), string compilation will be blocked. When disabled (false),\n" 10653 "string compilation is allowed. Defaults to disabled."), 10654 10655 JS_FN_HELP("setPromiseRejectionTrackerCallback", SetPromiseRejectionTrackerCallback, 1, 0, 10656 "setPromiseRejectionTrackerCallback()", 10657 "Sets the callback to be invoked whenever a Promise rejection is unhandled\n" 10658 "or a previously-unhandled rejection becomes handled."), 10659 10660 JS_FN_HELP("dumpScopeChain", DumpScopeChain, 1, 0, 10661 "dumpScopeChain(obj)", 10662 " Prints the scope chain of an interpreted function or a module."), 10663 10664 JS_FN_HELP("blackRoot", EnsureBlackRoot, 0, 0, 10665 "blackRoot()", 10666 " Return an array in the current compartment whose elements will be marked\n" 10667 " as black roots by the GC."), 10668 10669 JS_FN_HELP("grayRoot", EnsureGrayRoot, 0, 0, 10670 "grayRoot()", 10671 " Return an array in the current compartment whose elements will be marked\n" 10672 " as gray roots by the GC."), 10673 10674 JS_FN_HELP("addMarkObservers", AddMarkObservers, 1, 0, 10675 "addMarkObservers(array_of_objects)", 10676 " Register an array of objects whose mark bits will be tested by calls to\n" 10677 " getMarks. The objects will be in calling compartment. Objects from\n" 10678 " multiple compartments may be monitored by calling this function in\n" 10679 " different compartments."), 10680 10681 JS_FN_HELP("clearMarkObservers", ClearMarkObservers, 1, 0, 10682 "clearMarkObservers()", 10683 " Clear out the list of objects whose mark bits will be tested.\n"), 10684 10685 JS_FN_HELP("getMarks", GetMarks, 0, 0, 10686 "getMarks()", 10687 " Return an array of strings representing the current state of the mark\n" 10688 " bits ('gray' or 'black', or 'dead' if the object has been collected)\n" 10689 " for the objects registered via addMarkObservers. Note that some of the\n" 10690 " objects tested may be from different compartments than the one in which\n" 10691 " this function runs."), 10692 10693 JS_FN_HELP("bindToAsyncStack", BindToAsyncStack, 2, 0, 10694 "bindToAsyncStack(fn, { stack, cause, explicit })", 10695 " Returns a new function that calls 'fn' with no arguments, passing\n" 10696 " 'undefined' as the 'this' value, and supplies an async stack for the\n" 10697 " call as described by the second argument, an object with the following\n" 10698 " properties (which are not optional, unless specified otherwise):\n" 10699 "\n" 10700 " stack: A SavedFrame object, like that returned by 'saveStack'. Stacks\n" 10701 " captured during calls to the returned function capture this as\n" 10702 " their async stack parent, accessible via a SavedFrame's\n" 10703 " 'asyncParent' property.\n" 10704 "\n" 10705 " cause: A string, supplied as the async cause on the top frame of\n" 10706 " captured async stacks.\n" 10707 "\n" 10708 " explicit: A boolean value, indicating whether the given 'stack' should\n" 10709 " always supplant the returned function's true callers (true),\n" 10710 " or only when there are no other JavaScript frames on the stack\n" 10711 " below it (false). If omitted, this is treated as 'true'."), 10712 10713 #ifndef __wasi__ 10714 JS_FN_HELP("wasmCompileInSeparateProcess", WasmCompileInSeparateProcess, 1, 0, 10715 "wasmCompileInSeparateProcess(buffer)", 10716 " Compile the given buffer in a separate process, serialize the resulting\n" 10717 " wasm::Module into bytes, and deserialize those bytes in the current\n" 10718 " process, returning the resulting WebAssembly.Module."), 10719 10720 JS_FN_HELP("wasmTextToBinary", WasmTextToBinary, 1, 0, 10721 "wasmTextToBinary(str)", 10722 " Translates the given text wasm module into its binary encoding."), 10723 #endif // __wasi__ 10724 10725 JS_FN_HELP("transplantableObject", TransplantableObject, 0, 0, 10726 "transplantableObject([options])", 10727 " Returns the pair {object, transplant}. |object| is an object which can be\n" 10728 " transplanted into a new object when the |transplant| function, which must\n" 10729 " be invoked with a global object, is called.\n" 10730 " |object| is swapped with a cross-compartment wrapper if the global object\n" 10731 " is in a different compartment.\n" 10732 "\n" 10733 " If options is given, it may have any of the following properties:\n" 10734 " proxy: Create a DOM Proxy object instead of a plain DOM object.\n" 10735 " object: Don't create a new DOM object, but instead use the supplied\n" 10736 " FakeDOMObject."), 10737 10738 JS_FN_HELP("cpuNow", CpuNow, /* nargs= */ 0, /* flags = */ 0, 10739 "cpuNow()", 10740 " Returns the approximate processor time used by the process since an arbitrary epoch, in seconds.\n" 10741 " Only the difference between two calls to `cpuNow()` is meaningful."), 10742 10743 #ifdef FUZZING_JS_FUZZILLI 10744 JS_FN_HELP("fuzzilli", Fuzzilli, 0, 0, 10745 "fuzzilli(operation, arg)", 10746 " Exposes functionality used by the Fuzzilli JavaScript fuzzer."), 10747 #endif 10748 10749 #ifdef FUZZING_INTERFACES 10750 JS_FN_HELP("getWasmSmithModule", GetWasmSmithModule, 1, 0, 10751 "getWasmSmithModule(arrayBuffer)", 10752 " Call wasm-smith to generate a random wasm module from the provided data."), 10753 #endif 10754 10755 JS_FN_HELP("isValidJSON", IsValidJSON, 1, 0, 10756 "isValidJSON(source)", 10757 " Returns true if the given source is valid JSON."), 10758 10759 JS_FN_HELP("createSideEffectfulResolveObject", CreateSideEffectfulResolveObject, 0, 0, 10760 "createSideEffectfulResolveObject()", 10761 " Return an object with a property 'obj.test == 42', backed by a resolve hook " 10762 " with the Debugger shouldAvoidSideEffects flag integration."), 10763 10764 JS_FN_HELP("getUseCounterResults", GetUseCounterResults, 0, 0, 10765 "getUseCounterResults()", 10766 " Return the values of the shell use counters."), 10767 10768 JS_FS_HELP_END 10769 }; 10770 // clang-format on 10771 10772 // clang-format off 10773 #ifdef FUZZING_JS_FUZZILLI 10774 static const JSFunctionSpec shell_function_fuzzilli_hash[] = { 10775 JS_INLINABLE_FN("fuzzilli_hash", fuzzilli_hash, 1, 0, FuzzilliHash), 10776 JS_FS_END, 10777 }; 10778 #endif 10779 // clang-format on 10780 10781 // clang-format off 10782 static const JSFunctionSpecWithHelp diff_testing_unsafe_functions[] = { 10783 10784 JS_FS_HELP_END 10785 }; 10786 // clang-format on 10787 10788 // clang-format off 10789 static const JSFunctionSpecWithHelp fuzzing_unsafe_functions[] = { 10790 JS_FN_HELP("getSelfHostedValue", GetSelfHostedValue, 1, 0, 10791 "getSelfHostedValue()", 10792 " Get a self-hosted value by its name. Note that these values don't get \n" 10793 " cached, so repeatedly getting the same value creates multiple distinct clones."), 10794 10795 JS_FN_HELP("line2pc", LineToPC, 0, 0, 10796 "line2pc([fun,] line)", 10797 " Map line number to PC."), 10798 10799 JS_FN_HELP("pc2line", PCToLine, 0, 0, 10800 "pc2line(fun[, pc])", 10801 " Map PC to line number."), 10802 10803 JS_INLINABLE_FN_HELP("assertFloat32", testingFunc_assertFloat32, 2, 0, TestAssertFloat32, 10804 "assertFloat32(value, isFloat32)", 10805 " In IonMonkey only, asserts that value has (resp. hasn't) the MIRType::Float32 if isFloat32 is true (resp. false)."), 10806 10807 JS_INLINABLE_FN_HELP("assertRecoveredOnBailout", testingFunc_assertRecoveredOnBailout, 2, 0, 10808 TestAssertRecoveredOnBailout, 10809 "assertRecoveredOnBailout(var)", 10810 " In IonMonkey only, asserts that variable has RecoveredOnBailout flag."), 10811 10812 JS_FN_HELP("withSourceHook", WithSourceHook, 1, 0, 10813 "withSourceHook(hook, fun)", 10814 " Set this JS runtime's lazy source retrieval hook (that is, the hook\n" 10815 " used to find sources compiled with |CompileOptions::LAZY_SOURCE|) to\n" 10816 " |hook|; call |fun| with no arguments; and then restore the runtime's\n" 10817 " original hook. Return or throw whatever |fun| did. |hook| gets\n" 10818 " passed the requested code's URL, and should return a string.\n" 10819 "\n" 10820 " Notes:\n" 10821 "\n" 10822 " 1) SpiderMonkey may assert if the returned code isn't close enough\n" 10823 " to the script's real code, so this function is not fuzzer-safe.\n" 10824 "\n" 10825 " 2) The runtime can have only one source retrieval hook active at a\n" 10826 " time. If |fun| is not careful, |hook| could be asked to retrieve the\n" 10827 " source code for compilations that occurred long before it was set,\n" 10828 " and that it knows nothing about. The reverse applies as well: the\n" 10829 " original hook, that we reinstate after the call to |fun| completes,\n" 10830 " might be asked for the source code of compilations that |fun|\n" 10831 " performed, and which, presumably, only |hook| knows how to find.\n"), 10832 10833 JS_FN_HELP("crash", Crash, 0, 0, 10834 "crash([message, [{disable_minidump:true}]])", 10835 " Crashes the process with a MOZ_CRASH, optionally providing a message.\n" 10836 " An options object may be passed as the second argument. If the key\n" 10837 " 'suppress_minidump' is set to true, then a minidump will not be\n" 10838 " generated by the crash (which only has an effect if the breakpad\n" 10839 " dumping library is loaded.)"), 10840 10841 #ifndef __wasi__ 10842 JS_FN_HELP("wasmLoop", WasmLoop, 2, 0, 10843 "wasmLoop(filename, imports)", 10844 " Performs an AFL-style persistent loop reading data from the given file and passing it\n" 10845 " to the 'wasmEval' function together with the specified imports object."), 10846 #endif // __wasi__ 10847 10848 JS_FN_HELP("setBufferStreamParams", SetBufferStreamParams, 2, 0, 10849 "setBufferStreamParams(delayMillis, chunkByteSize)", 10850 " Set the delay time (between calls to StreamConsumer::consumeChunk) and chunk\n" 10851 " size (in bytes)."), 10852 10853 #ifdef JS_CACHEIR_SPEW 10854 JS_FN_HELP("cacheIRHealthReport", CacheIRHealthReport, 0, 0, 10855 "cacheIRHealthReport()", 10856 " Show health rating of CacheIR stubs."), 10857 #endif 10858 10859 #ifdef DEBUG 10860 JS_FN_HELP("debugGetQueuedJobs", DebugGetQueuedJobs, 0, 0, 10861 "debugGetQueuedJobs()", 10862 " Returns an array of queued jobs."), 10863 #endif 10864 10865 #ifdef JS_HAS_INTL_API 10866 // One of the extras is AddMozDateTimeFormatConstructor, which is not fuzzing 10867 // safe, since it doesn't validate the custom format pattern. 10868 // 10869 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1887585#c1 10870 JS_FN_HELP("addIntlExtras", AddIntlExtras, 1, 0, 10871 "addIntlExtras(obj)", 10872 "Adds various not-yet-standardized Intl functions as properties on the\n" 10873 "provided object (this should generally be Intl itself). The added\n" 10874 "functions and their behavior are experimental: don't depend upon them\n" 10875 "unless you're willing to update your code if these experimental APIs change\n" 10876 "underneath you."), 10877 #endif // JS_HAS_INTL_API 10878 10879 #ifdef MOZ_EXECUTION_TRACING 10880 JS_FN_HELP("enableExecutionTracing", EnableExecutionTracing, 0, 0, 10881 "enableExecutionTracing()", 10882 " Enable execution tracing for the current context."), 10883 10884 JS_FN_HELP("getExecutionTrace", GetExecutionTrace, 0, 0, 10885 "getExecutionTrace()", 10886 " Get the execution trace."), 10887 10888 JS_FN_HELP("disableExecutionTracing", DisableExecutionTracing, 0, 0, 10889 "disableExecutionTracing()", 10890 " Disable execution tracing for the current context."), 10891 #endif // MOZ_EXECUTION_TRACING 10892 10893 JS_FN_HELP("startRecordingTelemetry", StartRecordingTelemetry, 1, 0, 10894 "startRecordingTelemetry(probeName)", 10895 " Start recording telemetry samples for the specified probe."), 10896 10897 JS_FN_HELP("stopRecordingTelemetry", StopRecordingTelemetry, 1, 0, 10898 "stopRecordingTelemetry(probeName)", 10899 " Stop recording telemetry samples for the specified probe."), 10900 10901 JS_FN_HELP("getTelemetrySamples", GetTelemetrySamples, 1, 0, 10902 "getTelemetry(probeName)", 10903 " Return an array of recorded telemetry samples for the specified probe."), 10904 10905 // compressLZ4 and decompressLZ4 are fuzzing unsafe because of unfixed 10906 // integer overflow issues. See bug 1900525. 10907 JS_FN_HELP("compressLZ4", CompressLZ4, 1, 0, 10908 "compressLZ4(bytes)", 10909 " Return a compressed copy of bytes using LZ4."), 10910 10911 JS_FN_HELP("decompressLZ4", DecompressLZ4, 1, 0, 10912 "decompressLZ4(bytes)", 10913 " Return a decompressed copy of bytes using LZ4."), 10914 10915 JS_FN_HELP("mozLog", SetMozLog, 0, 0, 10916 "mozLog(\"logLevels\")", 10917 " Modify log levels as if MOZ_LOG had been set to this at startup.\n" 10918 " `logLevels` is a comma-separated list of log modules, each\n" 10919 " with an optional \":<level>\" (defaults to 5 aka Debug).\n" 10920 " example: mozLog('gc,wasm:4')"), 10921 10922 JS_FS_HELP_END 10923 }; 10924 // clang-format on 10925 10926 // clang-format off 10927 static const JSFunctionSpecWithHelp performance_functions[] = { 10928 JS_FN_HELP("now", Now, 0, 0, 10929 "now()", 10930 " Return the current time with sub-ms precision.\n" 10931 " This function is an alias of the dateNow() function."), 10932 JS_FS_HELP_END 10933 }; 10934 // clang-format on 10935 10936 // clang-format off 10937 static const JSFunctionSpecWithHelp console_functions[] = { 10938 JS_FN_HELP("log", Print, 0, 0, 10939 "log([exp ...])", 10940 " Evaluate and print expressions to stdout.\n" 10941 " This function is an alias of the print() function."), 10942 JS_FS_HELP_END 10943 }; 10944 // clang-format on 10945 10946 bool DefineConsole(JSContext* cx, HandleObject global) { 10947 RootedObject obj(cx, JS_NewPlainObject(cx)); 10948 return obj && JS_DefineFunctionsWithHelp(cx, obj, console_functions) && 10949 JS_DefineProperty(cx, global, "console", obj, 0); 10950 } 10951 10952 #ifdef MOZ_PROFILING 10953 # define PROFILING_FUNCTION_COUNT 5 10954 # ifdef MOZ_CALLGRIND 10955 # define CALLGRIND_FUNCTION_COUNT 3 10956 # else 10957 # define CALLGRIND_FUNCTION_COUNT 0 10958 # endif 10959 # ifdef MOZ_VTUNE 10960 # define VTUNE_FUNCTION_COUNT 4 10961 # else 10962 # define VTUNE_FUNCTION_COUNT 0 10963 # endif 10964 # define EXTERNAL_FUNCTION_COUNT \ 10965 (PROFILING_FUNCTION_COUNT + CALLGRIND_FUNCTION_COUNT + VTUNE_FUNCTION_COUNT) 10966 #else 10967 # define EXTERNAL_FUNCTION_COUNT 0 10968 #endif 10969 10970 #undef PROFILING_FUNCTION_COUNT 10971 #undef CALLGRIND_FUNCTION_COUNT 10972 #undef VTUNE_FUNCTION_COUNT 10973 #undef EXTERNAL_FUNCTION_COUNT 10974 10975 static bool PrintHelpString(JSContext* cx, HandleValue v) { 10976 RootedString str(cx, v.toString()); 10977 MOZ_ASSERT(gOutFile->isOpen()); 10978 10979 UniqueChars bytes = JS_EncodeStringToUTF8(cx, str); 10980 if (!bytes) { 10981 return false; 10982 } 10983 10984 fprintf(gOutFile->fp, "%s\n", bytes.get()); 10985 return true; 10986 } 10987 10988 static bool PrintHelp(JSContext* cx, HandleObject obj) { 10989 RootedValue usage(cx); 10990 if (!JS_GetProperty(cx, obj, "usage", &usage)) { 10991 return false; 10992 } 10993 RootedValue help(cx); 10994 if (!JS_GetProperty(cx, obj, "help", &help)) { 10995 return false; 10996 } 10997 10998 if (!usage.isString() || !help.isString()) { 10999 return true; 11000 } 11001 11002 return PrintHelpString(cx, usage) && PrintHelpString(cx, help); 11003 } 11004 11005 struct ExtraGlobalBindingWithHelp { 11006 const char* name; 11007 const char* help; 11008 }; 11009 11010 // clang-format off 11011 static ExtraGlobalBindingWithHelp extraGlobalBindingsWithHelp[] = { 11012 // Defined in BindScriptArgs. 11013 { 11014 "scriptArgs", 11015 " An array containing the command line arguments passed after the path\n" 11016 " to a JS script."}, 11017 { 11018 "scriptPath", 11019 " The path to the JS script passed to JS shell. This does not reflect\n" 11020 " modules evaluated via -m option."}, 11021 11022 // Defined in DefineConsole. 11023 { 11024 "console", 11025 " An object with console.log() which aliases print()."}, 11026 11027 // Defined in NewGlobalObject. 11028 { 11029 "performance", 11030 " An object with the following properties:\n" 11031 " performance.now()\n" 11032 " See help(performance.now)\n" 11033 " performance.mozMemory.gc\n" 11034 " An object that represents GC statistics with the following properties:\n" 11035 " gcBytes\n" 11036 " gcMaxBytes\n" 11037 " mallocBytes\n" 11038 " gcIsHighFrequencyMode\n" 11039 " gcNumber\n" 11040 " majorGCCount\n" 11041 " minorGCCount\n" 11042 " sliceCount\n" 11043 " compartmentCount\n" 11044 " lastStartReason\n" 11045 " zone.gcBytes\n" 11046 " zone.gcTriggerBytes\n" 11047 " zone.gcAllocTrigger\n" 11048 " zone.mallocBytes\n" 11049 " zone.mallocTriggerBytes\n" 11050 " zone.gcNumber"}, 11051 { 11052 "new FakeDOMObject()", 11053 " A constructor to test IonMonkey DOM optimizations in JS shell.\n" 11054 " The prototype object has the following properties:\n" 11055 " FakeDOMObject.prototype.x\n" 11056 " Generic getter/setter with JSJitInfo\n" 11057 " FakeDOMObject.prototype.slot\n" 11058 " Getter with JSJitInfo.slotIndex\n" 11059 " FakeDOMObject.prototype.global\n" 11060 " Getter/setter with JSJitInfo::AliasEverything\n" 11061 " FakeDOMObject.prototype.doFoo()\n" 11062 " Method with JSJitInfo\n" 11063 " FakeDOMObject.prototype.getObject()\n" 11064 " Method with JSJitInfo that returns an object."}, 11065 }; 11066 // clang-format on 11067 11068 static bool MatchPattern(JSContext* cx, JS::Handle<RegExpObject*> regex, 11069 JS::Handle<JSString*> inputStr, bool* result) { 11070 JS::Rooted<JSString*> linearInputStr(cx, inputStr); 11071 if (!linearInputStr->ensureLinear(cx)) { 11072 return false; 11073 } 11074 11075 // Execute the regular expression in |regex|'s compartment. 11076 JSAutoRealm ar(cx, regex); 11077 if (!cx->compartment()->wrap(cx, &linearInputStr)) { 11078 return false; 11079 } 11080 JS::Rooted<JSLinearString*> input(cx, &linearInputStr->asLinear()); 11081 size_t ignored = 0; 11082 JS::Rooted<JS::Value> v(cx); 11083 if (!ExecuteRegExpLegacy(cx, nullptr, regex, input, &ignored, true, &v)) { 11084 return false; 11085 } 11086 *result = !v.isNull(); 11087 return true; 11088 } 11089 11090 static bool PrintEnumeratedHelp(JSContext* cx, HandleObject obj, 11091 Handle<RegExpObject*> pattern, bool brief) { 11092 RootedIdVector idv(cx); 11093 if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &idv)) { 11094 return false; 11095 } 11096 11097 for (size_t i = 0; i < idv.length(); i++) { 11098 RootedValue v(cx); 11099 RootedId id(cx, idv[i]); 11100 if (!JS_GetPropertyById(cx, obj, id, &v)) { 11101 return false; 11102 } 11103 if (!v.isObject()) { 11104 continue; 11105 } 11106 11107 RootedObject funcObj(cx, &v.toObject()); 11108 if (pattern) { 11109 // Only pay attention to objects with a 'help' property, which will 11110 // either be documented functions or interface objects. 11111 if (!JS_GetProperty(cx, funcObj, "help", &v)) { 11112 return false; 11113 } 11114 if (!v.isString()) { 11115 continue; 11116 } 11117 11118 // For functions, match against the name. For interface objects, 11119 // match against the usage string. 11120 if (!JS_GetProperty(cx, funcObj, "name", &v)) { 11121 return false; 11122 } 11123 if (!v.isString()) { 11124 if (!JS_GetProperty(cx, funcObj, "usage", &v)) { 11125 return false; 11126 } 11127 if (!v.isString()) { 11128 continue; 11129 } 11130 } 11131 11132 Rooted<JSString*> inputStr(cx, v.toString()); 11133 bool result = false; 11134 if (!MatchPattern(cx, pattern, inputStr, &result)) { 11135 return false; 11136 } 11137 if (!result) { 11138 continue; 11139 } 11140 } 11141 11142 if (!PrintHelp(cx, funcObj)) { 11143 return false; 11144 } 11145 } 11146 11147 return true; 11148 } 11149 11150 static bool PrintExtraGlobalEnumeratedHelp(JSContext* cx, 11151 Handle<RegExpObject*> pattern, 11152 bool brief) { 11153 for (const auto& item : extraGlobalBindingsWithHelp) { 11154 if (pattern) { 11155 JS::Rooted<JSString*> name(cx, JS_NewStringCopyZ(cx, item.name)); 11156 if (!name) { 11157 return false; 11158 } 11159 11160 bool result = false; 11161 if (!MatchPattern(cx, pattern, name, &result)) { 11162 return false; 11163 } 11164 if (!result) { 11165 continue; 11166 } 11167 } 11168 fprintf(gOutFile->fp, "%s\n", item.name); 11169 fprintf(gOutFile->fp, "%s\n", item.help); 11170 } 11171 11172 return true; 11173 } 11174 11175 static bool Help(JSContext* cx, unsigned argc, Value* vp) { 11176 if (!gOutFile->isOpen()) { 11177 JS_ReportErrorASCII(cx, "output file is closed"); 11178 return false; 11179 } 11180 11181 CallArgs args = CallArgsFromVp(argc, vp); 11182 args.rval().setUndefined(); 11183 RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); 11184 11185 // help() - display the version and dump out help for all functions on the 11186 // global. 11187 if (args.length() == 0) { 11188 fprintf(gOutFile->fp, "%s\n", JS_GetImplementationVersion()); 11189 11190 if (!PrintEnumeratedHelp(cx, global, nullptr, false)) { 11191 return false; 11192 } 11193 if (!PrintExtraGlobalEnumeratedHelp(cx, nullptr, false)) { 11194 return false; 11195 } 11196 return true; 11197 } 11198 11199 RootedValue v(cx); 11200 11201 if (args[0].isPrimitive()) { 11202 // help("foo") 11203 JS_ReportErrorASCII(cx, "primitive arg"); 11204 return false; 11205 } 11206 11207 RootedObject obj(cx, &args[0].toObject()); 11208 if (!obj) { 11209 return true; 11210 } 11211 bool isRegexp; 11212 if (!JS::ObjectIsRegExp(cx, obj, &isRegexp)) { 11213 return false; 11214 } 11215 11216 if (isRegexp) { 11217 // help(/pattern/) 11218 Rooted<RegExpObject*> pattern(cx, 11219 &UncheckedUnwrap(obj)->as<RegExpObject>()); 11220 if (!PrintEnumeratedHelp(cx, global, pattern, false)) { 11221 return false; 11222 } 11223 if (!PrintExtraGlobalEnumeratedHelp(cx, pattern, false)) { 11224 return false; 11225 } 11226 return true; 11227 } 11228 11229 // help(function) 11230 // help(namespace_obj) 11231 return PrintHelp(cx, obj); 11232 } 11233 11234 static const JSErrorFormatString jsShell_ErrorFormatString[JSShellErr_Limit] = { 11235 #define MSG_DEF(name, count, exception, format) \ 11236 {#name, format, count, JSEXN_ERR}, 11237 #include "jsshell.msg" 11238 #undef MSG_DEF 11239 }; 11240 11241 const JSErrorFormatString* js::shell::my_GetErrorMessage( 11242 void* userRef, const unsigned errorNumber) { 11243 if (errorNumber == 0 || errorNumber >= JSShellErr_Limit) { 11244 return nullptr; 11245 } 11246 11247 return &jsShell_ErrorFormatString[errorNumber]; 11248 } 11249 11250 static bool CreateLastWarningObject(JSContext* cx, JSErrorReport* report) { 11251 RootedObject warningObj(cx, JS_NewObject(cx, nullptr)); 11252 if (!warningObj) { 11253 return false; 11254 } 11255 11256 if (!CopyErrorReportToObject(cx, report, warningObj)) { 11257 return false; 11258 } 11259 11260 GetShellContext(cx)->lastWarning.setObject(*warningObj); 11261 return true; 11262 } 11263 11264 static FILE* ErrorFilePointer() { 11265 if (gErrFile->isOpen()) { 11266 return gErrFile->fp; 11267 } 11268 11269 fprintf(stderr, "error file is closed; falling back to stderr\n"); 11270 return stderr; 11271 } 11272 11273 bool shell::PrintStackTrace(JSContext* cx, HandleObject stackObj) { 11274 if (!stackObj || !stackObj->is<SavedFrame>()) { 11275 return true; 11276 } 11277 11278 JSPrincipals* principals = stackObj->nonCCWRealm()->principals(); 11279 RootedString stackStr(cx); 11280 if (!BuildStackString(cx, principals, stackObj, &stackStr, 2)) { 11281 return false; 11282 } 11283 11284 UniqueChars stack = JS_EncodeStringToUTF8(cx, stackStr); 11285 if (!stack) { 11286 return false; 11287 } 11288 11289 FILE* fp = ErrorFilePointer(); 11290 fputs("Stack:\n", fp); 11291 fputs(stack.get(), fp); 11292 11293 return true; 11294 } 11295 11296 js::shell::AutoReportException::~AutoReportException() { 11297 if (!JS_IsExceptionPending(cx)) { 11298 return; 11299 } 11300 11301 auto printError = [](JSContext* cx, auto& report, const auto& exnStack, 11302 const char* prefix = nullptr) { 11303 if (!report.init(cx, exnStack, JS::ErrorReportBuilder::WithSideEffects)) { 11304 fprintf(stderr, "out of memory initializing JS::ErrorReportBuilder\n"); 11305 fflush(stderr); 11306 JS_ClearPendingException(cx); 11307 return false; 11308 } 11309 11310 MOZ_ASSERT(!report.report()->isWarning()); 11311 11312 FILE* fp = ErrorFilePointer(); 11313 if (prefix) { 11314 fputs(prefix, fp); 11315 } 11316 JS::PrintError(fp, report, reportWarnings); 11317 JS_ClearPendingException(cx); 11318 11319 // If possible, use the original error stack as the source of truth, because 11320 // finally block handlers may have overwritten the exception stack. 11321 RootedObject stack(cx, exnStack.stack()); 11322 if (exnStack.exception().isObject()) { 11323 RootedObject exception(cx, &exnStack.exception().toObject()); 11324 if (JSObject* exceptionStack = JS::ExceptionStackOrNull(exception)) { 11325 stack.set(exceptionStack); 11326 } 11327 } 11328 11329 if (!PrintStackTrace(cx, stack)) { 11330 fputs("(Unable to print stack trace)\n", fp); 11331 JS_ClearPendingException(cx); 11332 } 11333 11334 return true; 11335 }; 11336 11337 // Get exception object and stack before printing and clearing exception. 11338 JS::ExceptionStack exnStack(cx); 11339 if (!JS::StealPendingExceptionStack(cx, &exnStack)) { 11340 fprintf(stderr, "out of memory while stealing exception\n"); 11341 fflush(stderr); 11342 JS_ClearPendingException(cx); 11343 return; 11344 } 11345 11346 ShellContext* sc = GetShellContext(cx); 11347 JS::ErrorReportBuilder report(cx); 11348 if (!printError(cx, report, exnStack)) { 11349 // Return if we couldn't initialize the error report. 11350 return; 11351 } 11352 11353 // Print the error's cause, if available. 11354 if (exnStack.exception().isObject()) { 11355 JSObject* exception = &exnStack.exception().toObject(); 11356 if (exception->is<ErrorObject>()) { 11357 auto* error = &exception->as<ErrorObject>(); 11358 if (auto maybeCause = error->getCause()) { 11359 RootedValue cause(cx, maybeCause.value()); 11360 11361 RootedObject causeStack(cx); 11362 if (cause.isObject()) { 11363 RootedObject causeObj(cx, &cause.toObject()); 11364 causeStack = JS::ExceptionStackOrNull(causeObj); 11365 } 11366 11367 JS::ExceptionStack exnStack(cx, cause, causeStack); 11368 JS::ErrorReportBuilder report(cx); 11369 printError(cx, report, exnStack, "Caused by: "); 11370 } 11371 } 11372 } 11373 11374 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT) 11375 // Don't quit the shell if an unhandled exception is reported during OOM 11376 // testing. 11377 if (cx->runningOOMTest) { 11378 return; 11379 } 11380 #endif 11381 11382 if (report.report()->errorNumber == JSMSG_OUT_OF_MEMORY) { 11383 sc->exitCode = EXITCODE_OUT_OF_MEMORY; 11384 } else { 11385 sc->exitCode = EXITCODE_RUNTIME_ERROR; 11386 } 11387 } 11388 11389 void js::shell::WarningReporter(JSContext* cx, JSErrorReport* report) { 11390 ShellContext* sc = GetShellContext(cx); 11391 FILE* fp = ErrorFilePointer(); 11392 11393 MOZ_ASSERT(report->isWarning()); 11394 11395 if (sc->lastWarningEnabled) { 11396 JS::AutoSaveExceptionState savedExc(cx); 11397 if (!CreateLastWarningObject(cx, report)) { 11398 fputs("Unhandled error happened while creating last warning object.\n", 11399 fp); 11400 fflush(fp); 11401 } 11402 savedExc.restore(); 11403 } 11404 11405 // Print the warning. 11406 JS::PrintError(fp, report, reportWarnings); 11407 } 11408 11409 static bool global_enumerate(JSContext* cx, JS::HandleObject obj, 11410 JS::MutableHandleIdVector properties, 11411 bool enumerableOnly) { 11412 #ifdef LAZY_STANDARD_CLASSES 11413 return JS_NewEnumerateStandardClasses(cx, obj, properties, enumerableOnly); 11414 #else 11415 return true; 11416 #endif 11417 } 11418 11419 static bool global_resolve(JSContext* cx, HandleObject obj, HandleId id, 11420 bool* resolvedp) { 11421 #ifdef LAZY_STANDARD_CLASSES 11422 if (!JS_ResolveStandardClass(cx, obj, id, resolvedp)) { 11423 return false; 11424 } 11425 #endif 11426 return true; 11427 } 11428 11429 static bool global_mayResolve(const JSAtomState& names, jsid id, 11430 JSObject* maybeObj) { 11431 return JS_MayResolveStandardClass(names, id, maybeObj); 11432 } 11433 11434 static const JSClassOps global_classOps = { 11435 nullptr, // addProperty 11436 nullptr, // delProperty 11437 nullptr, // enumerate 11438 global_enumerate, // newEnumerate 11439 global_resolve, // resolve 11440 global_mayResolve, // mayResolve 11441 nullptr, // finalize 11442 nullptr, // call 11443 nullptr, // construct 11444 JS_GlobalObjectTraceHook, // trace 11445 }; 11446 11447 static constexpr uint32_t DOM_PROTOTYPE_SLOT = JSCLASS_GLOBAL_SLOT_COUNT; 11448 static constexpr uint32_t DOM_GLOBAL_SLOTS = 1; 11449 11450 static const JSClass global_class = { 11451 "global", 11452 JSCLASS_GLOBAL_FLAGS | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS), 11453 &global_classOps, 11454 }; 11455 11456 /* 11457 * Define a FakeDOMObject constructor. It returns an object with a getter, 11458 * setter and method with attached JitInfo. This object can be used to test 11459 * IonMonkey DOM optimizations in the shell. 11460 */ 11461 11462 /* Fow now just use to a constant we can check. */ 11463 static const void* DOM_PRIVATE_VALUE = (void*)0x1234; 11464 11465 static bool dom_genericGetter(JSContext* cx, unsigned argc, JS::Value* vp); 11466 11467 static bool dom_genericSetter(JSContext* cx, unsigned argc, JS::Value* vp); 11468 11469 static bool dom_genericMethod(JSContext* cx, unsigned argc, JS::Value* vp); 11470 11471 static bool dom_get_x(JSContext* cx, HandleObject obj, void* self, 11472 JSJitGetterCallArgs args) { 11473 MOZ_ASSERT(JS::GetClass(obj) == GetDomClass()); 11474 MOZ_ASSERT(self == DOM_PRIVATE_VALUE); 11475 args.rval().set(JS_NumberValue(double(3.14))); 11476 return true; 11477 } 11478 11479 static bool dom_set_x(JSContext* cx, HandleObject obj, void* self, 11480 JSJitSetterCallArgs args) { 11481 MOZ_ASSERT(JS::GetClass(obj) == GetDomClass()); 11482 MOZ_ASSERT(self == DOM_PRIVATE_VALUE); 11483 return true; 11484 } 11485 11486 static bool dom_get_slot(JSContext* cx, HandleObject obj, void* self, 11487 JSJitGetterCallArgs args) { 11488 MOZ_ASSERT(JS::GetClass(obj) == GetDomClass()); 11489 MOZ_ASSERT(self == DOM_PRIVATE_VALUE); 11490 11491 Value v = JS::GetReservedSlot(obj, DOM_OBJECT_SLOT2); 11492 MOZ_ASSERT(v.toInt32() == 42); 11493 args.rval().set(v); 11494 return true; 11495 } 11496 11497 static bool dom_get_global(JSContext* cx, HandleObject obj, void* self, 11498 JSJitGetterCallArgs args) { 11499 MOZ_ASSERT(JS::GetClass(obj) == GetDomClass()); 11500 MOZ_ASSERT(self == DOM_PRIVATE_VALUE); 11501 11502 // Return the current global (instead of obj->global()) to test cx->realm 11503 // switching in the JIT. 11504 args.rval().setObject(*ToWindowProxyIfWindow(cx->global())); 11505 11506 return true; 11507 } 11508 11509 static bool dom_set_global(JSContext* cx, HandleObject obj, void* self, 11510 JSJitSetterCallArgs args) { 11511 MOZ_ASSERT(JS::GetClass(obj) == GetDomClass()); 11512 MOZ_ASSERT(self == DOM_PRIVATE_VALUE); 11513 11514 // Throw an exception if our argument is not the current global. This lets 11515 // us test cx->realm switching. 11516 if (!args[0].isObject() || 11517 ToWindowIfWindowProxy(&args[0].toObject()) != cx->global()) { 11518 JS_ReportErrorASCII(cx, "Setter not called with matching global argument"); 11519 return false; 11520 } 11521 11522 return true; 11523 } 11524 11525 static bool dom_doFoo(JSContext* cx, HandleObject obj, void* self, 11526 const JSJitMethodCallArgs& args) { 11527 MOZ_ASSERT(JS::GetClass(obj) == GetDomClass()); 11528 MOZ_ASSERT(self == DOM_PRIVATE_VALUE); 11529 MOZ_ASSERT(cx->realm() == args.callee().as<JSFunction>().realm()); 11530 11531 /* Just return args.length(). */ 11532 args.rval().setInt32(args.length()); 11533 return true; 11534 } 11535 11536 static bool dom_doBar(JSContext* cx, HandleObject obj, void* self, 11537 const JSJitMethodCallArgs& args) { 11538 MOZ_ASSERT(JS::GetClass(obj) == GetDomClass()); 11539 MOZ_ASSERT(self == DOM_PRIVATE_VALUE); 11540 MOZ_ASSERT(cx->realm() == args.callee().as<JSFunction>().realm()); 11541 11542 static const JSClass barClass = { 11543 "BarObj", 11544 }; 11545 11546 JSObject* retObj = 11547 JS_NewObjectWithGivenProtoAndUseAllocSite(cx, &barClass, nullptr); 11548 if (!retObj) { 11549 return false; 11550 } 11551 11552 args.rval().setObject(*retObj); 11553 return true; 11554 } 11555 11556 static const JSJitInfo dom_x_getterinfo = { 11557 {(JSJitGetterOp)dom_get_x}, 11558 {0}, /* protoID */ 11559 {0}, /* depth */ 11560 JSJitInfo::Getter, 11561 JSJitInfo::AliasNone, /* aliasSet */ 11562 JSVAL_TYPE_UNKNOWN, /* returnType */ 11563 true, /* isInfallible. False in setters. */ 11564 true, /* isMovable */ 11565 true, /* isEliminatable */ 11566 false, /* isAlwaysInSlot */ 11567 false, /* isLazilyCachedInSlot */ 11568 false, /* isTypedMethod */ 11569 0 /* slotIndex */ 11570 }; 11571 11572 static const JSJitInfo dom_x_setterinfo = { 11573 {(JSJitGetterOp)dom_set_x}, 11574 {0}, /* protoID */ 11575 {0}, /* depth */ 11576 JSJitInfo::Setter, 11577 JSJitInfo::AliasEverything, /* aliasSet */ 11578 JSVAL_TYPE_UNKNOWN, /* returnType */ 11579 false, /* isInfallible. False in setters. */ 11580 false, /* isMovable. */ 11581 false, /* isEliminatable. */ 11582 false, /* isAlwaysInSlot */ 11583 false, /* isLazilyCachedInSlot */ 11584 false, /* isTypedMethod */ 11585 0 /* slotIndex */ 11586 }; 11587 11588 static const JSJitInfo dom_slot_getterinfo = { 11589 {(JSJitGetterOp)dom_get_slot}, 11590 {0}, /* protoID */ 11591 {0}, /* depth */ 11592 JSJitInfo::Getter, 11593 JSJitInfo::AliasNone, /* aliasSet */ 11594 JSVAL_TYPE_INT32, /* returnType */ 11595 false, /* isInfallible. False in setters. */ 11596 true, /* isMovable */ 11597 true, /* isEliminatable */ 11598 true, /* isAlwaysInSlot */ 11599 false, /* isLazilyCachedInSlot */ 11600 false, /* isTypedMethod */ 11601 DOM_OBJECT_SLOT2 /* slotIndex */ 11602 }; 11603 11604 // Note: this getter uses AliasEverything and is marked as fallible and 11605 // non-movable (1) to prevent Ion from getting too clever optimizing it and 11606 // (2) it's nice to have a few different kinds of getters in the shell. 11607 static const JSJitInfo dom_global_getterinfo = { 11608 {(JSJitGetterOp)dom_get_global}, 11609 {0}, /* protoID */ 11610 {0}, /* depth */ 11611 JSJitInfo::Getter, 11612 JSJitInfo::AliasEverything, /* aliasSet */ 11613 JSVAL_TYPE_OBJECT, /* returnType */ 11614 false, /* isInfallible. False in setters. */ 11615 false, /* isMovable */ 11616 false, /* isEliminatable */ 11617 false, /* isAlwaysInSlot */ 11618 false, /* isLazilyCachedInSlot */ 11619 false, /* isTypedMethod */ 11620 0 /* slotIndex */ 11621 }; 11622 11623 static const JSJitInfo dom_global_setterinfo = { 11624 {(JSJitGetterOp)dom_set_global}, 11625 {0}, /* protoID */ 11626 {0}, /* depth */ 11627 JSJitInfo::Setter, 11628 JSJitInfo::AliasEverything, /* aliasSet */ 11629 JSVAL_TYPE_UNKNOWN, /* returnType */ 11630 false, /* isInfallible. False in setters. */ 11631 false, /* isMovable. */ 11632 false, /* isEliminatable. */ 11633 false, /* isAlwaysInSlot */ 11634 false, /* isLazilyCachedInSlot */ 11635 false, /* isTypedMethod */ 11636 0 /* slotIndex */ 11637 }; 11638 11639 static const JSJitInfo doFoo_methodinfo = { 11640 {(JSJitGetterOp)dom_doFoo}, 11641 {0}, /* protoID */ 11642 {0}, /* depth */ 11643 JSJitInfo::Method, 11644 JSJitInfo::AliasEverything, /* aliasSet */ 11645 JSVAL_TYPE_UNKNOWN, /* returnType */ 11646 false, /* isInfallible. False in setters. */ 11647 false, /* isMovable */ 11648 false, /* isEliminatable */ 11649 false, /* isAlwaysInSlot */ 11650 false, /* isLazilyCachedInSlot */ 11651 false, /* isTypedMethod */ 11652 0 /* slotIndex */ 11653 }; 11654 11655 static const JSJitInfo doBar_methodinfo = { 11656 {(JSJitGetterOp)dom_doBar}, 11657 {0}, /* protoID */ 11658 {0}, /* depth */ 11659 JSJitInfo::Method, 11660 JSJitInfo::AliasEverything, /* aliasSet */ 11661 JSVAL_TYPE_OBJECT, /* returnType */ 11662 false, /* isInfallible. False in setters. */ 11663 false, /* isMovable */ 11664 false, /* isEliminatable */ 11665 false, /* isAlwaysInSlot */ 11666 false, /* isLazilyCachedInSlot */ 11667 false, /* isTypedMethod */ 11668 0 /* slotIndex */ 11669 }; 11670 11671 static const JSPropertySpec dom_props[] = { 11672 JSPropertySpec::nativeAccessors("x", JSPROP_ENUMERATE, dom_genericGetter, 11673 &dom_x_getterinfo, dom_genericSetter, 11674 &dom_x_setterinfo), 11675 JSPropertySpec::nativeAccessors("slot", JSPROP_ENUMERATE, dom_genericGetter, 11676 &dom_slot_getterinfo), 11677 JSPropertySpec::nativeAccessors("global", JSPROP_ENUMERATE, 11678 dom_genericGetter, &dom_global_getterinfo, 11679 dom_genericSetter, &dom_global_setterinfo), 11680 JS_PS_END, 11681 }; 11682 11683 static const JSFunctionSpec dom_methods[] = { 11684 JS_FNINFO("doFoo", dom_genericMethod, &doFoo_methodinfo, 3, 11685 JSPROP_ENUMERATE), 11686 JS_FNINFO("doBar", dom_genericMethod, &doBar_methodinfo, 3, 11687 JSPROP_ENUMERATE), 11688 JS_FS_END, 11689 }; 11690 11691 static void FakeDOMObject_finalize(JS::GCContext* gcx, JSObject* obj) { 11692 // Dummy finalize method so we can swap with background finalized object. 11693 } 11694 11695 static const JSClassOps FakeDOMObjectClassOps = { 11696 nullptr, // addProperty 11697 nullptr, // delProperty 11698 nullptr, // enumerate 11699 nullptr, // newEnumerate 11700 nullptr, // resolve 11701 nullptr, // mayResolve 11702 FakeDOMObject_finalize, 11703 nullptr, // call 11704 nullptr, // construct 11705 nullptr, 11706 }; 11707 11708 static const JSClass dom_class = { 11709 "FakeDOMObject", 11710 JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(2) | 11711 JSCLASS_BACKGROUND_FINALIZE | JSCLASS_PRESERVES_WRAPPER, 11712 &FakeDOMObjectClassOps}; 11713 11714 static const JSClass* GetDomClass() { return &dom_class; } 11715 11716 static bool dom_genericGetter(JSContext* cx, unsigned argc, JS::Value* vp) { 11717 CallArgs args = CallArgsFromVp(argc, vp); 11718 11719 if (!args.thisv().isObject()) { 11720 args.rval().setUndefined(); 11721 return true; 11722 } 11723 11724 RootedObject obj(cx, &args.thisv().toObject()); 11725 if (JS::GetClass(obj) != &dom_class) { 11726 args.rval().set(UndefinedValue()); 11727 return true; 11728 } 11729 11730 JS::Value val = JS::GetReservedSlot(obj, DOM_OBJECT_SLOT); 11731 11732 const JSJitInfo* info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); 11733 MOZ_ASSERT(info->type() == JSJitInfo::Getter); 11734 JSJitGetterOp getter = info->getter; 11735 return getter(cx, obj, val.toPrivate(), JSJitGetterCallArgs(args)); 11736 } 11737 11738 static bool dom_genericSetter(JSContext* cx, unsigned argc, JS::Value* vp) { 11739 CallArgs args = CallArgsFromVp(argc, vp); 11740 11741 if (args.length() < 1 || !args.thisv().isObject()) { 11742 args.rval().setUndefined(); 11743 return true; 11744 } 11745 11746 RootedObject obj(cx, &args.thisv().toObject()); 11747 if (JS::GetClass(obj) != &dom_class) { 11748 args.rval().set(UndefinedValue()); 11749 return true; 11750 } 11751 11752 JS::Value val = JS::GetReservedSlot(obj, DOM_OBJECT_SLOT); 11753 11754 const JSJitInfo* info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); 11755 MOZ_ASSERT(info->type() == JSJitInfo::Setter); 11756 JSJitSetterOp setter = info->setter; 11757 if (!setter(cx, obj, val.toPrivate(), JSJitSetterCallArgs(args))) { 11758 return false; 11759 } 11760 args.rval().set(UndefinedValue()); 11761 return true; 11762 } 11763 11764 static bool dom_genericMethod(JSContext* cx, unsigned argc, JS::Value* vp) { 11765 CallArgs args = CallArgsFromVp(argc, vp); 11766 11767 if (!args.thisv().isObject()) { 11768 args.rval().setUndefined(); 11769 return true; 11770 } 11771 11772 RootedObject obj(cx, &args.thisv().toObject()); 11773 if (JS::GetClass(obj) != &dom_class) { 11774 args.rval().set(UndefinedValue()); 11775 return true; 11776 } 11777 11778 JS::Value val = JS::GetReservedSlot(obj, DOM_OBJECT_SLOT); 11779 11780 const JSJitInfo* info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); 11781 MOZ_ASSERT(info->type() == JSJitInfo::Method); 11782 JSJitMethodOp method = info->method; 11783 return method(cx, obj, val.toPrivate(), JSJitMethodCallArgs(args)); 11784 } 11785 11786 static void InitDOMObject(HandleObject obj) { 11787 JS::SetReservedSlot(obj, DOM_OBJECT_SLOT, 11788 PrivateValue(const_cast<void*>(DOM_PRIVATE_VALUE))); 11789 JS::SetReservedSlot(obj, DOM_OBJECT_SLOT2, Int32Value(42)); 11790 } 11791 11792 static JSObject* GetDOMPrototype(JSContext* cx, JSObject* global) { 11793 MOZ_ASSERT(JS_IsGlobalObject(global)); 11794 if (JS::GetClass(global) != &global_class) { 11795 JS_ReportErrorASCII(cx, "Can't get FakeDOMObject prototype in sandbox"); 11796 return nullptr; 11797 } 11798 11799 const JS::Value& slot = JS::GetReservedSlot(global, DOM_PROTOTYPE_SLOT); 11800 MOZ_ASSERT(slot.isObject()); 11801 return &slot.toObject(); 11802 } 11803 11804 static bool dom_constructor(JSContext* cx, unsigned argc, JS::Value* vp) { 11805 CallArgs args = CallArgsFromVp(argc, vp); 11806 11807 RootedObject callee(cx, &args.callee()); 11808 RootedValue protov(cx); 11809 if (!GetProperty(cx, callee, callee, cx->names().prototype, &protov)) { 11810 return false; 11811 } 11812 11813 if (!protov.isObject()) { 11814 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_PROTOTYPE, 11815 "FakeDOMObject"); 11816 return false; 11817 } 11818 11819 RootedObject proto(cx, &protov.toObject()); 11820 RootedObject domObj(cx, JS_NewObjectWithGivenProto(cx, &dom_class, proto)); 11821 if (!domObj) { 11822 return false; 11823 } 11824 11825 InitDOMObject(domObj); 11826 11827 args.rval().setObject(*domObj); 11828 return true; 11829 } 11830 11831 static bool InstanceClassHasProtoAtDepth(const JSClass* clasp, uint32_t protoID, 11832 uint32_t depth) { 11833 // Only the (fake) DOM object supports any JIT optimizations. 11834 return clasp == GetDomClass(); 11835 } 11836 11837 static bool InstanceClassIsError(const JSClass* clasp) { return false; } 11838 11839 static bool ExtractExceptionInfo(JSContext* cx, JS::HandleObject obj, 11840 bool* isException, 11841 JS::MutableHandle<JSString*> fileName, 11842 uint32_t* line, uint32_t* column, 11843 JS::MutableHandle<JSString*> message) { 11844 *isException = false; 11845 return true; 11846 } 11847 11848 static bool ShellBuildId(JS::BuildIdCharVector* buildId) { 11849 // The browser embeds the date into the buildid and the buildid is embedded 11850 // in the binary, so every 'make' necessarily builds a new firefox binary. 11851 // Fortunately, the actual firefox executable is tiny -- all the code is in 11852 // libxul.so and other shared modules -- so this isn't a big deal. Not so 11853 // for the statically-linked JS shell. To avoid recompiling js.cpp and 11854 // re-linking 'js' on every 'make', we use a constant buildid and rely on 11855 // the shell user to manually clear any caches between cache-breaking updates. 11856 const char buildid[] = "JS-shell"; 11857 return buildId->append(buildid, sizeof(buildid)); 11858 } 11859 11860 static bool TimesAccessed(JSContext* cx, unsigned argc, Value* vp) { 11861 static int32_t accessed = 0; 11862 CallArgs args = CallArgsFromVp(argc, vp); 11863 args.rval().setInt32(++accessed); 11864 return true; 11865 } 11866 11867 static const JSPropertySpec TestingProperties[] = { 11868 JS_PSG("timesAccessed", TimesAccessed, 0), 11869 JS_PS_END, 11870 }; 11871 11872 static JSObject* NewGlobalObject(JSContext* cx, JS::RealmOptions& options, 11873 JSPrincipals* principals, ShellGlobalKind kind, 11874 bool immutablePrototype) { 11875 RootedObject glob(cx, 11876 JS_NewGlobalObject(cx, &global_class, principals, 11877 JS::DontFireOnNewGlobalHook, options)); 11878 if (!glob) { 11879 return nullptr; 11880 } 11881 11882 { 11883 JSAutoRealm ar(cx, glob); 11884 11885 if (kind == ShellGlobalKind::WindowProxy) { 11886 RootedObject proxy(cx, NewShellWindowProxy(cx, glob)); 11887 if (!proxy) { 11888 return nullptr; 11889 } 11890 js::SetWindowProxy(cx, glob, proxy); 11891 } 11892 11893 #ifndef LAZY_STANDARD_CLASSES 11894 if (!JS::InitRealmStandardClasses(cx)) { 11895 return nullptr; 11896 } 11897 #endif 11898 11899 if (immutablePrototype) { 11900 bool succeeded; 11901 if (!JS_SetImmutablePrototype(cx, glob, &succeeded)) { 11902 return nullptr; 11903 } 11904 MOZ_ASSERT(succeeded, 11905 "a fresh, unexposed global object is always capable of " 11906 "having its [[Prototype]] be immutable"); 11907 } 11908 11909 #ifdef JS_HAS_CTYPES 11910 if (!fuzzingSafe && !JS::InitCTypesClass(cx, glob)) { 11911 return nullptr; 11912 } 11913 #endif 11914 if (!JS_InitReflectParse(cx, glob)) { 11915 return nullptr; 11916 } 11917 if (!JS_DefineDebuggerObject(cx, glob)) { 11918 return nullptr; 11919 } 11920 if (!JS_DefineFunctionsWithHelp(cx, glob, shell_functions) || 11921 !JS_DefineProfilingFunctions(cx, glob)) { 11922 return nullptr; 11923 } 11924 #ifdef FUZZING_JS_FUZZILLI 11925 if (!JS_DefineFunctions(cx, glob, shell_function_fuzzilli_hash)) { 11926 return nullptr; 11927 } 11928 #endif 11929 if (!js::DefineTestingFunctions(cx, glob, fuzzingSafe, 11930 disableOOMFunctions)) { 11931 return nullptr; 11932 } 11933 if (!JS_DefineProperties(cx, glob, TestingProperties)) { 11934 return nullptr; 11935 } 11936 11937 if (!fuzzingSafe) { 11938 if (!JS_DefineFunctionsWithHelp(cx, glob, fuzzing_unsafe_functions)) { 11939 return nullptr; 11940 } 11941 if (!DefineConsole(cx, glob)) { 11942 return nullptr; 11943 } 11944 } 11945 11946 if (!DefineOS(cx, glob, fuzzingSafe, &gOutFile, &gErrFile)) { 11947 return nullptr; 11948 } 11949 11950 if (!js::SupportDifferentialTesting()) { 11951 if (!JS_DefineFunctionsWithHelp(cx, glob, 11952 diff_testing_unsafe_functions)) { 11953 return nullptr; 11954 } 11955 11956 RootedObject performanceObj(cx, JS_NewObject(cx, nullptr)); 11957 if (!performanceObj) { 11958 return nullptr; 11959 } 11960 if (!JS_DefineFunctionsWithHelp(cx, performanceObj, 11961 performance_functions)) { 11962 return nullptr; 11963 } 11964 RootedObject mozMemoryObj(cx, JS_NewObject(cx, nullptr)); 11965 if (!mozMemoryObj) { 11966 return nullptr; 11967 } 11968 RootedObject gcObj(cx, gc::NewMemoryInfoObject(cx)); 11969 if (!gcObj) { 11970 return nullptr; 11971 } 11972 if (!JS_DefineProperty(cx, glob, "performance", performanceObj, 11973 JSPROP_ENUMERATE)) { 11974 return nullptr; 11975 } 11976 if (!JS_DefineProperty(cx, performanceObj, "mozMemory", mozMemoryObj, 11977 JSPROP_ENUMERATE)) { 11978 return nullptr; 11979 } 11980 if (!JS_DefineProperty(cx, mozMemoryObj, "gc", gcObj, JSPROP_ENUMERATE)) { 11981 return nullptr; 11982 } 11983 } 11984 11985 /* Initialize FakeDOMObject. */ 11986 static const js::DOMCallbacks DOMcallbacks = {InstanceClassHasProtoAtDepth, 11987 InstanceClassIsError, 11988 ExtractExceptionInfo}; 11989 SetDOMCallbacks(cx, &DOMcallbacks); 11990 11991 RootedObject domProto( 11992 cx, JS_InitClass(cx, glob, &dom_class, nullptr, "FakeDOMObject", 11993 dom_constructor, 0, dom_props, dom_methods, nullptr, 11994 nullptr)); 11995 if (!domProto) { 11996 return nullptr; 11997 } 11998 11999 // FakeDOMObject.prototype is the only DOM object which needs to retrieved 12000 // in the shell; store it directly instead of creating a separate layer 12001 // (ProtoAndIfaceCache) as done in the browser. 12002 JS::SetReservedSlot(glob, DOM_PROTOTYPE_SLOT, ObjectValue(*domProto)); 12003 12004 /* Initialize FakeDOMObject.prototype */ 12005 InitDOMObject(domProto); 12006 12007 if (!DefineToStringTag(cx, glob, cx->names().global)) { 12008 return nullptr; 12009 } 12010 12011 JS_FireOnNewGlobalObject(cx, glob); 12012 } 12013 12014 return glob; 12015 } 12016 12017 static bool BindScriptArgs(JSContext* cx, OptionParser* op) { 12018 AutoReportException are(cx); 12019 12020 MultiStringRange msr = op->getMultiStringArg("scriptArgs"); 12021 RootedObject scriptArgs(cx); 12022 scriptArgs = JS::NewArrayObject(cx, 0); 12023 if (!scriptArgs) { 12024 return false; 12025 } 12026 12027 if (!JS_DefineProperty(cx, cx->global(), "scriptArgs", scriptArgs, 0)) { 12028 return false; 12029 } 12030 12031 for (size_t i = 0; !msr.empty(); msr.popFront(), ++i) { 12032 const char* scriptArg = msr.front(); 12033 UniqueChars scriptArgUtf8 = JS::EncodeNarrowToUtf8(cx, scriptArg); 12034 if (!scriptArgUtf8) { 12035 return false; 12036 } 12037 RootedString str(cx, NewStringCopyUTF8(cx, scriptArgUtf8.get())); 12038 if (!str || !JS_DefineElement(cx, scriptArgs, i, str, JSPROP_ENUMERATE)) { 12039 return false; 12040 } 12041 } 12042 12043 RootedValue scriptPathValue(cx); 12044 if (const char* scriptPath = op->getStringArg("script")) { 12045 UniqueChars scriptPathUtf8 = JS::EncodeNarrowToUtf8(cx, scriptPath); 12046 if (!scriptPathUtf8) { 12047 return false; 12048 } 12049 RootedString scriptPathString(cx, 12050 NewStringCopyUTF8(cx, scriptPathUtf8.get())); 12051 if (!scriptPathString) { 12052 return false; 12053 } 12054 scriptPathValue = StringValue(scriptPathString); 12055 } else { 12056 scriptPathValue = UndefinedValue(); 12057 } 12058 12059 if (!JS_DefineProperty(cx, cx->global(), "scriptPath", scriptPathValue, 0)) { 12060 return false; 12061 } 12062 12063 return true; 12064 } 12065 12066 static bool OptionFailure(const char* option, const char* str) { 12067 fprintf(stderr, "Unrecognized option for %s: %s\n", option, str); 12068 return false; 12069 } 12070 12071 template <typename... Ts> 12072 auto minVal(Ts... args); 12073 template <typename T> 12074 auto minVal(T a) { 12075 return a; 12076 } 12077 12078 template <typename T, typename... Ts> 12079 auto minVal(T a, Ts... args) { 12080 return std::min(a, minVal(args...)); 12081 } 12082 12083 [[nodiscard]] static bool ProcessArgs(JSContext* cx, OptionParser* op) { 12084 ShellContext* sc = GetShellContext(cx); 12085 12086 /* |scriptArgs| gets bound on the global before any code is run. */ 12087 if (!BindScriptArgs(cx, op)) { 12088 return false; 12089 } 12090 12091 MultiStringRange filePaths = op->getMultiStringOption('f'); 12092 MultiStringRange utf16FilePaths = op->getMultiStringOption('u'); 12093 MultiStringRange preludePaths = op->getMultiStringOption('p'); 12094 MultiStringRange codeChunks = op->getMultiStringOption('e'); 12095 MultiStringRange modulePaths = op->getMultiStringOption('m'); 12096 12097 #ifdef FUZZING_JS_FUZZILLI 12098 // Check for REPRL file source 12099 if (op->getBoolOption("reprl")) { 12100 return FuzzilliReprlGetAndRun(cx); 12101 } 12102 #endif /* FUZZING_JS_FUZZILLI */ 12103 12104 if (filePaths.empty() && utf16FilePaths.empty() && codeChunks.empty() && 12105 modulePaths.empty() && !op->getStringArg("script")) { 12106 // Always use the interactive shell when -i is used. Without -i we let 12107 // Process figure it out based on isatty. 12108 bool forceTTY = op->getBoolOption('i'); 12109 return Process(cx, nullptr, forceTTY, FileScript); 12110 } 12111 12112 while (!preludePaths.empty() || !filePaths.empty() || 12113 !utf16FilePaths.empty() || !codeChunks.empty() || 12114 !modulePaths.empty()) { 12115 size_t ppArgno = preludePaths.empty() ? SIZE_MAX : preludePaths.argno(); 12116 size_t fpArgno = filePaths.empty() ? SIZE_MAX : filePaths.argno(); 12117 size_t ufpArgno = 12118 utf16FilePaths.empty() ? SIZE_MAX : utf16FilePaths.argno(); 12119 size_t ccArgno = codeChunks.empty() ? SIZE_MAX : codeChunks.argno(); 12120 size_t mpArgno = modulePaths.empty() ? SIZE_MAX : modulePaths.argno(); 12121 size_t minArgno = minVal(ppArgno, fpArgno, ufpArgno, ccArgno, mpArgno); 12122 12123 if (ppArgno == minArgno) { 12124 UniqueChars path = JS::EncodeNarrowToUtf8(cx, preludePaths.front()); 12125 if (!path) { 12126 return false; 12127 } 12128 if (!Process(cx, path.get(), false, PreludeScript)) { 12129 return false; 12130 } 12131 12132 preludePaths.popFront(); 12133 continue; 12134 } 12135 12136 if (fpArgno == minArgno) { 12137 UniqueChars path = JS::EncodeNarrowToUtf8(cx, filePaths.front()); 12138 if (!path) { 12139 return false; 12140 } 12141 if (!Process(cx, path.get(), false, FileScript)) { 12142 return false; 12143 } 12144 12145 filePaths.popFront(); 12146 continue; 12147 } 12148 12149 if (ufpArgno == minArgno) { 12150 UniqueChars path = JS::EncodeNarrowToUtf8(cx, utf16FilePaths.front()); 12151 if (!path) { 12152 return false; 12153 } 12154 if (!Process(cx, path.get(), false, FileScriptUtf16)) { 12155 return false; 12156 } 12157 12158 utf16FilePaths.popFront(); 12159 continue; 12160 } 12161 12162 if (ccArgno == minArgno) { 12163 UniqueChars code = JS::EncodeNarrowToUtf8(cx, codeChunks.front()); 12164 if (!code) { 12165 return false; 12166 } 12167 12168 // Command line scripts are always parsed with full-parse to evaluate 12169 // conditions which might filter code coverage conditions. 12170 JS::CompileOptions opts(cx); 12171 opts.setFileAndLine("-e", 1).setForceFullParse(); 12172 12173 JS::SourceText<Utf8Unit> srcBuf; 12174 if (!srcBuf.init(cx, code.get(), strlen(code.get()), 12175 JS::SourceOwnership::Borrowed)) { 12176 return false; 12177 } 12178 12179 RootedValue rval(cx); 12180 if (!JS::Evaluate(cx, opts, srcBuf, &rval)) { 12181 return false; 12182 } 12183 12184 codeChunks.popFront(); 12185 if (sc->quitting) { 12186 break; 12187 } 12188 12189 continue; 12190 } 12191 12192 MOZ_ASSERT(mpArgno == minArgno); 12193 12194 UniqueChars path = JS::EncodeNarrowToUtf8(cx, modulePaths.front()); 12195 if (!path) { 12196 return false; 12197 } 12198 if (!Process(cx, path.get(), false, FileModule)) { 12199 return false; 12200 } 12201 12202 modulePaths.popFront(); 12203 } 12204 12205 if (sc->quitting) { 12206 return false; 12207 } 12208 12209 /* The |script| argument is processed after all options. */ 12210 if (const char* path = op->getStringArg("script")) { 12211 UniqueChars pathUtf8 = JS::EncodeNarrowToUtf8(cx, path); 12212 if (!pathUtf8) { 12213 return false; 12214 } 12215 if (!Process(cx, pathUtf8.get(), false, FileScript)) { 12216 return false; 12217 } 12218 } 12219 12220 if (op->getBoolOption('i')) { 12221 if (!Process(cx, nullptr, true, FileScript)) { 12222 return false; 12223 } 12224 } 12225 12226 return true; 12227 } 12228 12229 static void SetWorkerContextOptions(JSContext* cx) { 12230 // Copy option values from the main thread. 12231 JS::ContextOptionsRef(cx) 12232 .setAsmJS(enableAsmJS) 12233 .setWasm(enableWasm) 12234 .setWasmBaseline(enableWasmBaseline) 12235 .setWasmIon(enableWasmOptimizing) 12236 12237 .setTestWasmAwaitTier2(enableTestWasmAwaitTier2) 12238 .setSourcePragmas(enableSourcePragmas); 12239 12240 cx->runtime()->setOffthreadBaselineCompilationEnabled( 12241 offthreadBaselineCompilation); 12242 cx->runtime()->setOffthreadIonCompilationEnabled(offthreadIonCompilation); 12243 cx->runtime()->profilingScripts = 12244 enableCodeCoverage || enableDisassemblyDumps; 12245 12246 #ifdef JS_GC_ZEAL 12247 if (gZealBits && gZealFrequency) { 12248 for (size_t i = 0; i < size_t(gc::ZealMode::Count); i++) { 12249 if (gZealBits & (1 << i)) { 12250 cx->runtime()->gc.setZeal(i, gZealFrequency); 12251 } 12252 } 12253 } 12254 #endif 12255 12256 JS_SetNativeStackQuota(cx, gWorkerStackSize); 12257 } 12258 12259 [[nodiscard]] static bool PrintUnhandledRejection( 12260 JSContext* cx, Handle<PromiseObject*> promise) { 12261 RootedValue reason(cx, promise->reason()); 12262 RootedObject site(cx, promise->resolutionSite()); 12263 12264 RootedString str(cx, JS_ValueToSource(cx, reason)); 12265 if (!str) { 12266 return false; 12267 } 12268 12269 UniqueChars utf8chars = JS_EncodeStringToUTF8(cx, str); 12270 if (!utf8chars) { 12271 return false; 12272 } 12273 12274 FILE* fp = ErrorFilePointer(); 12275 fprintf(fp, "Unhandled rejection: %s\n", utf8chars.get()); 12276 12277 if (!site) { 12278 fputs("(no stack trace available)\n", stderr); 12279 return true; 12280 } 12281 12282 JSPrincipals* principals = cx->realm()->principals(); 12283 RootedString stackStr(cx); 12284 if (!BuildStackString(cx, principals, site, &stackStr, 2)) { 12285 return false; 12286 } 12287 12288 UniqueChars stack = JS_EncodeStringToUTF8(cx, stackStr); 12289 if (!stack) { 12290 return false; 12291 } 12292 12293 fputs("Stack:\n", fp); 12294 fputs(stack.get(), fp); 12295 12296 return true; 12297 } 12298 12299 [[nodiscard]] static bool ReportUnhandledRejections(JSContext* cx) { 12300 ShellContext* sc = GetShellContext(cx); 12301 if (!sc->trackUnhandledRejections) { 12302 return true; 12303 } 12304 12305 if (!sc->unhandledRejectedPromises) { 12306 return true; 12307 } 12308 12309 AutoRealm ar(cx, sc->unhandledRejectedPromises); 12310 12311 if (sc->unhandledRejectedPromises->size() == 0) { 12312 return true; 12313 } 12314 12315 sc->exitCode = EXITCODE_RUNTIME_ERROR; 12316 12317 RootedValue iter(cx); 12318 if (!SetObject::iterator(cx, SetObject::IteratorKind::Values, 12319 sc->unhandledRejectedPromises, &iter)) { 12320 return false; 12321 } 12322 12323 Rooted<SetIteratorObject*> iterObj(cx, 12324 &iter.toObject().as<SetIteratorObject>()); 12325 JSObject* obj = SetIteratorObject::createResult(cx); 12326 if (!obj) { 12327 return false; 12328 } 12329 12330 Rooted<ArrayObject*> resultObj(cx, &obj->as<ArrayObject>()); 12331 while (true) { 12332 bool done = SetIteratorObject::next(iterObj, resultObj); 12333 if (done) { 12334 break; 12335 } 12336 12337 RootedObject obj(cx, &resultObj->getDenseElement(0).toObject()); 12338 Rooted<PromiseObject*> promise(cx, obj->maybeUnwrapIf<PromiseObject>()); 12339 if (!promise) { 12340 FILE* fp = ErrorFilePointer(); 12341 fputs( 12342 "Unhandled rejection: dead proxy found in unhandled " 12343 "rejections set\n", 12344 fp); 12345 continue; 12346 } 12347 12348 AutoRealm ar2(cx, promise); 12349 12350 if (!PrintUnhandledRejection(cx, promise)) { 12351 return false; 12352 } 12353 } 12354 12355 sc->unhandledRejectedPromises = nullptr; 12356 12357 return true; 12358 } 12359 12360 bool ShellContext::registerWithCx(JSContext* cx) { 12361 cx_ = cx; 12362 JS_SetContextPrivate(cx, this); 12363 12364 if (isWorker) { 12365 SetWorkerContextOptions(cx); 12366 } 12367 12368 JS::SetWarningReporter(cx, WarningReporter); 12369 JS_SetFutexCanWait(cx); 12370 JS_InitDestroyPrincipalsCallback(cx, ShellPrincipals::destroy); 12371 JS_SetDestroyCompartmentCallback(cx, DestroyShellCompartmentPrivate); 12372 js::SetWindowProxyClass(cx, &ShellWindowProxyClass); 12373 12374 js::UseInternalJobQueues(cx); 12375 12376 js::SetPreserveWrapperCallbacks(cx, DummyPreserveWrapperCallback, 12377 DummyHasReleasedWrapperCallback); 12378 12379 JS::SetHostCleanupFinalizationRegistryCallback( 12380 cx, ShellCleanupFinalizationRegistryCallback, this); 12381 JS_AddExtraGCRootsTracer(cx, TraceBlackRoots, nullptr); 12382 JS_SetGrayGCRootsTracer(cx, TraceGrayRoots, nullptr); 12383 12384 return true; 12385 } 12386 12387 ShellContext::~ShellContext() { 12388 markObservers.reset(); 12389 if (cx_) { 12390 JS_SetContextPrivate(cx_, nullptr); 12391 JS::SetHostCleanupFinalizationRegistryCallback(cx_, nullptr, nullptr); 12392 JS_SetGrayGCRootsTracer(cx_, nullptr, nullptr); 12393 JS_RemoveExtraGCRootsTracer(cx_, TraceBlackRoots, nullptr); 12394 } 12395 MOZ_ASSERT(offThreadJobs.empty()); 12396 } 12397 12398 static int Shell(JSContext* cx, OptionParser* op) { 12399 #ifdef JS_STRUCTURED_SPEW 12400 cx->spewer().enableSpewing(); 12401 #endif 12402 12403 auto exitShell = MakeScopeExit([&] { 12404 #ifdef JS_STRUCTURED_SPEW 12405 cx->spewer().disableSpewing(); 12406 #endif 12407 }); 12408 12409 #ifdef MOZ_CODE_COVERAGE 12410 InstallCoverageSignalHandlers(); 12411 #endif 12412 12413 Maybe<JS::AutoDisableGenerationalGC> noggc; 12414 if (op->getBoolOption("no-ggc")) { 12415 noggc.emplace(cx); 12416 } 12417 12418 Maybe<AutoDisableCompactingGC> nocgc; 12419 if (op->getBoolOption("no-cgc")) { 12420 nocgc.emplace(cx); 12421 } 12422 12423 #ifdef DEBUG 12424 if (op->getBoolOption("differential-testing")) { 12425 JS::SetSupportDifferentialTesting(true); 12426 } 12427 #endif 12428 12429 if (op->getBoolOption("disable-oom-functions")) { 12430 disableOOMFunctions = true; 12431 } 12432 12433 if (op->getBoolOption("more-compartments")) { 12434 defaultToSameCompartment = false; 12435 } 12436 12437 bool reprl_mode = FuzzilliUseReprlMode(op); 12438 12439 // Begin REPRL Loop 12440 int result = EXIT_SUCCESS; 12441 do { 12442 JS::RealmOptions options; 12443 SetStandardRealmOptions(options); 12444 RootedObject glob( 12445 cx, NewGlobalObject(cx, options, nullptr, ShellGlobalKind::WindowProxy, 12446 /* immutablePrototype = */ true)); 12447 if (!glob) { 12448 return 1; 12449 } 12450 12451 JSAutoRealm ar(cx, glob); 12452 12453 ShellContext* sc = GetShellContext(cx); 12454 if (!sc->moduleLoader && !InitModuleLoader(cx, *op)) { 12455 return EXIT_FAILURE; 12456 } 12457 12458 #ifdef FUZZING_INTERFACES 12459 if (fuzzHaveModule) { 12460 return FuzzJSRuntimeStart(cx, &sArgc, &sArgv); 12461 } 12462 #endif 12463 12464 sc->exitCode = 0; 12465 result = EXIT_SUCCESS; 12466 { 12467 AutoReportException are(cx); 12468 if (!ProcessArgs(cx, op) && !sc->quitting) { 12469 result = EXITCODE_RUNTIME_ERROR; 12470 } 12471 } 12472 12473 /* 12474 * The job queue must be drained even on error to finish outstanding async 12475 * tasks before the main thread JSRuntime is torn down. Drain after 12476 * uncaught exceptions have been reported since draining runs callbacks. 12477 */ 12478 RunShellJobs(cx); 12479 12480 // Only if there's no other error, report unhandled rejections. 12481 if (!result && !sc->exitCode) { 12482 AutoReportException are(cx); 12483 if (!ReportUnhandledRejections(cx)) { 12484 FILE* fp = ErrorFilePointer(); 12485 fputs("Error while printing unhandled rejection\n", fp); 12486 } 12487 } 12488 12489 if (sc->exitCode) { 12490 result = sc->exitCode; 12491 } 12492 12493 #ifdef FUZZING_JS_FUZZILLI 12494 if (reprl_mode) { 12495 fflush(stdout); 12496 fflush(stderr); 12497 // Send return code to parent and reset edge counters. 12498 int status = (result & 0xff) << 8; 12499 if (js::SupportDifferentialTesting()) { 12500 struct { 12501 int status; 12502 uint32_t execHash; 12503 uint32_t execHashInputs; 12504 } s; 12505 s.status = status; 12506 s.execHash = cx->executionHash; 12507 s.execHashInputs = cx->executionHashInputs; 12508 MOZ_RELEASE_ASSERT(write(REPRL_CWFD, &s, 12) == 12); 12509 } else { 12510 MOZ_RELEASE_ASSERT(write(REPRL_CWFD, &status, 4) == 4); 12511 } 12512 __sanitizer_cov_reset_edgeguards(); 12513 cx->executionHash = 1; 12514 cx->executionHashInputs = 0; 12515 } 12516 #endif 12517 12518 if (enableDisassemblyDumps) { 12519 AutoReportException are(cx); 12520 if (!js::DumpRealmPCCounts(cx)) { 12521 result = EXITCODE_OUT_OF_MEMORY; 12522 } 12523 } 12524 12525 // End REPRL loop 12526 } while (reprl_mode); 12527 12528 return result; 12529 } 12530 12531 // Used to allocate memory when jemalloc isn't yet initialized. 12532 JS_DECLARE_NEW_METHODS(SystemAlloc_New, malloc, static) 12533 12534 static void SetOutputFile(const char* const envVar, RCFile* defaultOut, 12535 RCFile** outFileP) { 12536 RCFile* outFile; 12537 12538 const char* outPath = getenv(envVar); 12539 FILE* newfp; 12540 if (outPath && *outPath && (newfp = fopen(outPath, "w"))) { 12541 outFile = SystemAlloc_New<RCFile>(newfp); 12542 } else { 12543 outFile = defaultOut; 12544 } 12545 12546 if (!outFile) { 12547 MOZ_CRASH("Failed to allocate output file"); 12548 } 12549 12550 outFile->acquire(); 12551 *outFileP = outFile; 12552 } 12553 12554 static void PreInit() { 12555 #ifdef XP_WIN 12556 const char* crash_option = getenv("XRE_NO_WINDOWS_CRASH_DIALOG"); 12557 if (crash_option && crash_option[0] == '1') { 12558 // Disable the segfault dialog. We want to fail the tests immediately 12559 // instead of hanging automation. 12560 UINT newMode = SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX; 12561 UINT prevMode = SetErrorMode(newMode); 12562 SetErrorMode(prevMode | newMode); 12563 } 12564 #endif 12565 } 12566 12567 #ifndef JS_WITHOUT_NSPR 12568 class AutoLibraryLoader { 12569 Vector<PRLibrary*, 4, SystemAllocPolicy> libraries; 12570 12571 public: 12572 ~AutoLibraryLoader() { 12573 for (auto dll : libraries) { 12574 PR_UnloadLibrary(dll); 12575 } 12576 } 12577 12578 PRLibrary* load(const char* path) { 12579 PRLibSpec libSpec; 12580 libSpec.type = PR_LibSpec_Pathname; 12581 libSpec.value.pathname = path; 12582 PRLibrary* dll = PR_LoadLibraryWithFlags(libSpec, PR_LD_NOW | PR_LD_GLOBAL); 12583 if (!dll) { 12584 fprintf(stderr, "LoadLibrary '%s' failed with code %d\n", path, 12585 PR_GetError()); 12586 MOZ_CRASH("Failed to load library"); 12587 } 12588 12589 MOZ_ALWAYS_TRUE(libraries.append(dll)); 12590 return dll; 12591 } 12592 }; 12593 #endif 12594 12595 static bool ReadSelfHostedXDRFile(JSContext* cx, FileContents& buf) { 12596 FILE* file = fopen(selfHostedXDRPath, "rb"); 12597 if (!file) { 12598 fprintf(stderr, "Can't open self-hosted stencil XDR file.\n"); 12599 return false; 12600 } 12601 AutoCloseFile autoClose(file); 12602 12603 struct stat st; 12604 if (fstat(fileno(file), &st) < 0) { 12605 fprintf(stderr, "Unable to stat self-hosted stencil XDR file.\n"); 12606 return false; 12607 } 12608 12609 if (st.st_size >= INT32_MAX) { 12610 fprintf(stderr, "self-hosted stencil XDR file too large.\n"); 12611 return false; 12612 } 12613 uint32_t filesize = uint32_t(st.st_size); 12614 12615 if (!buf.growBy(filesize)) { 12616 return false; 12617 } 12618 size_t cc = fread(buf.begin(), 1, filesize, file); 12619 if (cc != filesize) { 12620 fprintf(stderr, "Short read on self-hosted stencil XDR file.\n"); 12621 return false; 12622 } 12623 12624 return true; 12625 } 12626 12627 static bool WriteSelfHostedXDRFile(JSContext* cx, JS::SelfHostedCache buffer) { 12628 FILE* file = fopen(selfHostedXDRPath, "wb"); 12629 if (!file) { 12630 JS_ReportErrorUTF8(cx, "Can't open self-hosted stencil XDR file."); 12631 return false; 12632 } 12633 AutoCloseFile autoClose(file); 12634 12635 size_t cc = fwrite(buffer.Elements(), 1, buffer.LengthBytes(), file); 12636 if (cc != buffer.LengthBytes()) { 12637 JS_ReportErrorUTF8(cx, "Short write on self-hosted stencil XDR file."); 12638 return false; 12639 } 12640 12641 return true; 12642 } 12643 12644 static bool SetGCParameterFromArg(JSContext* cx, char* arg) { 12645 char* c = strchr(arg, '='); 12646 if (!c) { 12647 fprintf(stderr, 12648 "Error: --gc-param argument '%s' must be of the form " 12649 "name=decimalValue\n", 12650 arg); 12651 return false; 12652 } 12653 12654 *c = '\0'; 12655 const char* name = arg; 12656 const char* valueStr = c + 1; 12657 12658 JSGCParamKey key; 12659 bool writable; 12660 if (!GetGCParameterInfo(name, &key, &writable)) { 12661 fprintf(stderr, "Error: Unknown GC parameter name '%s'\n", name); 12662 fprintf(stderr, "Writable GC parameter names are:\n"); 12663 #define PRINT_WRITABLE_PARAM_NAME(name, _, writable) \ 12664 if (writable) { \ 12665 fprintf(stderr, " %s\n", name); \ 12666 } 12667 FOR_EACH_GC_PARAM(PRINT_WRITABLE_PARAM_NAME) 12668 #undef PRINT_WRITABLE_PARAM_NAME 12669 return false; 12670 } 12671 12672 if (!writable) { 12673 fprintf(stderr, "Error: GC parameter '%s' is not writable\n", name); 12674 return false; 12675 } 12676 12677 char* end = nullptr; 12678 unsigned long int value = strtoul(valueStr, &end, 10); 12679 if (end == valueStr || *end) { 12680 fprintf(stderr, 12681 "Error: Could not parse '%s' as decimal for GC parameter '%s'\n", 12682 valueStr, name); 12683 return false; 12684 } 12685 12686 uint32_t paramValue = uint32_t(value); 12687 if (value == ULONG_MAX || value != paramValue || 12688 !cx->runtime()->gc.setParameter(cx, key, paramValue)) { 12689 fprintf(stderr, "Error: Value %s is out of range for GC parameter '%s'\n", 12690 valueStr, name); 12691 return false; 12692 } 12693 12694 return true; 12695 } 12696 12697 int main(int argc, char** argv) { 12698 PreInit(); 12699 12700 sArgc = argc; 12701 sArgv = argv; 12702 12703 int result; 12704 12705 setlocale(LC_ALL, ""); 12706 12707 // Special-case stdout and stderr. We bump their refcounts to prevent them 12708 // from getting closed and then having some printf fail somewhere. 12709 RCFile rcStdout(stdout); 12710 rcStdout.acquire(); 12711 RCFile rcStderr(stderr); 12712 rcStderr.acquire(); 12713 12714 SetOutputFile("JS_STDOUT", &rcStdout, &gOutFile); 12715 SetOutputFile("JS_STDERR", &rcStderr, &gErrFile); 12716 12717 #ifdef MOZ_MEMORY 12718 // Use a larger jemalloc page cache. This should match the value for browser 12719 // foreground processes in ContentChild::RecvNotifyProcessPriorityChanged. 12720 moz_set_max_dirty_page_modifier(4); 12721 #endif 12722 12723 OptionParser op("Usage: {progname} [options] [[script] scriptArgs*]"); 12724 if (!InitOptionParser(op)) { 12725 return EXIT_FAILURE; 12726 } 12727 12728 switch (op.parseArgs(argc, argv)) { 12729 case OptionParser::EarlyExit: 12730 return EXIT_SUCCESS; 12731 case OptionParser::ParseError: 12732 op.printHelp(argv[0]); 12733 return EXIT_FAILURE; 12734 case OptionParser::Fail: 12735 return EXIT_FAILURE; 12736 case OptionParser::Okay: 12737 break; 12738 } 12739 12740 if (op.getHelpOption()) { 12741 return EXIT_SUCCESS; 12742 } 12743 12744 if (!SetGlobalOptionsPreJSInit(op)) { 12745 return EXIT_FAILURE; 12746 } 12747 12748 if (!JS::SetLoggingInterface(shellLoggingInterface)) { 12749 return 1; 12750 } 12751 char* logopts = getenv("MOZ_LOG"); 12752 if (logopts) { 12753 ParseLoggerOptions(mozilla::Range(logopts, strlen(logopts))); 12754 } 12755 12756 // Start the engine. 12757 if (const char* message = JS_InitWithFailureDiagnostic()) { 12758 fprintf(gErrFile->fp, "JS_Init failed: %s\n", message); 12759 return 1; 12760 } 12761 12762 // `selfHostedXDRBuffer` contains XDR buffer of the self-hosted JS. 12763 // A part of it is borrowed by ImmutableScriptData of the self-hosted scripts. 12764 // 12765 // This buffer should outlive JS_Shutdown. 12766 Maybe<FileContents> selfHostedXDRBuffer; 12767 12768 auto shutdownEngine = MakeScopeExit([] { JS_ShutDown(); }); 12769 12770 if (!SetGlobalOptionsPostJSInit(op)) { 12771 return EXIT_FAILURE; 12772 } 12773 12774 if (!InitSharedObjectMailbox()) { 12775 return EXIT_FAILURE; 12776 } 12777 12778 JS::SetProcessBuildIdOp(ShellBuildId); 12779 12780 /* Use the same parameters as the browser in xpcjsruntime.cpp. */ 12781 JSContext* const cx = JS_NewContext(JS::DefaultHeapMaxBytes); 12782 if (!cx) { 12783 return 1; 12784 } 12785 12786 // Register telemetry callbacks. 12787 JS_SetAccumulateTelemetryCallback(cx, AccumulateTelemetryDataCallback); 12788 JS_SetSetUseCounterCallback(cx, SetUseCounterCallback); 12789 12790 auto destroyCx = MakeScopeExit([cx] { JS_DestroyContext(cx); }); 12791 12792 UniquePtr<ShellContext> sc = 12793 MakeUnique<ShellContext>(cx, ShellContext::MainThread); 12794 if (!sc || !sc->registerWithCx(cx)) { 12795 return 1; 12796 } 12797 12798 if (!SetContextOptions(cx, op)) { 12799 return 1; 12800 } 12801 12802 JS_SetTrustedPrincipals(cx, &ShellPrincipals::fullyTrusted); 12803 JS_SetSecurityCallbacks(cx, &ShellPrincipals::securityCallbacks); 12804 12805 JS_AddInterruptCallback(cx, ShellInterruptCallback); 12806 12807 JS::SetGCSliceCallback(cx, GCSliceCallback); 12808 12809 bufferStreamState = js_new<ExclusiveWaitableData<BufferStreamState>>( 12810 mutexid::BufferStreamState); 12811 if (!bufferStreamState) { 12812 return 1; 12813 } 12814 auto shutdownBufferStreams = MakeScopeExit([] { 12815 ShutdownBufferStreams(); 12816 js_delete(bufferStreamState); 12817 }); 12818 JS::InitConsumeStreamCallback(cx, ConsumeBufferSource, ReportStreamError); 12819 12820 JS::SetPromiseRejectionTrackerCallback( 12821 cx, ForwardingPromiseRejectionTrackerCallback); 12822 12823 JS::dbg::SetDebuggerMallocSizeOf(cx, moz_malloc_size_of); 12824 12825 auto shutdownShellThreads = MakeScopeExit([cx] { 12826 KillWatchdog(cx); 12827 KillWorkerThreads(cx); 12828 DestructSharedObjectMailbox(); 12829 CancelOffThreadJobsForRuntime(cx); 12830 }); 12831 12832 // The file content should stay alive as long as Worker thread can be 12833 // initialized. 12834 JS::SelfHostedCache xdrSpan = nullptr; 12835 JS::SelfHostedWriter xdrWriter = nullptr; 12836 if (selfHostedXDRPath) { 12837 if (encodeSelfHostedCode) { 12838 xdrWriter = WriteSelfHostedXDRFile; 12839 } else { 12840 selfHostedXDRBuffer.emplace(cx); 12841 if (ReadSelfHostedXDRFile(cx, *selfHostedXDRBuffer)) { 12842 MOZ_ASSERT(selfHostedXDRBuffer->length() > 0); 12843 JS::SelfHostedCache span(selfHostedXDRBuffer->begin(), 12844 selfHostedXDRBuffer->end()); 12845 xdrSpan = span; 12846 } else { 12847 fprintf(stderr, "Falling back on parsing source.\n"); 12848 selfHostedXDRPath = nullptr; 12849 } 12850 } 12851 } 12852 12853 if (!JS::InitSelfHostedCode(cx, xdrSpan, xdrWriter)) { 12854 return 1; 12855 } 12856 12857 EnvironmentPreparer environmentPreparer(cx); 12858 12859 JS::SetProcessLargeAllocationFailureCallback(my_LargeAllocFailCallback); 12860 12861 if (op.getBoolOption("wasm-compile-and-serialize")) { 12862 #ifdef __wasi__ 12863 MOZ_CRASH("WASI doesn't support wasm"); 12864 #else 12865 if (!WasmCompileAndSerialize(cx)) { 12866 // Errors have been printed directly to stderr. 12867 MOZ_ASSERT(!cx->isExceptionPending()); 12868 return EXIT_FAILURE; 12869 } 12870 #endif 12871 return EXIT_SUCCESS; 12872 } 12873 12874 result = Shell(cx, &op); 12875 12876 #ifdef DEBUG 12877 if (OOM_printAllocationCount) { 12878 printf("OOM max count: %" PRIu64 "\n", js::oom::simulator.counter()); 12879 } 12880 #endif 12881 12882 return result; 12883 } 12884 12885 bool InitOptionParser(OptionParser& op) { 12886 op.setDescription( 12887 "The SpiderMonkey shell provides a command line interface to the " 12888 "JavaScript engine. Code and file options provided via the command line " 12889 "are " 12890 "run left to right. If provided, the optional script argument is run " 12891 "after " 12892 "all options have been processed. Just-In-Time compilation modes may be " 12893 "enabled via " 12894 "command line options."); 12895 op.setDescriptionWidth(72); 12896 op.setHelpWidth(80); 12897 op.setVersion(JS_GetImplementationVersion()); 12898 12899 if (!op.addMultiStringOption( 12900 'f', "file", "PATH", 12901 "File path to run, parsing file contents as UTF-8") || 12902 !op.addMultiStringOption( 12903 'u', "utf16-file", "PATH", 12904 "File path to run, inflating the file's UTF-8 contents to UTF-16 and " 12905 "then parsing that") || 12906 !op.addMultiStringOption('m', "module", "PATH", "Module path to run") || 12907 !op.addMultiStringOption('p', "prelude", "PATH", "Prelude path to run") || 12908 !op.addMultiStringOption('e', "execute", "CODE", "Inline code to run") || 12909 !op.addStringOption('\0', "selfhosted-xdr-path", "[filename]", 12910 "Read/Write selfhosted script data from/to the given " 12911 "XDR file") || 12912 !op.addStringOption('\0', "selfhosted-xdr-mode", "(encode,decode,off)", 12913 "Whether to encode/decode data of the file provided" 12914 "with --selfhosted-xdr-path.") || 12915 !op.addBoolOption('i', "shell", "Enter prompt after running code") || 12916 !op.addBoolOption('c', "compileonly", 12917 "Only compile, don't run (syntax checking mode)") || 12918 !op.addBoolOption('w', "warnings", "Emit warnings") || 12919 !op.addBoolOption('W', "nowarnings", "Don't emit warnings") || 12920 !op.addBoolOption('D', "dump-bytecode", 12921 "Dump bytecode with exec count for all scripts") || 12922 !op.addBoolOption('b', "print-timing", 12923 "Print sub-ms runtime for each file that's run") || 12924 !op.addBoolOption('\0', "code-coverage", 12925 "Enable code coverage instrumentation.") || 12926 !op.addBoolOption( 12927 '\0', "disable-parser-deferred-alloc", 12928 "Disable deferred allocation of GC objects until after parser") || 12929 #ifdef DEBUG 12930 !op.addBoolOption('O', "print-alloc", 12931 "Print the number of allocations at exit") || 12932 #endif 12933 !op.addOptionalStringArg("script", 12934 "A script to execute (after all options)") || 12935 !op.addOptionalMultiStringArg( 12936 "scriptArgs", 12937 "String arguments to bind as |scriptArgs| in the " 12938 "shell's global") || 12939 !op.addIntOption( 12940 '\0', "cpu-count", "COUNT", 12941 "Set the number of CPUs (hardware threads) to COUNT, the " 12942 "default is the actual number of CPUs. The total number of " 12943 "background helper threads is the CPU count plus some constant.", 12944 -1) || 12945 !op.addIntOption('\0', "thread-count", "COUNT", "Alias for --cpu-count.", 12946 -1) || 12947 !op.addBoolOption('\0', "ion", "Enable IonMonkey (default)") || 12948 !op.addBoolOption('\0', "no-ion", "Disable IonMonkey") || 12949 !op.addBoolOption('\0', "no-ion-for-main-context", 12950 "Disable IonMonkey for the main context only") || 12951 !op.addIntOption('\0', "inlining-entry-threshold", "COUNT", 12952 "The minimum stub entry count before trial-inlining a" 12953 " call", 12954 -1) || 12955 !op.addIntOption('\0', "small-function-length", "COUNT", 12956 "The maximum bytecode length of a 'small function' for " 12957 "the purpose of inlining.", 12958 -1) || 12959 !op.addBoolOption('\0', "only-inline-selfhosted", 12960 "Only inline selfhosted functions") || 12961 !op.addBoolOption('\0', "asmjs", "Enable asm.js compilation") || 12962 !op.addStringOption( 12963 '\0', "wasm-compiler", "[option]", 12964 "Choose to enable a subset of the wasm compilers, valid options are " 12965 "'none', 'baseline', 'ion', 'optimizing', " 12966 "'baseline+ion', 'baseline+optimizing'.") || 12967 !op.addBoolOption('\0', "wasm-verbose", 12968 "Enable WebAssembly verbose logging") || 12969 !op.addBoolOption('\0', "disable-wasm-huge-memory", 12970 "Disable WebAssembly huge memory") || 12971 !op.addBoolOption('\0', "test-wasm-await-tier2", 12972 "Forcibly activate tiering and block " 12973 "instantiation on completion of tier2") || 12974 !op.addBoolOption('\0', "no-native-regexp", 12975 "Disable native regexp compilation") || 12976 !op.addIntOption( 12977 '\0', "regexp-warmup-threshold", "COUNT", 12978 "Wait for COUNT invocations before compiling regexps to native code " 12979 "(default 10)", 12980 -1) || 12981 !op.addBoolOption('\0', "trace-regexp-parser", "Trace regexp parsing") || 12982 !op.addBoolOption('\0', "trace-regexp-assembler", 12983 "Trace regexp assembler") || 12984 !op.addBoolOption('\0', "trace-regexp-interpreter", 12985 "Trace regexp interpreter") || 12986 !op.addBoolOption('\0', "trace-regexp-peephole", 12987 "Trace regexp peephole optimization") || 12988 !op.addBoolOption('\0', "less-debug-code", 12989 "Emit less machine code for " 12990 "checking assertions under DEBUG.") || 12991 !op.addBoolOption('\0', "disable-tosource", "Disable toSource/uneval") || 12992 !op.addBoolOption('\0', "disable-property-error-message-fix", 12993 "Disable fix for the error message when accessing " 12994 "property of null or undefined") || 12995 !op.addBoolOption('\0', "enable-iterator-helpers", 12996 "Enable iterator helpers") || 12997 !op.addBoolOption('\0', "enable-async-iterator-helpers", 12998 "Enable async iterator helpers") || 12999 !op.addBoolOption('\0', "enable-shadow-realms", "Enable ShadowRealms") || 13000 !op.addBoolOption('\0', "disable-array-grouping", 13001 "Disable Object.groupBy and Map.groupBy") || 13002 !op.addBoolOption('\0', "enable-symbols-as-weakmap-keys", 13003 "Enable Symbols As WeakMap keys") || 13004 !op.addBoolOption('\0', "no-symbols-as-weakmap-keys", 13005 "Disable Symbols As WeakMap keys") || 13006 !op.addBoolOption('\0', "enable-uint8array-base64", 13007 "Enable Uint8Array base64/hex methods") || 13008 !op.addBoolOption('\0', "enable-top-level-await", 13009 "Enable top-level await") || 13010 !op.addStringOption('\0', "shared-memory", "on/off", 13011 "SharedArrayBuffer and Atomics " 13012 #if SHARED_MEMORY_DEFAULT 13013 "(default: on, off to disable)" 13014 #else 13015 "(default: off, on to enable)" 13016 #endif 13017 ) || 13018 !op.addStringOption('\0', "spectre-mitigations", "on/off", 13019 "Whether Spectre mitigations are enabled (default: " 13020 "off, on to enable)") || 13021 !op.addStringOption('\0', "write-protect-code", "on/off", 13022 "Whether the W^X policy is enforced to mark JIT code " 13023 "pages as either writable or executable but never " 13024 "both at the same time (default: on, off to " 13025 "disable)") || 13026 !op.addStringOption('\0', "cache-ir-stubs", "on/off/call", 13027 "Use CacheIR stubs (default: on, off to disable, " 13028 "call to enable work-in-progress call ICs)") || 13029 !op.addStringOption('\0', "ion-shared-stubs", "on/off", 13030 "Use shared stubs (default: on, off to disable)") || 13031 !op.addStringOption( 13032 '\0', "stub-folding", "on/off", 13033 "Enable stub folding (default: on, off to disable)") || 13034 !op.addStringOption('\0', "stub-folding-loads-and-stores", "on/off", 13035 "Enable stub folding for load and stores (default: " 13036 "on, off to disable)") || 13037 !op.addStringOption('\0', "ion-scalar-replacement", "on/off", 13038 "Scalar Replacement (default: on, off to disable)") || 13039 !op.addStringOption('\0', "ion-gvn", "[mode]", 13040 "Specify Ion global value numbering:\n" 13041 " off: disable GVN\n" 13042 " on: enable GVN (default)\n") || 13043 !op.addStringOption( 13044 '\0', "ion-licm", "on/off", 13045 "Loop invariant code motion (default: on, off to disable)") || 13046 !op.addStringOption('\0', "ion-edgecase-analysis", "on/off", 13047 "Find edge cases where Ion can avoid bailouts " 13048 "(default: on, off to disable)") || 13049 !op.addStringOption('\0', "ion-pruning", "on/off", 13050 "Branch pruning (default: on, off to disable)") || 13051 !op.addStringOption('\0', "ion-range-analysis", "on/off", 13052 "Range analysis (default: on, off to disable)") || 13053 !op.addStringOption('\0', "ion-sink", "on/off", 13054 "Sink code motion (default: off, on to enable)") || 13055 !op.addStringOption( 13056 '\0', "ion-instruction-reordering", "on/off", 13057 "Instruction reordering (default: off, on to enable)") || 13058 !op.addStringOption( 13059 '\0', "ion-optimize-shapeguards", "on/off", 13060 "Eliminate redundant shape guards (default: on, off to disable)") || 13061 !op.addStringOption( 13062 '\0', "ion-optimize-gcbarriers", "on/off", 13063 "Eliminate redundant GC barriers (default: on, off to disable)") || 13064 !op.addStringOption('\0', "ion-iterator-indices", "on/off", 13065 "Optimize property access in for-in loops " 13066 "(default: on, off to disable)") || 13067 !op.addStringOption('\0', "ion-load-keys", "on/off", 13068 "Atomize property loads used as keys " 13069 "(default: on, off to disable)") || 13070 !op.addBoolOption('\0', "ion-check-range-analysis", 13071 "Range analysis checking") || 13072 !op.addBoolOption('\0', "ion-extra-checks", 13073 "Perform extra dynamic validation checks") || 13074 !op.addStringOption( 13075 '\0', "ion-inlining", "on/off", 13076 "Inline methods where possible (default: on, off to disable)") || 13077 !op.addStringOption( 13078 '\0', "ion-osr", "on/off", 13079 "On-Stack Replacement (default: on, off to disable)") || 13080 !op.addBoolOption('\0', "disable-bailout-loop-check", 13081 "Turn off bailout loop check") || 13082 !op.addBoolOption('\0', "enable-ic-frame-pointers", 13083 "Use frame pointers in all IC stubs") || 13084 !op.addBoolOption('\0', "scalar-replace-arguments", 13085 "Use scalar replacement to optimize ArgumentsObject") || 13086 !op.addStringOption( 13087 '\0', "ion-limit-script-size", "on/off", 13088 "Don't compile very large scripts (default: on, off to disable)") || 13089 !op.addIntOption('\0', "ion-warmup-threshold", "COUNT", 13090 "Wait for COUNT calls or iterations before compiling " 13091 "at the normal optimization level (default: 1500)", 13092 -1) || 13093 !op.addStringOption( 13094 '\0', "ion-regalloc", "[mode]", 13095 "Specify Ion register allocation:\n" 13096 " backtracking: Priority based backtracking register allocation " 13097 "(default)\n" 13098 " simple: Simple register allocator optimized for compile time") || 13099 !op.addBoolOption( 13100 '\0', "ion-eager", 13101 "Always ion-compile methods (implies --baseline-eager)") || 13102 !op.addBoolOption('\0', "fast-warmup", 13103 "Reduce warmup thresholds for each tier.") || 13104 !op.addStringOption( 13105 '\0', "baseline-offthread-compile", "on/off", 13106 "Compile baseline scripts offthread (default: off)") || 13107 !op.addStringOption( 13108 '\0', "baseline-batching", "on/off", 13109 "Batch baseline scripts before dispatching (default: off)") || 13110 !op.addStringOption('\0', "ion-offthread-compile", "on/off", 13111 "Compile Ion scripts offthread (default: on)") || 13112 !op.addStringOption('\0', "ion-parallel-compile", "on/off", 13113 "--ion-parallel compile is deprecated. Use " 13114 "--ion-offthread-compile.") || 13115 !op.addBoolOption('\0', "disable-main-thread-denormals", 13116 "Disable Denormals on the main thread only, to " 13117 "emulate WebAudio worklets.") || 13118 !op.addStringOption('\0', "object-keys-scalar-replacement", "on/off", 13119 "Replace Object.keys with a NativeIterators " 13120 "(default: on)") || 13121 !op.addBoolOption('\0', "baseline", 13122 "Enable baseline compiler (default)") || 13123 !op.addBoolOption('\0', "no-baseline", "Disable baseline compiler") || 13124 !op.addBoolOption('\0', "baseline-eager", 13125 "Always baseline-compile methods") || 13126 #ifdef ENABLE_PORTABLE_BASELINE_INTERP 13127 !op.addBoolOption('\0', "portable-baseline-eager", 13128 "Always use the portable baseline interpreter") || 13129 !op.addBoolOption('\0', "portable-baseline", 13130 "Enable Portable Baseline Interpreter (default)") || 13131 !op.addBoolOption('\0', "no-portable-baseline", 13132 "Disable Portable Baseline Interpreter") || 13133 #endif 13134 #ifdef ENABLE_JS_AOT_ICS 13135 !op.addBoolOption('\0', "aot-ics", "Enable ahead-of-time-known ICs") || 13136 !op.addBoolOption( 13137 '\0', "enforce-aot-ics", 13138 "Enable enforcing only use of ahead-of-time-known ICs") || 13139 #endif 13140 !op.addIntOption( 13141 '\0', "baseline-warmup-threshold", "COUNT", 13142 "Wait for COUNT calls or iterations before baseline-compiling " 13143 "(default: 10)", 13144 -1) || 13145 !op.addIntOption( 13146 '\0', "baseline-queue-capacity", "CAPACITY", 13147 "Wait for at most CAPACITY scripts to be added to baseline compile" 13148 "queue before dispatching (default: 8)", 13149 -1) || 13150 !op.addBoolOption('\0', "blinterp", 13151 "Enable Baseline Interpreter (default)") || 13152 !op.addBoolOption('\0', "no-blinterp", "Disable Baseline Interpreter") || 13153 !op.addBoolOption('\0', "disable-jithints", 13154 "Disable caching eager baseline compilation hints.") || 13155 !op.addBoolOption( 13156 '\0', "emit-interpreter-entry", 13157 "Emit Interpreter entry trampolines (default under --enable-perf)") || 13158 !op.addBoolOption( 13159 '\0', "no-emit-interpreter-entry", 13160 "Do not emit Interpreter entry trampolines (default).") || 13161 !op.addBoolOption('\0', "blinterp-eager", 13162 "Always Baseline-interpret scripts") || 13163 !op.addIntOption( 13164 '\0', "blinterp-warmup-threshold", "COUNT", 13165 "Wait for COUNT calls or iterations before Baseline-interpreting " 13166 "(default: 10)", 13167 -1) || 13168 !op.addIntOption( 13169 '\0', "trial-inlining-warmup-threshold", "COUNT", 13170 "Wait for COUNT calls or iterations before trial-inlining " 13171 "(default: 500)", 13172 -1) || 13173 !op.addStringOption( 13174 '\0', "monomorphic-inlining", "default/always/never", 13175 "Whether monomorphic inlining is used instead of trial inlining " 13176 "always, never, or based on heuristics (default)") || 13177 !op.addBoolOption( 13178 '\0', "no-sse3", 13179 "Pretend CPU does not support SSE3 instructions and above " 13180 "to test JIT codegen (no-op on platforms other than x86 and x64).") || 13181 !op.addBoolOption( 13182 '\0', "no-ssse3", 13183 "Pretend CPU does not support SSSE3 [sic] instructions and above " 13184 "to test JIT codegen (no-op on platforms other than x86 and x64).") || 13185 !op.addBoolOption( 13186 '\0', "no-sse41", 13187 "Pretend CPU does not support SSE4.1 instructions " 13188 "to test JIT codegen (no-op on platforms other than x86 and x64).") || 13189 !op.addBoolOption('\0', "no-sse4", "Alias for --no-sse41") || 13190 !op.addBoolOption( 13191 '\0', "no-sse42", 13192 "Pretend CPU does not support SSE4.2 instructions " 13193 "to test JIT codegen (no-op on platforms other than x86 and x64).") || 13194 #ifdef ENABLE_WASM_AVX 13195 !op.addBoolOption('\0', "enable-avx", 13196 "No-op. AVX is enabled by default, if available.") || 13197 !op.addBoolOption( 13198 '\0', "no-avx", 13199 "Pretend CPU does not support AVX or AVX2 instructions " 13200 "to test JIT codegen (no-op on platforms other than x86 and x64).") || 13201 #else 13202 !op.addBoolOption('\0', "enable-avx", 13203 "AVX is disabled by default. Enable AVX. " 13204 "(no-op on platforms other than x86 and x64).") || 13205 !op.addBoolOption('\0', "no-avx", 13206 "No-op. AVX is currently disabled by default.") || 13207 #endif 13208 !op.addBoolOption('\0', "no-fjcvtzs", 13209 "Pretend CPU does not support FJCVTZS instruction.") || 13210 !op.addBoolOption('\0', "no-cssc", 13211 "Pretend CPU does not support CSSC extension.") || 13212 !op.addBoolOption('\0', "more-compartments", 13213 "Make newGlobal default to creating a new " 13214 "compartment.") || 13215 !op.addBoolOption('\0', "fuzzing-safe", 13216 "Don't expose functions that aren't safe for " 13217 "fuzzers to call") || 13218 #ifdef DEBUG 13219 !op.addBoolOption('\0', "differential-testing", 13220 "Avoid random/undefined behavior that disturbs " 13221 "differential testing (correctness fuzzing)") || 13222 #endif 13223 !op.addBoolOption('\0', "disable-oom-functions", 13224 "Disable functions that cause " 13225 "artificial OOMs") || 13226 !op.addBoolOption('\0', "no-threads", "Disable helper threads") || 13227 !op.addBoolOption( 13228 '\0', "no-jit-backend", 13229 "Disable the JIT backend completely for this process") || 13230 #ifdef DEBUG 13231 !op.addBoolOption('\0', "dump-entrained-variables", 13232 "Print variables which are " 13233 "unnecessarily entrained by inner functions") || 13234 #endif 13235 !op.addBoolOption('\0', "no-ggc", "Disable Generational GC") || 13236 !op.addBoolOption('\0', "no-cgc", "Disable Compacting GC") || 13237 !op.addBoolOption('\0', "no-incremental-gc", "Disable Incremental GC") || 13238 !op.addBoolOption('\0', "no-parallel-marking", 13239 "Disable GC parallel marking") || 13240 !op.addBoolOption('\0', "enable-parallel-marking", 13241 "Enable GC parallel marking") || 13242 !op.addStringOption('\0', "nursery-strings", "on/off", 13243 "Allocate strings in the nursery") || 13244 !op.addStringOption('\0', "nursery-bigints", "on/off", 13245 "Allocate BigInts in the nursery") || 13246 !op.addIntOption('\0', "available-memory", "SIZE", 13247 "Select GC settings based on available memory (MB)", 13248 0) || 13249 !op.addBoolOption('\0', "disable-decommit", 13250 "Disable decommitting unsued GC memory") || 13251 !op.addStringOption('\0', "arm-hwcap", "[features]", 13252 "Specify ARM code generation features, or 'help' to " 13253 "list all features.") || 13254 !op.addIntOption('\0', "arm-asm-nop-fill", "SIZE", 13255 "Insert the given number of NOP instructions at all " 13256 "possible pool locations.", 13257 0) || 13258 !op.addIntOption('\0', "asm-pool-max-offset", "OFFSET", 13259 "The maximum pc relative OFFSET permitted in pool " 13260 "reference instructions.", 13261 1024) || 13262 !op.addBoolOption('\0', "arm-sim-icache-checks", 13263 "Enable icache flush checks in the ARM " 13264 "simulator.") || 13265 !op.addIntOption('\0', "arm-sim-stop-at", "NUMBER", 13266 "Stop the ARM simulator after the given " 13267 "NUMBER of instructions.", 13268 -1) || 13269 !op.addBoolOption('\0', "mips-sim-icache-checks", 13270 "Enable icache flush checks in the MIPS " 13271 "simulator.") || 13272 !op.addIntOption('\0', "mips-sim-stop-at", "NUMBER", 13273 "Stop the MIPS simulator after the given " 13274 "NUMBER of instructions.", 13275 -1) || 13276 !op.addBoolOption('\0', "loong64-sim-icache-checks", 13277 "Enable icache flush checks in the LoongArch64 " 13278 "simulator.") || 13279 !op.addIntOption('\0', "loong64-sim-stop-at", "NUMBER", 13280 "Stop the LoongArch64 simulator after the given " 13281 "NUMBER of instructions.", 13282 -1) || 13283 #ifdef JS_CODEGEN_RISCV64 13284 !op.addBoolOption('\0', "riscv-debug", "debug print riscv info.") || 13285 #endif 13286 #ifdef JS_SIMULATOR_RISCV64 13287 !op.addBoolOption('\0', "trace-sim", "print simulator info.") || 13288 !op.addBoolOption('\0', "debug-sim", "debug simulator.") || 13289 !op.addBoolOption('\0', "riscv-trap-to-simulator-debugger", 13290 "trap into simulator debuggger.") || 13291 !op.addIntOption('\0', "riscv-sim-stop-at", "NUMBER", 13292 "Stop the riscv simulator after the given " 13293 "NUMBER of instructions.", 13294 -1) || 13295 #endif 13296 !op.addIntOption('\0', "nursery-size", "SIZE-MB", 13297 "Set the maximum nursery size in MB", 13298 JS::DefaultNurseryMaxBytes / 1024 / 1024) || 13299 #ifdef JS_GC_ZEAL 13300 !op.addStringOption('z', "gc-zeal", "LEVEL(;LEVEL)*[,N]", 13301 gc::ZealModeHelpText) || 13302 #else 13303 !op.addStringOption('z', "gc-zeal", "LEVEL(;LEVEL)*[,N]", 13304 "option ignored in non-gc-zeal builds") || 13305 #endif 13306 !op.addMultiStringOption('\0', "gc-param", "NAME=VALUE", 13307 "Set a named GC parameter") || 13308 !op.addStringOption('\0', "module-load-path", "DIR", 13309 "Set directory to load modules from") || 13310 !op.addBoolOption('\0', "no-source-pragmas", 13311 "Disable source(Mapping)URL pragma parsing") || 13312 !op.addBoolOption('\0', "no-async-stacks", "Disable async stacks") || 13313 !op.addBoolOption('\0', "async-stacks-capture-debuggee-only", 13314 "Limit async stack capture to only debuggees") || 13315 !op.addMultiStringOption('\0', "dll", "LIBRARY", 13316 "Dynamically load LIBRARY") || 13317 !op.addBoolOption('\0', "suppress-minidump", 13318 "Suppress crash minidumps") || 13319 !op.addStringOption( 13320 '\0', "delazification-mode", "[option]", 13321 "Select one of the delazification mode for scripts given on the " 13322 "command line, valid options are: " 13323 "'on-demand', 'concurrent-df', 'eager', 'concurrent-df+on-demand'. " 13324 "Choosing 'concurrent-df+on-demand' will run both concurrent-df and " 13325 "on-demand delazification mode, and compare compilation outcome. ") || 13326 !op.addBoolOption('\0', "wasm-compile-and-serialize", 13327 "Compile the wasm bytecode from stdin and serialize " 13328 "the results to stdout") || 13329 #ifdef FUZZING_JS_FUZZILLI 13330 !op.addBoolOption('\0', "reprl", "Enable REPRL mode for fuzzing") || 13331 #endif 13332 !op.addMultiStringOption('P', "setpref", "name[=val]", 13333 "Set the value of a JS pref. The value may " 13334 "be omitted for boolean prefs, in which case " 13335 "they default to true. Use --list-prefs " 13336 "to print all pref names.") || 13337 !op.addBoolOption( 13338 '\0', "list-prefs", 13339 "Print list of prefs that can be set with --setpref.") || 13340 !op.addBoolOption('\0', "use-fdlibm-for-sin-cos-tan", 13341 "Use fdlibm for Math.sin, Math.cos, and Math.tan") || 13342 !op.addBoolOption('\0', "wasm-gc", "Enable WebAssembly gc proposal.") || 13343 !op.addBoolOption('\0', "wasm-relaxed-simd", 13344 "Enable WebAssembly relaxed-simd proposal.") || 13345 !op.addBoolOption('\0', "wasm-multi-memory", 13346 "Enable WebAssembly multi-memory proposal.") || 13347 !op.addBoolOption('\0', "wasm-memory-control", 13348 "Enable WebAssembly memory-control proposal.") || 13349 !op.addBoolOption('\0', "wasm-memory64", 13350 "Enable WebAssembly memory64 proposal.") || 13351 !op.addBoolOption('\0', "wasm-tail-calls", 13352 "Enable WebAssembly tail-calls proposal.") || 13353 !op.addBoolOption('\0', "wasm-js-string-builtins", 13354 "Enable WebAssembly js-string-builtins proposal.") || 13355 !op.addBoolOption('\0', "enable-iterator-sequencing", 13356 "Enable Iterator Sequencing") || 13357 !op.addBoolOption('\0', "enable-math-sumprecise", 13358 "Enable Math.sumPrecise") || 13359 !op.addBoolOption('\0', "enable-error-iserror", "Enable Error.isError") || 13360 !op.addBoolOption('\0', "enable-iterator-range", 13361 "Enable Iterator.range") || 13362 !op.addBoolOption('\0', "enable-joint-iteration", 13363 "Enable Joint Iteration") || 13364 !op.addBoolOption('\0', "enable-atomics-pause", "Enable Atomics pause") || 13365 !op.addBoolOption('\0', "enable-explicit-resource-management", 13366 "Enable Explicit Resource Management") || 13367 !op.addBoolOption('\0', "disable-explicit-resource-management", 13368 "Disable Explicit Resource Management") || 13369 !op.addBoolOption('\0', "enable-temporal", "Enable Temporal") || 13370 !op.addBoolOption('\0', "enable-upsert", "Enable Upsert proposal") || 13371 !op.addBoolOption('\0', "enable-import-bytes", "Enable import bytes") || 13372 !op.addBoolOption('\0', "enable-promise-allkeyed", 13373 "Enable Promise.allKeyed") || 13374 !op.addBoolOption('\0', "enable-arraybuffer-immutable", 13375 "Enable immutable ArrayBuffers") || 13376 !op.addBoolOption('\0', "enable-iterator-chunking", 13377 "Enable Iterator Chunking") || 13378 !op.addBoolOption('\0', "enable-iterator-join", "Enable Iterator.join") || 13379 !op.addBoolOption('\0', "enable-legacy-regexp", 13380 "Enable Legacy RegExp features")) { 13381 return false; 13382 } 13383 13384 op.setArgTerminatesOptions("script", true); 13385 op.setArgCapturesRest("scriptArgs"); 13386 13387 // If --fuzzing-safe is used, print a warning for unknown shell flags instead 13388 // of aborting execution. 13389 op.setIgnoresUnknownOptions("fuzzing-safe", true); 13390 13391 return true; 13392 } 13393 13394 bool SetGlobalOptionsPreJSInit(const OptionParser& op) { 13395 if (op.getBoolOption("fuzzing-safe")) { 13396 fuzzingSafe = true; 13397 } else { 13398 fuzzingSafe = 13399 (getenv("MOZ_FUZZING_SAFE") && getenv("MOZ_FUZZING_SAFE")[0] != '0'); 13400 } 13401 13402 for (MultiStringRange args = op.getMultiStringOption("setpref"); 13403 !args.empty(); args.popFront()) { 13404 if (!SetPref(args.front())) { 13405 return false; 13406 } 13407 } 13408 13409 // Override pref values for prefs that have a custom shell flag. 13410 // If you're adding a new feature, consider using --setpref instead. 13411 if (op.getBoolOption("enable-shadow-realms")) { 13412 JS::Prefs::set_experimental_shadow_realms(true); 13413 } 13414 if (op.getBoolOption("enable-uint8array-base64")) { 13415 JS::Prefs::setAtStartup_experimental_uint8array_base64(true); 13416 } 13417 if (op.getBoolOption("enable-math-sumprecise")) { 13418 JS::Prefs::setAtStartup_experimental_math_sumprecise(true); 13419 } 13420 if (op.getBoolOption("enable-atomics-pause")) { 13421 JS::Prefs::setAtStartup_experimental_atomics_pause(true); 13422 } 13423 if (op.getBoolOption("enable-error-iserror")) { 13424 JS::Prefs::set_experimental_error_iserror(true); 13425 } 13426 13427 bool symbolsAsWeakMapKeys = true; 13428 if (op.getBoolOption("enable-symbols-as-weakmap-keys")) { 13429 symbolsAsWeakMapKeys = true; 13430 } 13431 if (op.getBoolOption("no-symbols-as-weakmap-keys")) { 13432 symbolsAsWeakMapKeys = false; 13433 } 13434 JS::Prefs::setAtStartup_experimental_symbols_as_weakmap_keys( 13435 symbolsAsWeakMapKeys); 13436 if (op.getBoolOption("enable-joint-iteration")) { 13437 JS::Prefs::setAtStartup_experimental_joint_iteration(true); 13438 } 13439 13440 if (op.getBoolOption("enable-legacy-regexp")) { 13441 JS::Prefs::set_experimental_legacy_regexp(true); 13442 } 13443 #ifdef NIGHTLY_BUILD 13444 if (op.getBoolOption("enable-async-iterator-helpers")) { 13445 JS::Prefs::setAtStartup_experimental_async_iterator_helpers(true); 13446 } 13447 if (op.getBoolOption("enable-iterator-sequencing")) { 13448 JS::Prefs::setAtStartup_experimental_iterator_sequencing(true); 13449 } 13450 if (op.getBoolOption("enable-iterator-range")) { 13451 JS::Prefs::setAtStartup_experimental_iterator_range(true); 13452 } 13453 if (op.getBoolOption("enable-upsert")) { 13454 JS::Prefs::setAtStartup_experimental_upsert(true); 13455 } 13456 if (op.getBoolOption("enable-arraybuffer-immutable")) { 13457 JS::Prefs::setAtStartup_experimental_arraybuffer_immutable(true); 13458 } 13459 if (op.getBoolOption("enable-import-bytes")) { 13460 JS::Prefs::setAtStartup_experimental_import_bytes(true); 13461 } 13462 if (op.getBoolOption("enable-promise-allkeyed")) { 13463 JS::Prefs::setAtStartup_experimental_promise_allkeyed(true); 13464 } 13465 if (op.getBoolOption("enable-iterator-chunking")) { 13466 JS::Prefs::setAtStartup_experimental_iterator_chunking(true); 13467 } 13468 if (op.getBoolOption("enable-iterator-join")) { 13469 JS::Prefs::setAtStartup_experimental_iterator_join(true); 13470 } 13471 #endif 13472 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT 13473 if (op.getBoolOption("enable-explicit-resource-management")) { 13474 JS::Prefs::set_experimental_explicit_resource_management(true); 13475 } 13476 if (op.getBoolOption("disable-explicit-resource-management")) { 13477 JS::Prefs::set_experimental_explicit_resource_management(false); 13478 } 13479 #endif 13480 #ifdef JS_HAS_INTL_API 13481 if (op.getBoolOption("enable-temporal")) { 13482 JS::Prefs::setAtStartup_experimental_temporal(true); 13483 } 13484 #endif 13485 JS::Prefs::setAtStartup_experimental_weakrefs_expose_cleanupSome(true); 13486 13487 if (op.getBoolOption("disable-property-error-message-fix")) { 13488 JS::Prefs::setAtStartup_property_error_message_fix(false); 13489 } 13490 13491 JS::Prefs::set_use_fdlibm_for_sin_cos_tan( 13492 op.getBoolOption("use-fdlibm-for-sin-cos-tan")); 13493 13494 if (const char* str = op.getStringOption("ion-regalloc")) { 13495 auto allocator = jit::LookupRegisterAllocator(str); 13496 if (allocator.isNothing()) { 13497 return OptionFailure("ion-regalloc", str); 13498 } 13499 switch (*allocator) { 13500 case jit::RegisterAllocator_Backtracking: 13501 JS::Prefs::setAtStartup_ion_regalloc(1); 13502 break; 13503 case jit::RegisterAllocator_Simple: 13504 JS::Prefs::setAtStartup_ion_regalloc(2); 13505 break; 13506 } 13507 } 13508 13509 if (op.getBoolOption("wasm-gc") || op.getBoolOption("wasm-relaxed-simd") || 13510 op.getBoolOption("wasm-multi-memory") || 13511 op.getBoolOption("wasm-memory-control") || 13512 op.getBoolOption("wasm-memory64") || 13513 op.getBoolOption("wasm-tail-calls") || 13514 op.getBoolOption("wasm-js-string-builtins")) { 13515 fprintf( 13516 stderr, 13517 "Wasm shell flags are now using prefs, use -P wasm_feature instead.\n"); 13518 return false; 13519 } 13520 13521 if (op.getBoolOption("list-prefs")) { 13522 ListPrefs(); 13523 return false; 13524 } 13525 13526 // Note: DisableJitBackend must be called before JS_InitWithFailureDiagnostic. 13527 if (op.getBoolOption("no-jit-backend")) { 13528 JS::DisableJitBackend(); 13529 } 13530 13531 #if defined(JS_CODEGEN_ARM) 13532 if (const char* str = op.getStringOption("arm-hwcap")) { 13533 jit::SetARMHwCapFlagsString(str); 13534 } 13535 13536 int32_t fill = op.getIntOption("arm-asm-nop-fill"); 13537 if (fill >= 0) { 13538 jit::Assembler::NopFill = fill; 13539 } 13540 13541 int32_t poolMaxOffset = op.getIntOption("asm-pool-max-offset"); 13542 if (poolMaxOffset >= 5 && poolMaxOffset <= 1024) { 13543 jit::Assembler::AsmPoolMaxOffset = poolMaxOffset; 13544 } 13545 #endif 13546 13547 // Fish around in `op` for various important compiler-configuration flags 13548 // and make sure they get handed on to any child processes we might create. 13549 // See bug 1700900. Semantically speaking, this is all rather dubious: 13550 // 13551 // * What set of flags need to be propagated in order to guarantee that the 13552 // child produces code that is "compatible" (in whatever sense) with that 13553 // produced by the parent? This isn't always easy to determine. 13554 // 13555 // * There's nothing that ensures that flags given to the child are 13556 // presented in the same order that they exist in the parent's `argv[]`. 13557 // That could be a problem in the case where two flags with contradictory 13558 // meanings are given, and they are presented to the child in the opposite 13559 // order. For example: --wasm-compiler=optimizing --wasm-compiler=baseline. 13560 13561 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 13562 MOZ_ASSERT(!js::jit::CPUFlagsHaveBeenComputed()); 13563 13564 if (op.getBoolOption("no-avx")) { 13565 js::jit::CPUInfo::SetAVXDisabled(); 13566 if (!sCompilerProcessFlags.append("--no-avx")) { 13567 return false; 13568 } 13569 } 13570 if (op.getBoolOption("enable-avx")) { 13571 js::jit::CPUInfo::SetAVXEnabled(); 13572 if (!sCompilerProcessFlags.append("--enable-avx")) { 13573 return false; 13574 } 13575 } 13576 if (op.getBoolOption("no-sse3")) { 13577 js::jit::CPUInfo::SetSSE3Disabled(); 13578 if (!sCompilerProcessFlags.append("--no-sse3")) { 13579 return false; 13580 } 13581 } 13582 if (op.getBoolOption("no-ssse3")) { 13583 js::jit::CPUInfo::SetSSSE3Disabled(); 13584 if (!sCompilerProcessFlags.append("--no-ssse3")) { 13585 return false; 13586 } 13587 } 13588 if (op.getBoolOption("no-sse4") || op.getBoolOption("no-sse41")) { 13589 js::jit::CPUInfo::SetSSE41Disabled(); 13590 if (!sCompilerProcessFlags.append("--no-sse41")) { 13591 return false; 13592 } 13593 } 13594 if (op.getBoolOption("no-sse42")) { 13595 js::jit::CPUInfo::SetSSE42Disabled(); 13596 if (!sCompilerProcessFlags.append("--no-sse42")) { 13597 return false; 13598 } 13599 } 13600 #endif 13601 #if defined(JS_CODEGEN_ARM64) 13602 if (op.getBoolOption("no-fjcvtzs")) { 13603 vixl::CPUFeatures fjcvtzs(vixl::CPUFeatures::kJSCVT); 13604 jit::ARM64Flags::DisableCPUFeatures(fjcvtzs); 13605 } 13606 if (op.getBoolOption("no-cssc")) { 13607 vixl::CPUFeatures cssc(vixl::CPUFeatures::kCSSC); 13608 jit::ARM64Flags::DisableCPUFeatures(cssc); 13609 } 13610 #endif 13611 #ifndef __wasi__ 13612 if (op.getBoolOption("disable-wasm-huge-memory")) { 13613 JS::Prefs::setAtStartup_wasm_disable_huge_memory(true); 13614 if (!sCompilerProcessFlags.append("--disable-wasm-huge-memory")) { 13615 return false; 13616 } 13617 } 13618 #endif 13619 13620 if (op.getBoolOption("disable-decommit")) { 13621 gc::DisableDecommit(); 13622 } 13623 13624 return true; 13625 } 13626 13627 bool SetGlobalOptionsPostJSInit(const OptionParser& op) { 13628 // Allow dumping on Linux with the fuzzing flag set, even when running with 13629 // the suid/sgid flag set on the shell. 13630 #ifdef XP_LINUX 13631 if (op.getBoolOption("fuzzing-safe")) { 13632 prctl(PR_SET_DUMPABLE, 1); 13633 } 13634 #endif 13635 13636 #ifdef DEBUG 13637 /* 13638 * Process OOM options as early as possible so that we can observe as many 13639 * allocations as possible. 13640 */ 13641 OOM_printAllocationCount = op.getBoolOption('O'); 13642 #endif 13643 13644 if (op.getBoolOption("no-threads")) { 13645 js::DisableExtraThreads(); 13646 } 13647 13648 enableCodeCoverage = op.getBoolOption("code-coverage"); 13649 if (enableCodeCoverage) { 13650 js::EnableCodeCoverage(); 13651 } 13652 13653 // If LCov is enabled, then the default delazification mode should be changed 13654 // to parse everything eagerly, such that we know the location of every 13655 // instruction, to report them in the LCov summary, even if there is no uses 13656 // of these instructions. 13657 // 13658 // Note: code coverage can be enabled either using the --code-coverage command 13659 // line, or the JS_CODE_COVERAGE_OUTPUT_DIR environment variable, which is 13660 // processed by JS_InitWithFailureDiagnostic. 13661 if (coverage::IsLCovEnabled()) { 13662 defaultDelazificationMode = 13663 JS::DelazificationOption::ParseEverythingEagerly; 13664 } 13665 13666 if (const char* xdr = op.getStringOption("selfhosted-xdr-path")) { 13667 shell::selfHostedXDRPath = xdr; 13668 } 13669 if (const char* opt = op.getStringOption("selfhosted-xdr-mode")) { 13670 if (strcmp(opt, "encode") == 0) { 13671 shell::encodeSelfHostedCode = true; 13672 } else if (strcmp(opt, "decode") == 0) { 13673 shell::encodeSelfHostedCode = false; 13674 } else if (strcmp(opt, "off") == 0) { 13675 shell::selfHostedXDRPath = nullptr; 13676 } else { 13677 MOZ_CRASH( 13678 "invalid option value for --selfhosted-xdr-mode, must be " 13679 "encode/decode"); 13680 } 13681 } 13682 13683 #ifdef JS_WITHOUT_NSPR 13684 if (!op.getMultiStringOption("dll").empty()) { 13685 fprintf(stderr, "Error: --dll requires NSPR support!\n"); 13686 return false; 13687 } 13688 #else 13689 AutoLibraryLoader loader; 13690 MultiStringRange dllPaths = op.getMultiStringOption("dll"); 13691 while (!dllPaths.empty()) { 13692 char* path = dllPaths.front(); 13693 loader.load(path); 13694 dllPaths.popFront(); 13695 } 13696 #endif 13697 13698 if (op.getBoolOption("suppress-minidump")) { 13699 js::NoteIntentionalCrash(); 13700 } 13701 13702 // The fake CPU count must be set before initializing the Runtime, 13703 // which spins up the thread pool. 13704 int32_t cpuCount = op.getIntOption("cpu-count"); // What we're really setting 13705 if (cpuCount < 0) { 13706 cpuCount = op.getIntOption("thread-count"); // Legacy name 13707 } 13708 if (cpuCount >= 0 && !SetFakeCPUCount(cpuCount)) { 13709 return false; 13710 } 13711 13712 return true; 13713 } 13714 13715 bool SetContextOptions(JSContext* cx, const OptionParser& op) { 13716 if (!SetContextWasmOptions(cx, op) || !SetContextJITOptions(cx, op) || 13717 !SetContextGCOptions(cx, op)) { 13718 return false; 13719 } 13720 13721 enableSourcePragmas = !op.getBoolOption("no-source-pragmas"); 13722 enableAsyncStacks = !op.getBoolOption("no-async-stacks"); 13723 enableAsyncStackCaptureDebuggeeOnly = 13724 op.getBoolOption("async-stacks-capture-debuggee-only"); 13725 enableToSource = !op.getBoolOption("disable-tosource"); 13726 JS::ContextOptionsRef(cx) 13727 .setSourcePragmas(enableSourcePragmas) 13728 .setAsyncStack(enableAsyncStacks) 13729 .setAsyncStackCaptureDebuggeeOnly(enableAsyncStackCaptureDebuggeeOnly); 13730 13731 if (const char* str = op.getStringOption("shared-memory")) { 13732 if (strcmp(str, "off") == 0) { 13733 enableSharedMemory = false; 13734 } else if (strcmp(str, "on") == 0) { 13735 enableSharedMemory = true; 13736 } else { 13737 return OptionFailure("shared-memory", str); 13738 } 13739 } 13740 13741 reportWarnings = op.getBoolOption('w'); 13742 compileOnly = op.getBoolOption('c'); 13743 printTiming = op.getBoolOption('b'); 13744 enableDisassemblyDumps = op.getBoolOption('D'); 13745 cx->runtime()->profilingScripts = 13746 enableCodeCoverage || enableDisassemblyDumps; 13747 13748 if (const char* mode = op.getStringOption("delazification-mode")) { 13749 if (strcmp(mode, "on-demand") == 0) { 13750 defaultDelazificationMode = JS::DelazificationOption::OnDemandOnly; 13751 } else if (strcmp(mode, "concurrent-df") == 0) { 13752 defaultDelazificationMode = 13753 JS::DelazificationOption::ConcurrentDepthFirst; 13754 } else if (strcmp(mode, "eager") == 0) { 13755 defaultDelazificationMode = 13756 JS::DelazificationOption::ParseEverythingEagerly; 13757 } else if (strcmp(mode, "concurrent-df+on-demand") == 0 || 13758 strcmp(mode, "on-demand+concurrent-df") == 0) { 13759 defaultDelazificationMode = 13760 JS::DelazificationOption::CheckConcurrentWithOnDemand; 13761 } else { 13762 return OptionFailure("delazification-mode", mode); 13763 } 13764 } 13765 13766 return true; 13767 } 13768 13769 bool SetContextWasmOptions(JSContext* cx, const OptionParser& op) { 13770 enableAsmJS = op.getBoolOption("asmjs"); 13771 13772 enableWasm = true; 13773 enableWasmBaseline = true; 13774 enableWasmOptimizing = true; 13775 13776 if (const char* str = op.getStringOption("wasm-compiler")) { 13777 if (strcmp(str, "none") == 0) { 13778 enableWasm = false; 13779 // Disable asm.js -- no wasm compilers available. 13780 enableAsmJS = false; 13781 } else if (strcmp(str, "baseline") == 0) { 13782 MOZ_ASSERT(enableWasmBaseline); 13783 enableWasmOptimizing = false; 13784 } else if (strcmp(str, "optimizing") == 0 || 13785 strcmp(str, "optimized") == 0) { 13786 enableWasmBaseline = false; 13787 MOZ_ASSERT(enableWasmOptimizing); 13788 } else if (strcmp(str, "baseline+optimizing") == 0 || 13789 strcmp(str, "baseline+optimized") == 0) { 13790 MOZ_ASSERT(enableWasmBaseline); 13791 MOZ_ASSERT(enableWasmOptimizing); 13792 } else if (strcmp(str, "ion") == 0) { 13793 enableWasmBaseline = false; 13794 enableWasmOptimizing = true; 13795 } else if (strcmp(str, "baseline+ion") == 0) { 13796 MOZ_ASSERT(enableWasmBaseline); 13797 enableWasmOptimizing = true; 13798 } else { 13799 return OptionFailure("wasm-compiler", str); 13800 } 13801 } 13802 13803 enableTestWasmAwaitTier2 = op.getBoolOption("test-wasm-await-tier2"); 13804 13805 JS::ContextOptionsRef(cx) 13806 .setAsmJS(enableAsmJS) 13807 .setWasm(enableWasm) 13808 .setWasmForTrustedPrinciples(enableWasm) 13809 .setWasmBaseline(enableWasmBaseline) 13810 .setWasmIon(enableWasmOptimizing) 13811 .setTestWasmAwaitTier2(enableTestWasmAwaitTier2); 13812 13813 #ifndef __wasi__ 13814 // Also the following are to be propagated. 13815 const char* to_propagate[] = { 13816 // Compiler selection options 13817 "--test-wasm-await-tier2", 13818 }; 13819 for (const char* p : to_propagate) { 13820 if (op.getBoolOption(p + 2 /* 2 => skip the leading '--' */)) { 13821 if (!sCompilerProcessFlags.append(p)) { 13822 return false; 13823 } 13824 } 13825 } 13826 13827 // Also --wasm-compiler= is to be propagated. This is tricky because it is 13828 // necessary to reconstitute the --wasm-compiler=<whatever> string from its 13829 // pieces, without causing a leak. Hence it is copied into a static buffer. 13830 // This is thread-unsafe, but we're in `main()` and on the process' root 13831 // thread. Also, we do this only once -- it wouldn't work properly if we 13832 // handled multiple --wasm-compiler= flags in a loop. 13833 const char* wasm_compiler = op.getStringOption("wasm-compiler"); 13834 if (wasm_compiler) { 13835 size_t n_needed = 13836 2 + strlen("wasm-compiler") + 1 + strlen(wasm_compiler) + 1; 13837 const size_t n_avail = 128; 13838 static char buf[n_avail]; 13839 // `n_needed` depends on the compiler name specified. However, it can't 13840 // be arbitrarily long, since previous flag-checking should have limited 13841 // it to a set of known possibilities: "baseline", "ion", 13842 // "baseline+ion", Still, assert this for safety. 13843 MOZ_RELEASE_ASSERT(n_needed < n_avail); 13844 memset(buf, 0, sizeof(buf)); 13845 SprintfBuf(buf, n_avail, "--%s=%s", "wasm-compiler", wasm_compiler); 13846 if (!sCompilerProcessFlags.append(buf)) { 13847 return false; 13848 } 13849 } 13850 #endif // __wasi__ 13851 13852 return true; 13853 } 13854 13855 bool SetContextJITOptions(JSContext* cx, const OptionParser& op) { 13856 // Check --fast-warmup first because it sets default warm-up thresholds. These 13857 // thresholds can then be overridden below by --ion-eager and other flags. 13858 if (op.getBoolOption("fast-warmup")) { 13859 jit::JitOptions.setFastWarmUp(); 13860 } 13861 13862 if (op.getBoolOption("no-ion-for-main-context")) { 13863 JS::ContextOptionsRef(cx).setDisableIon(); 13864 } 13865 13866 if (const char* str = op.getStringOption("cache-ir-stubs")) { 13867 if (strcmp(str, "on") == 0) { 13868 jit::JitOptions.disableCacheIR = false; 13869 } else if (strcmp(str, "off") == 0) { 13870 jit::JitOptions.disableCacheIR = true; 13871 } else { 13872 return OptionFailure("cache-ir-stubs", str); 13873 } 13874 } 13875 13876 if (const char* str = op.getStringOption("stub-folding")) { 13877 if (strcmp(str, "on") == 0) { 13878 jit::JitOptions.disableStubFolding = false; 13879 } else if (strcmp(str, "off") == 0) { 13880 jit::JitOptions.disableStubFolding = true; 13881 } else { 13882 return OptionFailure("stub-folding", str); 13883 } 13884 } 13885 13886 if (const char* str = op.getStringOption("stub-folding-loads-and-stores")) { 13887 if (strcmp(str, "on") == 0) { 13888 jit::JitOptions.disableStubFoldingLoadsAndStores = false; 13889 } else if (strcmp(str, "off") == 0) { 13890 jit::JitOptions.disableStubFoldingLoadsAndStores = true; 13891 } else { 13892 return OptionFailure("stub-folding-loads-and-stores", str); 13893 } 13894 } 13895 13896 if (const char* str = op.getStringOption("spectre-mitigations")) { 13897 if (strcmp(str, "on") == 0) { 13898 #if defined(JS_CODEGEN_RISCV64) || defined(JS_CODEGEN_LOONG64) || \ 13899 defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_WASM32) 13900 // MacroAssembler::spectreZeroRegister and MacroAssembler::spectreMovePtr 13901 // are not implemented for these targets. 13902 fprintf( 13903 stderr, 13904 "Warning: Spectre mitigations are not implemented for this target." 13905 " --spectre-mitigations=on is ignored.\n"); 13906 #else 13907 jit::JitOptions.spectreIndexMasking = true; 13908 jit::JitOptions.spectreObjectMitigations = true; 13909 jit::JitOptions.spectreStringMitigations = true; 13910 jit::JitOptions.spectreValueMasking = true; 13911 jit::JitOptions.spectreJitToCxxCalls = true; 13912 #endif 13913 } else if (strcmp(str, "off") == 0) { 13914 jit::JitOptions.spectreIndexMasking = false; 13915 jit::JitOptions.spectreObjectMitigations = false; 13916 jit::JitOptions.spectreStringMitigations = false; 13917 jit::JitOptions.spectreValueMasking = false; 13918 jit::JitOptions.spectreJitToCxxCalls = false; 13919 } else { 13920 return OptionFailure("spectre-mitigations", str); 13921 } 13922 } 13923 13924 if (const char* str = op.getStringOption("write-protect-code")) { 13925 if (strcmp(str, "on") == 0) { 13926 jit::JitOptions.maybeSetWriteProtectCode(true); 13927 } else if (strcmp(str, "off") == 0) { 13928 jit::JitOptions.maybeSetWriteProtectCode(false); 13929 } else { 13930 return OptionFailure("write-protect-code", str); 13931 } 13932 } 13933 13934 if (const char* str = op.getStringOption("monomorphic-inlining")) { 13935 if (strcmp(str, "default") == 0) { 13936 jit::JitOptions.monomorphicInlining = 13937 jit::UseMonomorphicInlining::Default; 13938 } else if (strcmp(str, "always") == 0) { 13939 jit::JitOptions.monomorphicInlining = jit::UseMonomorphicInlining::Always; 13940 } else if (strcmp(str, "never") == 0) { 13941 jit::JitOptions.monomorphicInlining = jit::UseMonomorphicInlining::Never; 13942 } else { 13943 return OptionFailure("monomorphic-inlining", str); 13944 } 13945 } 13946 13947 if (const char* str = op.getStringOption("ion-scalar-replacement")) { 13948 if (strcmp(str, "on") == 0) { 13949 jit::JitOptions.disableScalarReplacement = false; 13950 } else if (strcmp(str, "off") == 0) { 13951 jit::JitOptions.disableScalarReplacement = true; 13952 } else { 13953 return OptionFailure("ion-scalar-replacement", str); 13954 } 13955 } 13956 13957 if (op.getStringOption("ion-shared-stubs")) { 13958 // Dead option, preserved for now for potential fuzzer interaction. 13959 } 13960 13961 if (const char* str = op.getStringOption("ion-gvn")) { 13962 if (strcmp(str, "off") == 0) { 13963 jit::JitOptions.disableGvn = true; 13964 } else if (strcmp(str, "on") != 0 && strcmp(str, "optimistic") != 0 && 13965 strcmp(str, "pessimistic") != 0) { 13966 // We accept "pessimistic" and "optimistic" as synonyms for "on" 13967 // for backwards compatibility. 13968 return OptionFailure("ion-gvn", str); 13969 } 13970 } 13971 13972 if (const char* str = op.getStringOption("ion-licm")) { 13973 if (strcmp(str, "on") == 0) { 13974 jit::JitOptions.disableLicm = false; 13975 } else if (strcmp(str, "off") == 0) { 13976 jit::JitOptions.disableLicm = true; 13977 } else { 13978 return OptionFailure("ion-licm", str); 13979 } 13980 } 13981 13982 if (const char* str = op.getStringOption("ion-edgecase-analysis")) { 13983 if (strcmp(str, "on") == 0) { 13984 jit::JitOptions.disableEdgeCaseAnalysis = false; 13985 } else if (strcmp(str, "off") == 0) { 13986 jit::JitOptions.disableEdgeCaseAnalysis = true; 13987 } else { 13988 return OptionFailure("ion-edgecase-analysis", str); 13989 } 13990 } 13991 13992 if (const char* str = op.getStringOption("ion-pruning")) { 13993 if (strcmp(str, "on") == 0) { 13994 jit::JitOptions.disablePruning = false; 13995 } else if (strcmp(str, "off") == 0) { 13996 jit::JitOptions.disablePruning = true; 13997 } else { 13998 return OptionFailure("ion-pruning", str); 13999 } 14000 } 14001 14002 if (const char* str = op.getStringOption("ion-range-analysis")) { 14003 if (strcmp(str, "on") == 0) { 14004 jit::JitOptions.disableRangeAnalysis = false; 14005 } else if (strcmp(str, "off") == 0) { 14006 jit::JitOptions.disableRangeAnalysis = true; 14007 } else { 14008 return OptionFailure("ion-range-analysis", str); 14009 } 14010 } 14011 14012 if (const char* str = op.getStringOption("ion-sink")) { 14013 if (strcmp(str, "on") == 0) { 14014 jit::JitOptions.disableSink = false; 14015 } else if (strcmp(str, "off") == 0) { 14016 jit::JitOptions.disableSink = true; 14017 } else { 14018 return OptionFailure("ion-sink", str); 14019 } 14020 } 14021 14022 if (const char* str = op.getStringOption("ion-optimize-shapeguards")) { 14023 if (strcmp(str, "on") == 0) { 14024 jit::JitOptions.disableRedundantShapeGuards = false; 14025 } else if (strcmp(str, "off") == 0) { 14026 jit::JitOptions.disableRedundantShapeGuards = true; 14027 } else { 14028 return OptionFailure("ion-optimize-shapeguards", str); 14029 } 14030 } 14031 14032 if (const char* str = op.getStringOption("ion-optimize-gcbarriers")) { 14033 if (strcmp(str, "on") == 0) { 14034 jit::JitOptions.disableRedundantGCBarriers = false; 14035 } else if (strcmp(str, "off") == 0) { 14036 jit::JitOptions.disableRedundantGCBarriers = true; 14037 } else { 14038 return OptionFailure("ion-optimize-gcbarriers", str); 14039 } 14040 } 14041 14042 if (const char* str = op.getStringOption("ion-instruction-reordering")) { 14043 if (strcmp(str, "on") == 0) { 14044 jit::JitOptions.disableInstructionReordering = false; 14045 } else if (strcmp(str, "off") == 0) { 14046 jit::JitOptions.disableInstructionReordering = true; 14047 } else { 14048 return OptionFailure("ion-instruction-reordering", str); 14049 } 14050 } 14051 14052 if (op.getBoolOption("ion-check-range-analysis")) { 14053 jit::JitOptions.checkRangeAnalysis = true; 14054 } 14055 14056 if (op.getBoolOption("ion-extra-checks")) { 14057 jit::JitOptions.runExtraChecks = true; 14058 } 14059 14060 if (const char* str = op.getStringOption("ion-inlining")) { 14061 if (strcmp(str, "on") == 0) { 14062 jit::JitOptions.disableInlining = false; 14063 } else if (strcmp(str, "off") == 0) { 14064 jit::JitOptions.disableInlining = true; 14065 } else { 14066 return OptionFailure("ion-inlining", str); 14067 } 14068 } 14069 14070 if (const char* str = op.getStringOption("ion-osr")) { 14071 if (strcmp(str, "on") == 0) { 14072 jit::JitOptions.osr = true; 14073 } else if (strcmp(str, "off") == 0) { 14074 jit::JitOptions.osr = false; 14075 } else { 14076 return OptionFailure("ion-osr", str); 14077 } 14078 } 14079 14080 if (const char* str = op.getStringOption("ion-limit-script-size")) { 14081 if (strcmp(str, "on") == 0) { 14082 jit::JitOptions.limitScriptSize = true; 14083 } else if (strcmp(str, "off") == 0) { 14084 jit::JitOptions.limitScriptSize = false; 14085 } else { 14086 return OptionFailure("ion-limit-script-size", str); 14087 } 14088 } 14089 14090 if (op.getBoolOption("disable-main-thread-denormals")) { 14091 // This is a simplified version of WebAudio code, which is good enough for 14092 // fuzzing purposes. 14093 // 14094 // See dom/media/webaudio/blink/DenormalDisabler.h#124 14095 #if defined(__GNUC__) && defined(__SSE__) && defined(__x86_64__) 14096 int savedCSR; 14097 asm volatile("stmxcsr %0" : "=m"(savedCSR)); 14098 int newCSR = savedCSR | 0x8040; 14099 asm volatile("ldmxcsr %0" : : "m"(newCSR)); 14100 #elif defined(__arm__) 14101 int savedCSR; 14102 asm volatile("vmrs %[result], FPSCR" : [result] "=r"(savedCSR)); 14103 // Bit 24 is the flush-to-zero mode control bit. Setting it to 1 flushes 14104 // denormals to 0. 14105 int newCSR = savedCSR | (1 << 24); 14106 asm volatile("vmsr FPSCR, %[src]" : : [src] "r"(newCSR)); 14107 #elif defined(__aarch64__) 14108 int savedCSR; 14109 asm volatile("mrs %x[result], FPCR" : [result] "=r"(savedCSR)); 14110 // Bit 24 is the flush-to-zero mode control bit. Setting it to 1 flushes 14111 // denormals to 0. 14112 int newCSR = savedCSR | (1 << 24); 14113 asm volatile("msr FPCR, %x[src]" : : [src] "r"(newCSR)); 14114 #else 14115 // Do nothing on other architecture. 14116 #endif 14117 } 14118 14119 if (const char* str = op.getStringOption("object-keys-scalar-replacement")) { 14120 if (strcmp(str, "on") == 0) { 14121 jit::JitOptions.disableObjectKeysScalarReplacement = false; 14122 } else if (strcmp(str, "off") == 0) { 14123 jit::JitOptions.disableObjectKeysScalarReplacement = true; 14124 } else { 14125 return OptionFailure("object-keys-scalar-replacement", str); 14126 } 14127 } 14128 14129 int32_t warmUpThreshold = op.getIntOption("ion-warmup-threshold"); 14130 if (warmUpThreshold >= 0) { 14131 jit::JitOptions.setNormalIonWarmUpThreshold(warmUpThreshold); 14132 } 14133 14134 warmUpThreshold = op.getIntOption("baseline-warmup-threshold"); 14135 if (warmUpThreshold >= 0) { 14136 jit::JitOptions.baselineJitWarmUpThreshold = warmUpThreshold; 14137 } 14138 14139 warmUpThreshold = op.getIntOption("trial-inlining-warmup-threshold"); 14140 if (warmUpThreshold >= 0) { 14141 jit::JitOptions.trialInliningWarmUpThreshold = warmUpThreshold; 14142 } 14143 14144 warmUpThreshold = op.getIntOption("regexp-warmup-threshold"); 14145 if (warmUpThreshold >= 0) { 14146 jit::JitOptions.regexpWarmUpThreshold = warmUpThreshold; 14147 } 14148 14149 if (op.getBoolOption("baseline-eager")) { 14150 jit::JitOptions.setEagerBaselineCompilation(); 14151 } 14152 14153 #ifdef ENABLE_PORTABLE_BASELINE_INTERP 14154 if (op.getBoolOption("portable-baseline-eager")) { 14155 jit::JitOptions.setEagerPortableBaselineInterpreter(); 14156 } 14157 if (op.getBoolOption("portable-baseline")) { 14158 jit::JitOptions.portableBaselineInterpreter = true; 14159 } 14160 if (op.getBoolOption("no-portable-baseline")) { 14161 jit::JitOptions.portableBaselineInterpreter = false; 14162 } 14163 #endif 14164 14165 #ifdef ENABLE_JS_AOT_ICS 14166 if (op.getBoolOption("aot-ics")) { 14167 jit::JitOptions.enableAOTICs = true; 14168 } 14169 if (op.getBoolOption("enforce-aot-ics")) { 14170 jit::JitOptions.enableAOTICEnforce = true; 14171 } 14172 #endif 14173 14174 if (op.getBoolOption("blinterp")) { 14175 jit::JitOptions.baselineInterpreter = true; 14176 } 14177 14178 if (op.getBoolOption("no-blinterp")) { 14179 jit::JitOptions.baselineInterpreter = false; 14180 } 14181 14182 if (op.getBoolOption("disable-jithints")) { 14183 jit::JitOptions.disableJitHints = true; 14184 } 14185 14186 if (op.getBoolOption("emit-interpreter-entry")) { 14187 jit::JitOptions.emitInterpreterEntryTrampoline = true; 14188 } 14189 14190 if (op.getBoolOption("no-emit-interpreter-entry")) { 14191 jit::JitOptions.emitInterpreterEntryTrampoline = false; 14192 } 14193 14194 warmUpThreshold = op.getIntOption("blinterp-warmup-threshold"); 14195 if (warmUpThreshold >= 0) { 14196 jit::JitOptions.baselineInterpreterWarmUpThreshold = warmUpThreshold; 14197 } 14198 14199 if (op.getBoolOption("blinterp-eager")) { 14200 jit::JitOptions.baselineInterpreterWarmUpThreshold = 0; 14201 } 14202 14203 if (op.getBoolOption("no-baseline")) { 14204 jit::JitOptions.baselineJit = false; 14205 } 14206 14207 if (op.getBoolOption("no-ion")) { 14208 jit::JitOptions.ion = false; 14209 } 14210 14211 if (op.getBoolOption("no-native-regexp")) { 14212 jit::JitOptions.nativeRegExp = false; 14213 } 14214 14215 if (op.getBoolOption("trace-regexp-parser")) { 14216 jit::JitOptions.trace_regexp_parser = true; 14217 } 14218 if (op.getBoolOption("trace-regexp-assembler")) { 14219 jit::JitOptions.trace_regexp_assembler = true; 14220 } 14221 if (op.getBoolOption("trace-regexp-interpreter")) { 14222 jit::JitOptions.trace_regexp_bytecodes = true; 14223 } 14224 if (op.getBoolOption("trace-regexp-peephole")) { 14225 jit::JitOptions.trace_regexp_peephole_optimization = true; 14226 } 14227 14228 if (op.getBoolOption("less-debug-code")) { 14229 jit::JitOptions.lessDebugCode = true; 14230 } 14231 14232 int32_t inliningEntryThreshold = op.getIntOption("inlining-entry-threshold"); 14233 if (inliningEntryThreshold > 0) { 14234 jit::JitOptions.inliningEntryThreshold = inliningEntryThreshold; 14235 } 14236 14237 int32_t smallFunctionLength = op.getIntOption("small-function-length"); 14238 if (smallFunctionLength > 0) { 14239 jit::JitOptions.smallFunctionMaxBytecodeLength = smallFunctionLength; 14240 } 14241 14242 if (op.getBoolOption("ion-eager")) { 14243 jit::JitOptions.setEagerIonCompilation(); 14244 } 14245 14246 offthreadBaselineCompilation = false; 14247 if (const char* str = op.getStringOption("baseline-offthread-compile")) { 14248 if (strcmp(str, "on") == 0) { 14249 offthreadBaselineCompilation = true; 14250 } else if (strcmp(str, "off") != 0) { 14251 return OptionFailure("baseline-offthread-compile", str); 14252 } 14253 } 14254 cx->runtime()->setOffthreadBaselineCompilationEnabled( 14255 offthreadBaselineCompilation); 14256 14257 if (const char* str = op.getStringOption("baseline-batching")) { 14258 if (strcmp(str, "on") == 0) { 14259 jit::JitOptions.baselineBatching = true; 14260 } else if (strcmp(str, "off") == 0) { 14261 jit::JitOptions.baselineBatching = false; 14262 } else { 14263 return OptionFailure("baseline-batching", str); 14264 } 14265 } 14266 14267 int32_t queueCapacity = op.getIntOption("baseline-queue-capacity"); 14268 if (queueCapacity == 0) { 14269 fprintf(stderr, "baseline-queue-capacity must be positive\n"); 14270 return false; 14271 } 14272 if (queueCapacity > 0) { 14273 jit::JitOptions.baselineQueueCapacity = std::min( 14274 uint32_t(queueCapacity), js::jit::BaselineCompileQueue::MaxCapacity); 14275 } 14276 14277 offthreadIonCompilation = true; 14278 if (const char* str = op.getStringOption("ion-offthread-compile")) { 14279 if (strcmp(str, "off") == 0) { 14280 offthreadIonCompilation = false; 14281 } else if (strcmp(str, "on") != 0) { 14282 return OptionFailure("ion-offthread-compile", str); 14283 } 14284 } 14285 cx->runtime()->setOffthreadIonCompilationEnabled(offthreadIonCompilation); 14286 14287 if (op.getStringOption("ion-parallel-compile")) { 14288 fprintf(stderr, 14289 "--ion-parallel-compile is deprecated. Please use " 14290 "--ion-offthread-compile instead.\n"); 14291 return false; 14292 } 14293 14294 if (op.getBoolOption("disable-bailout-loop-check")) { 14295 jit::JitOptions.disableBailoutLoopCheck = true; 14296 } 14297 14298 if (op.getBoolOption("only-inline-selfhosted")) { 14299 jit::JitOptions.onlyInlineSelfHosted = true; 14300 } 14301 14302 if (op.getBoolOption("enable-ic-frame-pointers")) { 14303 jit::JitOptions.enableICFramePointers = true; 14304 } 14305 14306 if (const char* str = op.getStringOption("ion-iterator-indices")) { 14307 if (strcmp(str, "on") == 0) { 14308 jit::JitOptions.disableIteratorIndices = false; 14309 } else if (strcmp(str, "off") == 0) { 14310 jit::JitOptions.disableIteratorIndices = true; 14311 } else { 14312 return OptionFailure("ion-iterator-indices", str); 14313 } 14314 } 14315 14316 if (const char* str = op.getStringOption("ion-load-keys")) { 14317 if (strcmp(str, "on") == 0) { 14318 jit::JitOptions.disableMarkLoadsUsedAsPropertyKeys = false; 14319 } else if (strcmp(str, "off") == 0) { 14320 jit::JitOptions.disableMarkLoadsUsedAsPropertyKeys = true; 14321 } else { 14322 return OptionFailure("ion-load-keys", str); 14323 } 14324 } 14325 14326 #if defined(JS_SIMULATOR_ARM) 14327 if (op.getBoolOption("arm-sim-icache-checks")) { 14328 jit::SimulatorProcess::ICacheCheckingDisableCount = 0; 14329 } 14330 14331 int32_t stopAt = op.getIntOption("arm-sim-stop-at"); 14332 if (stopAt >= 0) { 14333 jit::Simulator::StopSimAt = stopAt; 14334 } 14335 #elif defined(JS_SIMULATOR_MIPS64) 14336 if (op.getBoolOption("mips-sim-icache-checks")) { 14337 jit::SimulatorProcess::ICacheCheckingDisableCount = 0; 14338 } 14339 14340 int32_t stopAt = op.getIntOption("mips-sim-stop-at"); 14341 if (stopAt >= 0) { 14342 jit::Simulator::StopSimAt = stopAt; 14343 } 14344 #elif defined(JS_SIMULATOR_LOONG64) 14345 if (op.getBoolOption("loong64-sim-icache-checks")) { 14346 jit::SimulatorProcess::ICacheCheckingDisableCount = 0; 14347 } 14348 14349 int32_t stopAt = op.getIntOption("loong64-sim-stop-at"); 14350 if (stopAt >= 0) { 14351 jit::Simulator::StopSimAt = stopAt; 14352 } 14353 #endif 14354 14355 #ifdef DEBUG 14356 # ifdef JS_CODEGEN_RISCV64 14357 if (op.getBoolOption("riscv-debug")) { 14358 jit::Assembler::FLAG_riscv_debug = true; 14359 } 14360 # endif 14361 # ifdef JS_SIMULATOR_RISCV64 14362 if (op.getBoolOption("trace-sim")) { 14363 jit::Simulator::FLAG_trace_sim = true; 14364 } 14365 if (op.getBoolOption("debug-sim")) { 14366 jit::Simulator::FLAG_debug_sim = true; 14367 } 14368 if (op.getBoolOption("riscv-trap-to-simulator-debugger")) { 14369 jit::Simulator::FLAG_riscv_trap_to_simulator_debugger = true; 14370 } 14371 int32_t stopAt = op.getIntOption("riscv-sim-stop-at"); 14372 if (stopAt >= 0) { 14373 jit::Simulator::StopSimAt = stopAt; 14374 } 14375 # endif 14376 #endif 14377 14378 return true; 14379 } 14380 14381 bool SetContextGCOptions(JSContext* cx, const OptionParser& op) { 14382 JS_SetGCParameter(cx, JSGC_MAX_BYTES, 0xffffffff); 14383 14384 size_t nurseryBytes = op.getIntOption("nursery-size") * 1024L * 1024L; 14385 if (nurseryBytes == 0) { 14386 fprintf(stderr, "Error: --nursery-size parameter must be non-zero.\n"); 14387 fprintf(stderr, 14388 "The nursery can be disabled by passing the --no-ggc option.\n"); 14389 return false; 14390 } 14391 JS_SetGCParameter(cx, JSGC_MAX_NURSERY_BYTES, nurseryBytes); 14392 14393 size_t availMemMB = op.getIntOption("available-memory"); 14394 if (availMemMB > 0) { 14395 JS_SetGCParametersBasedOnAvailableMemory(cx, availMemMB); 14396 } 14397 14398 if (const char* opt = op.getStringOption("nursery-strings")) { 14399 if (strcmp(opt, "on") == 0) { 14400 cx->runtime()->gc.nursery().enableStrings(); 14401 } else if (strcmp(opt, "off") == 0) { 14402 cx->runtime()->gc.nursery().disableStrings(); 14403 } else { 14404 MOZ_CRASH("invalid option value for --nursery-strings, must be on/off"); 14405 } 14406 } 14407 14408 if (const char* opt = op.getStringOption("nursery-bigints")) { 14409 if (strcmp(opt, "on") == 0) { 14410 cx->runtime()->gc.nursery().enableBigInts(); 14411 } else if (strcmp(opt, "off") == 0) { 14412 cx->runtime()->gc.nursery().disableBigInts(); 14413 } else { 14414 MOZ_CRASH("invalid option value for --nursery-bigints, must be on/off"); 14415 } 14416 } 14417 14418 bool incrementalGC = !op.getBoolOption("no-incremental-gc"); 14419 JS_SetGCParameter(cx, JSGC_INCREMENTAL_GC_ENABLED, incrementalGC); 14420 14421 #ifndef ANDROID 14422 bool parallelMarking = true; 14423 #else 14424 bool parallelMarking = false; 14425 #endif 14426 if (op.getBoolOption("enable-parallel-marking")) { 14427 parallelMarking = true; 14428 } 14429 if (op.getBoolOption("no-parallel-marking")) { 14430 parallelMarking = false; 14431 } 14432 JS_SetGCParameter(cx, JSGC_PARALLEL_MARKING_ENABLED, parallelMarking); 14433 14434 JS_SetGCParameter(cx, JSGC_SLICE_TIME_BUDGET_MS, 5); 14435 14436 JS_SetGCParameter(cx, JSGC_PER_ZONE_GC_ENABLED, true); 14437 14438 for (MultiStringRange args = op.getMultiStringOption("gc-param"); 14439 !args.empty(); args.popFront()) { 14440 if (!SetGCParameterFromArg(cx, args.front())) { 14441 return false; 14442 } 14443 } 14444 14445 #ifdef DEBUG 14446 dumpEntrainedVariables = op.getBoolOption("dump-entrained-variables"); 14447 #endif 14448 14449 #ifdef JS_GC_ZEAL 14450 const char* zealStr = op.getStringOption("gc-zeal"); 14451 if (zealStr) { 14452 if (!cx->runtime()->gc.parseAndSetZeal(zealStr)) { 14453 return false; 14454 } 14455 uint32_t nextScheduled; 14456 cx->runtime()->gc.getZealBits(&gZealBits, &gZealFrequency, &nextScheduled); 14457 } 14458 #endif 14459 14460 return true; 14461 } 14462 14463 bool InitModuleLoader(JSContext* cx, const OptionParser& op) { 14464 RootedString moduleLoadPath(cx); 14465 if (const char* option = op.getStringOption("module-load-path")) { 14466 UniqueChars pathUtf8 = JS::EncodeNarrowToUtf8(cx, option); 14467 if (!pathUtf8) { 14468 return false; 14469 } 14470 14471 Rooted<JSString*> jspath(cx, NewStringCopyUTF8(cx, pathUtf8.get())); 14472 if (!jspath) { 14473 return false; 14474 } 14475 14476 moduleLoadPath = js::shell::ResolvePath(cx, jspath, RootRelative); 14477 14478 processWideModuleLoadPath = JS_EncodeStringToUTF8(cx, moduleLoadPath); 14479 if (!processWideModuleLoadPath) { 14480 return false; 14481 } 14482 } else { 14483 processWideModuleLoadPath = js::shell::GetCWD(cx); 14484 if (!processWideModuleLoadPath) { 14485 return false; 14486 } 14487 14488 moduleLoadPath = NewStringCopyUTF8(cx, processWideModuleLoadPath.get()); 14489 if (!moduleLoadPath) { 14490 return false; 14491 } 14492 } 14493 14494 ShellContext* sc = GetShellContext(cx); 14495 sc->moduleLoader = js::MakeUnique<ModuleLoader>(); 14496 if (!sc->moduleLoader || !sc->moduleLoader->init(cx, moduleLoadPath)) { 14497 return false; 14498 } 14499 14500 return true; 14501 }