diff options
Diffstat (limited to 'js/src/shell/js.cpp')
-rw-r--r-- | js/src/shell/js.cpp | 338 |
1 files changed, 244 insertions, 94 deletions
diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 03e9e0c109..624b8217c3 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -11,6 +11,7 @@ #include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_ASSERT_IF, MOZ_RELEASE_ASSERT, MOZ_CRASH #include "mozilla/Atomics.h" #include "mozilla/Attributes.h" +#include "mozilla/Compression.h" #include "mozilla/DebugOnly.h" #include "mozilla/EnumSet.h" #include "mozilla/IntegerPrintfMacros.h" @@ -127,7 +128,7 @@ #include "js/ErrorReport.h" // JS::PrintError #include "js/Exception.h" // JS::StealPendingExceptionStack #include "js/experimental/CodeCoverage.h" // js::EnableCodeCoverage -#include "js/experimental/CompileScript.h" // JS::NewFrontendContext, JS::DestroyFrontendContext, JS::HadFrontendErrors, JS::ConvertFrontendErrorsToRuntimeErrors, JS::CompileGlobalScriptToStencil, JS::CompileModuleScriptToStencil, JS::CompilationStorage +#include "js/experimental/CompileScript.h" // JS::NewFrontendContext, JS::DestroyFrontendContext, JS::HadFrontendErrors, JS::ConvertFrontendErrorsToRuntimeErrors, JS::CompileGlobalScriptToStencil, JS::CompileModuleScriptToStencil #include "js/experimental/CTypes.h" // JS::InitCTypesClass #include "js/experimental/Intl.h" // JS::AddMoz{DateTimeFormat,DisplayNames}Constructor #include "js/experimental/JitInfo.h" // JSJit{Getter,Setter,Method}CallArgs, JSJitGetterInfo, JSJit{Getter,Setter}Op, JSJitInfo @@ -213,6 +214,8 @@ #include "vm/Realm-inl.h" #include "vm/Stack-inl.h" +#undef compress + using namespace js; using namespace js::cli; using namespace js::shell; @@ -637,15 +640,11 @@ void OffThreadJob::run() { switch (kind_) { case Kind::CompileScript: { - JS::CompilationStorage compileStorage; - stencil_ = JS::CompileGlobalScriptToStencil(fc_, options_, srcBuf_, - compileStorage); + stencil_ = JS::CompileGlobalScriptToStencil(fc_, options_, srcBuf_); break; } case Kind::CompileModule: { - JS::CompilationStorage compileStorage; - stencil_ = JS::CompileModuleScriptToStencil(fc_, options_, srcBuf_, - compileStorage); + stencil_ = JS::CompileModuleScriptToStencil(fc_, options_, srcBuf_); break; } case Kind::Decode: { @@ -724,12 +723,6 @@ bool shell::enableWasm = false; bool shell::enableSharedMemory = SHARED_MEMORY_DEFAULT; bool shell::enableWasmBaseline = false; bool shell::enableWasmOptimizing = false; - -#define WASM_FEATURE(NAME, _, STAGE, ...) \ - bool shell::enableWasm##NAME = STAGE != WasmFeatureStage::Experimental; -JS_FOR_WASM_FEATURES(WASM_FEATURE); -#undef WASM_FEATURE - bool shell::enableWasmVerbose = false; bool shell::enableTestWasmAwaitTier2 = false; bool shell::enableSourcePragmas = true; @@ -1538,7 +1531,6 @@ static bool BoundToAsyncStack(JSContext* cx, unsigned argc, Value* vp) { } RootedString causeString(cx, ToString(cx, v)); if (!causeString) { - MOZ_ASSERT(cx->isExceptionPending()); return false; } @@ -9004,6 +8996,121 @@ static bool IsValidJSON(JSContext* cx, unsigned argc, Value* vp) { return true; } +// Quick file format for a LZ4 compressed file +static constexpr uint32_t LZ4MagicHeader = -1; +// A magic word and a length field +static constexpr size_t LZ4HeaderSize = sizeof(uint32_t) * 2; +static constexpr size_t LZ4MaxSize = UINT32_MAX; + +static bool CompressLZ4(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + RootedObject callee(cx, &args.callee()); + + if (!args.get(0).isObject() || + !args.get(0).toObject().is<ArrayBufferObject>()) { + ReportUsageErrorASCII(cx, callee, "First argument must be an ArrayBuffer"); + return false; + } + + JS::Rooted<ArrayBufferObject*> bytes( + cx, &args.get(0).toObject().as<ArrayBufferObject>()); + size_t byteLength = bytes->byteLength(); + if (byteLength > LZ4MaxSize) { + ReportOutOfMemory(cx); + return false; + } + + // Create a buffer big enough for the header and the max amount of compressed + // bytes. + size_t outputCapacity = + LZ4HeaderSize + mozilla::Compression::LZ4::maxCompressedSize(byteLength); + + mozilla::UniquePtr<void, JS::FreePolicy> output(js_malloc(outputCapacity)); + if (!output) { + ReportOutOfMemory(cx); + return false; + } + + // Write the magic header word and decompressed size in bytes. + ((uint32_t*)(output.get()))[0] = LZ4MagicHeader; + ((uint32_t*)(output.get()))[1] = byteLength; + + // Compress the bytes into the output + char* compressedBytesStart = ((char*)output.get()) + LZ4HeaderSize; + size_t compressedBytesLength = mozilla::Compression::LZ4::compress( + (const char*)bytes->dataPointer(), byteLength, compressedBytesStart); + size_t outputLength = compressedBytesLength + LZ4HeaderSize; + + // Create an ArrayBuffer wrapping the compressed bytes + JSObject* outputArrayBuffer = + NewArrayBufferWithContents(cx, outputLength, std::move(output)); + if (!outputArrayBuffer) { + return false; + } + + args.rval().setObject(*outputArrayBuffer); + return true; +} + +static bool DecompressLZ4(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + RootedObject callee(cx, &args.callee()); + + if (!args.get(0).isObject() || + !args.get(0).toObject().is<ArrayBufferObject>()) { + ReportUsageErrorASCII(cx, callee, "First argument must be an ArrayBuffer"); + return false; + } + + JS::Rooted<ArrayBufferObject*> bytes( + cx, &args.get(0).toObject().as<ArrayBufferObject>()); + size_t byteLength = bytes->byteLength(); + if (byteLength < LZ4HeaderSize) { + JS_ReportErrorASCII(cx, "Invalid LZ4 buffer"); + return false; + } + + // Check the magic header and get the decompressed byte length. + uint32_t magicHeader = ((uint32_t*)(bytes->dataPointer()))[0]; + uint32_t decompressedBytesLength = ((uint32_t*)(bytes->dataPointer()))[1]; + if (magicHeader != LZ4MagicHeader) { + JS_ReportErrorASCII(cx, "Invalid magic header"); + return false; + } + + // Allocate a buffer to store the decompressed bytes. + mozilla::UniquePtr<void, JS::FreePolicy> decompressedBytes( + js_malloc(decompressedBytesLength)); + if (!decompressedBytes) { + ReportOutOfMemory(cx); + return false; + } + + // Decompress the bytes into the output + const char* compressedBytesStart = + ((const char*)bytes->dataPointer()) + LZ4HeaderSize; + size_t compressedBytesLength = byteLength - LZ4HeaderSize; + size_t actualDecompressedBytesLength = 0; + if (!mozilla::Compression::LZ4::decompress( + compressedBytesStart, compressedBytesLength, + (char*)decompressedBytes.get(), decompressedBytesLength, + &actualDecompressedBytesLength) || + actualDecompressedBytesLength != decompressedBytesLength) { + JS_ReportErrorASCII(cx, "Invalid LZ4 buffer"); + return false; + } + + // Create an ArrayBuffer wrapping the decompressed bytes + JSObject* outputArrayBuffer = NewArrayBufferWithContents( + cx, decompressedBytesLength, std::move(decompressedBytes)); + if (!outputArrayBuffer) { + return false; + } + + args.rval().setObject(*outputArrayBuffer); + return true; +} + // clang-format off static const JSFunctionSpecWithHelp shell_functions[] = { JS_FN_HELP("options", Options, 0, 0, @@ -9675,6 +9782,14 @@ JS_FN_HELP("createUserArrayBuffer", CreateUserArrayBuffer, 1, 0, "isValidJSON(source)", " Returns true if the given source is valid JSON."), + JS_FN_HELP("compressLZ4", CompressLZ4, 1, 0, +"compressLZ4(bytes)", +" Return a compressed copy of bytes using LZ4."), + + JS_FN_HELP("decompressLZ4", DecompressLZ4, 1, 0, +"decompressLZ4(bytes)", +" Return a decompressed copy of bytes using LZ4."), + JS_FS_HELP_END }; // clang-format on @@ -11015,9 +11130,6 @@ static void SetWorkerContextOptions(JSContext* cx) { .setWasm(enableWasm) .setWasmBaseline(enableWasmBaseline) .setWasmIon(enableWasmOptimizing) -#define WASM_FEATURE(NAME, ...) .setWasm##NAME(enableWasm##NAME) - JS_FOR_WASM_FEATURES(WASM_FEATURE) -#undef WASM_FEATURE .setWasmVerbose(enableWasmVerbose) .setTestWasmAwaitTier2(enableTestWasmAwaitTier2) @@ -11453,21 +11565,34 @@ static bool ParsePrefValue(const char* name, const char* val, T* result) { } } -static bool SetJSPref(const char* pref) { - const char* assign = strchr(pref, '='); - if (!assign) { - fprintf(stderr, "Missing '=' for --setpref\n"); - return false; +static bool SetJSPrefToTrueForBool(const char* name) { + // Search for a matching pref and try to set it to a default value for the + // type. +#define CHECK_PREF(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF) \ + if (strcmp(name, NAME) == 0) { \ + if constexpr (std::is_same_v<TYPE, bool>) { \ + JS::Prefs::SETTER(true); \ + return true; \ + } else { \ + fprintf(stderr, "Pref %s must have a value specified.\n", name); \ + return false; \ + } \ } + FOR_EACH_JS_PREF(CHECK_PREF) +#undef CHECK_PREF - size_t nameLen = assign - pref; - const char* valStart = assign + 1; // Skip '='. + // Nothing matched, return false + fprintf(stderr, "Invalid pref name: %s\n", name); + return false; +} - // Search for a matching pref and try to set it. +static bool SetJSPrefToValue(const char* name, size_t nameLen, + const char* value) { + // Search for a matching pref and try to set it to the provided value. #define CHECK_PREF(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF) \ - if (nameLen == strlen(NAME) && memcmp(pref, NAME, strlen(NAME)) == 0) { \ + if (nameLen == strlen(NAME) && memcmp(name, NAME, strlen(NAME)) == 0) { \ TYPE v; \ - if (!ParsePrefValue<TYPE>(NAME, valStart, &v)) { \ + if (!ParsePrefValue<TYPE>(NAME, value, &v)) { \ return false; \ } \ JS::Prefs::SETTER(v); \ @@ -11476,10 +11601,29 @@ static bool SetJSPref(const char* pref) { FOR_EACH_JS_PREF(CHECK_PREF) #undef CHECK_PREF - fprintf(stderr, "Invalid pref name: %s\n", pref); + // Nothing matched, return false + fprintf(stderr, "Invalid pref name: %s\n", name); return false; } +static bool SetJSPref(const char* pref) { + const char* assign = strchr(pref, '='); + if (!assign) { + if (!SetJSPrefToTrueForBool(pref)) { + return false; + } + return true; + } + + size_t nameLen = assign - pref; + const char* valStart = assign + 1; // Skip '='. + + if (!SetJSPrefToValue(pref, nameLen, valStart)) { + return false; + } + return true; +} + static void ListJSPrefs() { auto printPref = [](const char* name, auto defaultVal) { using T = decltype(defaultVal); @@ -11832,20 +11976,8 @@ bool InitOptionParser(OptionParser& op) { !op.addBoolOption('\0', "test-wasm-await-tier2", "Forcibly activate tiering and block " "instantiation on completion of tier2") || -#define WASM_FEATURE(NAME, LOWER_NAME, STAGE, COMPILE_PRED, COMPILER_PRED, \ - FLAG_PRED, FLAG_FORCE_ON, FLAG_FUZZ_ON, SHELL, ...) \ - !op.addBoolOption('\0', "no-wasm-" SHELL, \ - STAGE == WasmFeatureStage::Experimental \ - ? "No-op." \ - : "Disable wasm " SHELL " feature.") || \ - !op.addBoolOption('\0', "wasm-" SHELL, \ - STAGE == WasmFeatureStage::Experimental \ - ? "Enable wasm " SHELL " feature." \ - : "No-op.") || - JS_FOR_WASM_FEATURES(WASM_FEATURE) -#undef WASM_FEATURE - !op.addBoolOption('\0', "no-native-regexp", - "Disable native regexp compilation") || + !op.addBoolOption('\0', "no-native-regexp", + "Disable native regexp compilation") || !op.addIntOption( '\0', "regexp-warmup-threshold", "COUNT", "Wait for COUNT invocations before compiling regexps to native code " @@ -12206,14 +12338,29 @@ bool InitOptionParser(OptionParser& op) { #endif !op.addStringOption('\0', "telemetry-dir", "[directory]", "Output telemetry results in a directory") || - !op.addMultiStringOption('\0', "setpref", "name=val", - "Set the value of a JS pref. Use --list-prefs " + !op.addMultiStringOption('P', "setpref", "name[=val]", + "Set the value of a JS pref. The value may " + "be omitted for boolean prefs, in which case " + "they default to true. Use --list-prefs " "to print all pref names.") || !op.addBoolOption( '\0', "list-prefs", "Print list of prefs that can be set with --setpref.") || !op.addBoolOption('\0', "use-fdlibm-for-sin-cos-tan", - "Use fdlibm for Math.sin, Math.cos, and Math.tan")) { + "Use fdlibm for Math.sin, Math.cos, and Math.tan") || + !op.addBoolOption('\0', "wasm-gc", "Enable WebAssembly gc proposal.") || + !op.addBoolOption('\0', "wasm-relaxed-simd", + "Enable WebAssembly relaxed-simd proposal.") || + !op.addBoolOption('\0', "wasm-multi-memory", + "Enable WebAssembly multi-memory proposal.") || + !op.addBoolOption('\0', "wasm-memory-control", + "Enable WebAssembly memory-control proposal.") || + !op.addBoolOption('\0', "wasm-memory64", + "Enable WebAssembly memory64 proposal.") || + !op.addBoolOption('\0', "wasm-tail-calls", + "Enable WebAssembly tail-calls proposal.") || + !op.addBoolOption('\0', "wasm-js-string-builtins", + "Enable WebAssembly js-string-builtins proposal.")) { return false; } @@ -12234,36 +12381,60 @@ bool SetGlobalOptionsPreJSInit(const OptionParser& op) { // Override pref values for prefs that have a custom shell flag. // If you're adding a new feature, consider using --setpref instead. - JS::Prefs::setAtStartup_array_grouping( - !op.getBoolOption("disable-array-grouping")); - JS::Prefs::setAtStartup_arraybuffer_transfer( - !op.getBoolOption("disable-arraybuffer-transfer")); - JS::Prefs::set_experimental_shadow_realms( - op.getBoolOption("enable-shadow-realms")); - JS::Prefs::setAtStartup_well_formed_unicode_strings( - !op.getBoolOption("disable-well-formed-unicode-strings")); + if (op.getBoolOption("disable-array-grouping")) { + JS::Prefs::setAtStartup_array_grouping(false); + } + if (op.getBoolOption("disable-arraybuffer-transfer")) { + JS::Prefs::setAtStartup_arraybuffer_transfer(false); + } + if (op.getBoolOption("enable-shadow-realms")) { + JS::Prefs::set_experimental_shadow_realms(true); + } + if (op.getBoolOption("disable-well-formed-unicode-strings")) { + JS::Prefs::setAtStartup_well_formed_unicode_strings(false); + } #ifdef NIGHTLY_BUILD - JS::Prefs::setAtStartup_experimental_arraybuffer_resizable( - op.getBoolOption("enable-arraybuffer-resizable")); - JS::Prefs::setAtStartup_experimental_sharedarraybuffer_growable( - op.getBoolOption("enable-arraybuffer-resizable")); - JS::Prefs::setAtStartup_experimental_iterator_helpers( - op.getBoolOption("enable-iterator-helpers")); - JS::Prefs::setAtStartup_experimental_new_set_methods( - op.getBoolOption("enable-new-set-methods")); - JS::Prefs::setAtStartup_experimental_symbols_as_weakmap_keys( - op.getBoolOption("enable-symbols-as-weakmap-keys")); + if (op.getBoolOption("enable-arraybuffer-resizable")) { + JS::Prefs::setAtStartup_experimental_arraybuffer_resizable(true); + JS::Prefs::setAtStartup_experimental_sharedarraybuffer_growable(true); + } + if (op.getBoolOption("enable-iterator-helpers")) { + JS::Prefs::setAtStartup_experimental_iterator_helpers(true); + } + if (op.getBoolOption("enable-new-set-methods")) { + JS::Prefs::setAtStartup_experimental_new_set_methods(true); + } + if (op.getBoolOption("enable-symbols-as-weakmap-keys")) { + JS::Prefs::setAtStartup_experimental_symbols_as_weakmap_keys(true); + } #endif - JS::Prefs::setAtStartup_weakrefs(!op.getBoolOption("disable-weak-refs")); + if (op.getBoolOption("disable-weak-refs")) { + JS::Prefs::setAtStartup_weakrefs(false); + } JS::Prefs::setAtStartup_experimental_weakrefs_expose_cleanupSome(true); - JS::Prefs::setAtStartup_destructuring_fuse( - !op.getBoolOption("disable-destructuring-fuse")); + if (op.getBoolOption("disable-destructuring-fuse")) { + JS::Prefs::setAtStartup_destructuring_fuse(false); + } + if (op.getBoolOption("disable-property-error-message-fix")) { + JS::Prefs::setAtStartup_property_error_message_fix(false); + } + JS::Prefs::set_use_fdlibm_for_sin_cos_tan( op.getBoolOption("use-fdlibm-for-sin-cos-tan")); - JS::Prefs::setAtStartup_property_error_message_fix( - !op.getBoolOption("disable-property-error-message-fix")); + + if (op.getBoolOption("wasm-gc") || op.getBoolOption("wasm-relaxed-simd") || + op.getBoolOption("wasm-multi-memory") || + op.getBoolOption("wasm-memory-control") || + op.getBoolOption("wasm-memory64") || + op.getBoolOption("wasm-tail-calls") || + op.getBoolOption("wasm-js-string-builtins")) { + fprintf( + stderr, + "Wasm shell flags are now using prefs, use -P wasm_feature instead.\n"); + return false; + } if (op.getBoolOption("list-prefs")) { ListJSPrefs(); @@ -12556,17 +12727,6 @@ bool SetContextWasmOptions(JSContext* cx, const OptionParser& op) { } } -#define WASM_FEATURE(NAME, LOWER_NAME, STAGE, COMPILE_PRED, COMPILER_PRED, \ - FLAG_PRED, FLAG_FORCE_ON, FLAG_FUZZ_ON, SHELL, ...) \ - if (STAGE == WasmFeatureStage::Experimental) { \ - enableWasm##NAME = op.getBoolOption("wasm-" SHELL); \ - } else { \ - enableWasm##NAME = !op.getBoolOption("no-wasm-" SHELL); \ - } - - JS_FOR_WASM_FEATURES(WASM_FEATURE); -#undef WASM_FEATURE - enableWasmVerbose = op.getBoolOption("wasm-verbose"); enableTestWasmAwaitTier2 = op.getBoolOption("test-wasm-await-tier2"); @@ -12575,11 +12735,7 @@ bool SetContextWasmOptions(JSContext* cx, const OptionParser& op) { .setWasm(enableWasm) .setWasmForTrustedPrinciples(enableWasm) .setWasmBaseline(enableWasmBaseline) - .setWasmIon(enableWasmOptimizing) -#define WASM_FEATURE(NAME, ...) .setWasm##NAME(enableWasm##NAME) - JS_FOR_WASM_FEATURES(WASM_FEATURE) -#undef WASM_FEATURE - ; + .setWasmIon(enableWasmOptimizing); #ifndef __wasi__ // This must be set before self-hosted code is initialized, as self-hosted @@ -12598,18 +12754,12 @@ bool SetContextWasmOptions(JSContext* cx, const OptionParser& op) { // Also the following are to be propagated. const char* to_propagate[] = { -# define WASM_FEATURE(NAME, LOWER_NAME, STAGE, COMPILE_PRED, COMPILER_PRED, \ - FLAG_PRED, FLAG_FORCE_ON, FLAG_FUZZ_ON, SHELL, ...) \ - STAGE == WasmFeatureStage::Experimental ? "--wasm-" SHELL \ - : "--no-wasm-" SHELL, - JS_FOR_WASM_FEATURES(WASM_FEATURE) -# undef WASM_FEATURE // Compiler selection options "--test-wasm-await-tier2", - NULL}; - for (const char** p = &to_propagate[0]; *p; p++) { - if (op.getBoolOption(&(*p)[2] /* 2 => skip the leading '--' */)) { - if (!sCompilerProcessFlags.append(*p)) { + }; + for (const char* p : to_propagate) { + if (op.getBoolOption(p + 2 /* 2 => skip the leading '--' */)) { + if (!sCompilerProcessFlags.append(p)) { return false; } } |