diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 085b93c9090..a7683e47a1c 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -42,6 +42,9 @@ jobs: brew install llvm yasm - name: Run sccache-cache uses: mozilla-actions/sccache-action@v0.0.4 + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: "15.4" - name: Build run: | cargo build --verbose --features ${{ matrix.features }} @@ -137,7 +140,7 @@ jobs: uses: nttld/setup-ndk@v1 id: setup-ndk with: - ndk-version: r25c + ndk-version: r26c - name: Install Rust uses: dtolnay/rust-toolchain@stable with: @@ -192,8 +195,7 @@ jobs: publish-release: name: Check version and publish release runs-on: ubuntu-latest - needs: - ["linux", "mac", "windows"] + needs: ["linux", "mac", "windows"] if: ${{ !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }} steps: - uses: actions/checkout@v4 @@ -225,7 +227,16 @@ jobs: name: Result runs-on: ubuntu-latest needs: - ["android", "linux", "linux-cross-compile", "mac", "windows", "integrity", "publish-release", "verify-release"] + [ + "android", + "linux", + "linux-cross-compile", + "mac", + "windows", + "integrity", + "publish-release", + "verify-release", + ] if: ${{ always() }} steps: - name: Mark the job as successful @@ -234,4 +245,3 @@ jobs: - name: Mark the job as unsuccessful if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }} run: exit 1 - diff --git a/android-build b/android-build index e263436874d..3dc36d36ebc 100755 --- a/android-build +++ b/android-build @@ -12,7 +12,7 @@ import sys from typing import Dict, Optional -SUPPORTED_NDK_VERSION = '25' +SUPPORTED_NDK_VERSION = '26' API_LEVEL = '30' diff --git a/mozjs-sys/Cargo.toml b/mozjs-sys/Cargo.toml index c042f844d4f..1b403fa6f48 100644 --- a/mozjs-sys/Cargo.toml +++ b/mozjs-sys/Cargo.toml @@ -29,10 +29,13 @@ jitspew = [] streams = [] [dependencies] -encoding_c = "0.9.8" -encoding_c_mem = "0.2.6" libc.workspace = true +# SM depends on them and we provide them using cargo libz-sys = "1.1.13" +encoding_c = "0.9.8" +encoding_c_mem = "0.2.6" +# unicode-bidi-ffi = { path = "./mozjs/intl/bidi/rust/unicode-bidi-ffi" } +icu_capi = "1.4.0" # keep in sync with intl/icu_capi/Cargo.toml [build-dependencies] bindgen.workspace = true diff --git a/mozjs-sys/build.rs b/mozjs-sys/build.rs index e97b410e75b..038ca77ec2d 100644 --- a/mozjs-sys/build.rs +++ b/mozjs-sys/build.rs @@ -84,6 +84,8 @@ fn main() { if build_from_source { fs::create_dir_all(&build_dir).expect("could not create build dir"); + //TODO: use this and remove `no-rust-unicode-bidi.patch` + //cbindgen_bidi(&build_dir); build_spidermonkey(&build_dir); build_jsapi(&build_dir); build_jsapi_bindings(&build_dir); @@ -218,6 +220,22 @@ fn find_moztools() -> Option { } } +/* +fn cbindgen_bidi(build_dir: &Path) { + /// Appends intl/bidi to `root` + fn root_to_bidi(root: &Pah) -> PathBuf { + root.join("intl").join("bidi") + } + let mozjs_sys_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + + cbindgen::Builder::new() + .with_crate(root_to_bidi(mozjs_sys_dir.join("mozjs")).join("rust").join("unicode-bidi-ffi")) + .generate() + .expect("Unable to generate bindings") + .write_to_file(root_to_bidi(build_dir).join("unicode_bidi_ffi_generated.h")); +} +*/ + fn build_spidermonkey(build_dir: &Path) { let target = env::var("TARGET").unwrap(); let make; @@ -513,7 +531,6 @@ const BLACKLIST_FUNCTIONS: &'static [&'static str] = &[ "JS::EncodeStencil", "JS::FinishDecodeMultiStencilsOffThread", "JS::FinishIncrementalEncoding", - "JS::FinishOffThreadStencil", "JS::FromPropertyDescriptor", "JS::GetExceptionCause", "JS::GetModulePrivate", diff --git a/mozjs-sys/etc/filters.txt b/mozjs-sys/etc/filters.txt index 8b053f21e65..81f690e8e10 100644 --- a/mozjs-sys/etc/filters.txt +++ b/mozjs-sys/etc/filters.txt @@ -30,7 +30,7 @@ - /python/mozbuild/mozbuild/test/frontend/data - /testing/mozbase/rust - /third_party/rust -- Cargo.toml +- /Cargo.toml # Include the rest. + /* diff --git a/mozjs-sys/etc/patches/0003-No-builtin-available.patch b/mozjs-sys/etc/patches/0003-No-builtin-available.patch deleted file mode 100644 index 80e143d4293..00000000000 --- a/mozjs-sys/etc/patches/0003-No-builtin-available.patch +++ /dev/null @@ -1,19 +0,0 @@ -diff --git a/mozglue/misc/Mutex_posix.cpp b/mozglue/misc/Mutex_posix.cpp -index 7378a544f2..2614a2ef3c 100644 ---- a/mozglue/misc/Mutex_posix.cpp -+++ b/mozglue/misc/Mutex_posix.cpp -@@ -66,11 +66,9 @@ mozilla::detail::MutexImpl::MutexImpl() { - "mozilla::detail::MutexImpl::MutexImpl: " - "pthread_mutexattr_settype failed"); - # elif defined(POLICY_KIND) -- if (__builtin_available(macOS 10.14, *)) { -- TRY_CALL_PTHREADS(pthread_mutexattr_setpolicy_np(&attr, POLICY_KIND), -- "mozilla::detail::MutexImpl::MutexImpl: " -- "pthread_mutexattr_setpolicy_np failed"); -- } -+ (pthread_mutexattr_setpolicy_np(&attr, POLICY_KIND), -+ "mozilla::detail::MutexImpl::MutexImpl: " -+ "pthread_mutexattr_setpolicy_np failed"); - # endif - attrp = &attr; - #endif diff --git a/mozjs-sys/etc/patches/0005-Do-not-rely-on-internal-.cargo-config.patch b/mozjs-sys/etc/patches/0005-Do-not-rely-on-internal-.cargo-config.patch index 6ad2a1ae2ce..589f28180cb 100644 --- a/mozjs-sys/etc/patches/0005-Do-not-rely-on-internal-.cargo-config.patch +++ b/mozjs-sys/etc/patches/0005-Do-not-rely-on-internal-.cargo-config.patch @@ -5,38 +5,46 @@ Subject: [PATCH] Do not rely on internal .cargo/config diff --git a/config/recurse.mk b/config/recurse.mk -index 4188e9e2136d..21984e7f808a 100644 +index 7956578ac..7ff2ec209 100644 --- a/config/recurse.mk +++ b/config/recurse.mk -@@ -205,18 +205,6 @@ endif +@@ -201,24 +201,6 @@ endif # Those need to depend on config/export for system wrappers. $(addprefix build/unix/stdc++compat/,target host) build/clang-plugin/host: config/export - + -# Rust targets, and export targets that run cbindgen need --# $topobjdir/.cargo/config to be preprocessed first. Ideally, we'd only set it +-# $topobjdir/.cargo/config.toml to be preprocessed first. Ideally, we'd only set it -# as a dependency of the rust targets, but unfortunately, that pushes Make to -# execute them much later than we'd like them to be when the file doesn't exist -# prior to Make running. So we also set it as a dependency of pre-export, which -# ensures it exists before recursing the rust targets and the export targets -# that run cbindgen, tricking Make into keeping them early. --$(rust_targets): $(DEPTH)/.cargo/config +-# When $topobjdir/.cargo/config exists from an old build, we also remove it because +-# cargo will prefer to use it rather than config.toml. +-CARGO_CONFIG_DEPS = $(DEPTH)/.cargo/config.toml +-ifneq (,$(wildcard $(DEPTH)/.cargo/config)) +-CARGO_CONFIG_DEPS += $(MDDEPDIR)/cargo-config-cleanup.stub +-endif +-$(rust_targets): $(CARGO_CONFIG_DEPS) -ifndef TEST_MOZBUILD --pre-export:: $(DEPTH)/.cargo/config +-recurse_pre-export: $(CARGO_CONFIG_DEPS) -endif - - # When building gtest as part of the build (LINK_GTEST_DURING_COMPILE), - # force the build system to get to it first, so that it can be linked - # quickly without LTO, allowing the build system to go ahead with + $(MDDEPDIR)/cargo-config-cleanup.stub: + rm $(DEPTH)/.cargo/config + touch $@ diff --git a/moz.build b/moz.build -index 94419df73ada..487b8b4dbabd 100644 +index 9db27fd05..a330fd1aa 100644 --- a/moz.build +++ b/moz.build -@@ -163,6 +163,4 @@ if CONFIG['MOZ_BUILD_APP']: +@@ -183,10 +183,6 @@ if CONFIG["MOZ_BUILD_APP"]: else: include("/toolkit/toolkit.mozbuild") --OBJDIR_PP_FILES[".cargo"] += [".cargo/config.in"] +-OBJDIR_PP_FILES[".cargo"] += [ +- CONFIG["MOZ_OVERRIDE_CARGO_CONFIG"] or ".cargo/config.toml.in" +-] - DEFINES["top_srcdir"] = TOPSRCDIR - + SPHINX_TREES["contributing"] = "docs/contributing" diff --git a/mozjs-sys/etc/patches/0010-update-mozboot-to-ndk-r25.patch b/mozjs-sys/etc/patches/0010-update-mozboot-to-ndk-r25.patch deleted file mode 100644 index 97eed0941ec..00000000000 --- a/mozjs-sys/etc/patches/0010-update-mozboot-to-ndk-r25.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git b/python/mozboot/mozboot/android.py a/python/mozboot/mozboot/android.py -index 26929da69..75dfd5524 100644 ---- b/python/mozboot/mozboot/android.py -+++ a/python/mozboot/mozboot/android.py -@@ -20,7 +20,7 @@ from tqdm import tqdm - # variable. - from mozboot.bootstrap import MOZCONFIG_SUGGESTION_TEMPLATE - --NDK_VERSION = "r23c" -+NDK_VERSION = "r25c" - CMDLINE_TOOLS_VERSION_STRING = "9.0" - CMDLINE_TOOLS_VERSION = "9477386" - diff --git a/mozjs-sys/etc/patches/0014-Remove-everything-about-bindgen-from-SM-itself.patch b/mozjs-sys/etc/patches/0014-Remove-everything-about-bindgen-from-SM-itself.patch deleted file mode 100644 index 24f3324c19a..00000000000 --- a/mozjs-sys/etc/patches/0014-Remove-everything-about-bindgen-from-SM-itself.patch +++ /dev/null @@ -1,308 +0,0 @@ -diff --git a/build/moz.configure/bindgen.configure b/build/moz.configure/bindgen.configure -index 5ce66fe9a..b06fab152 100644 ---- a/build/moz.configure/bindgen.configure -+++ b/build/moz.configure/bindgen.configure -@@ -101,303 +101,3 @@ rustfmt = check_prog( - input="RUSTFMT", - allow_missing=True, - ) -- -- --option( -- "--with-libclang-path", -- nargs=1, -- help="Absolute path to a directory containing Clang/LLVM libraries for bindgen (version 3.9.x or above)", --) --option( -- "--with-clang-path", -- nargs=1, -- help="Absolute path to a Clang binary for bindgen (version 3.9.x or above)", --) -- -- --@depends( -- "--with-clang-path", -- configure_cache, -- c_compiler, -- cxx_compiler, -- clang_search_path, -- target, -- target_sysroot.path, -- android_version, --) --@checking("for clang for bindgen", lambda x: x.path if x else "not found") --def bindgen_clang_compiler( -- clang_path, -- configure_cache, -- c_compiler, -- cxx_compiler, -- clang_search_path, -- target, -- sysroot_path, -- android_version, --): -- # When the target compiler is clang, use that, including flags. -- if cxx_compiler.type == "clang": -- if clang_path and clang_path[0] not in ( -- c_compiler.compiler, -- cxx_compiler.compiler, -- ): -- die( -- "--with-clang-path is not valid when the target compiler is %s", -- cxx_compiler.type, -- ) -- return namespace( -- path=cxx_compiler.compiler, -- flags=cxx_compiler.flags, -- ) -- # When the target compiler is clang-cl, use clang in the same directory, -- # and figure the right flags to use. -- if cxx_compiler.type == "clang-cl": -- if clang_path and os.path.dirname(clang_path[0]) != os.path.dirname( -- cxx_compiler.compiler -- ): -- die( -- "--with-clang-path must point to clang in the same directory " -- "as the target compiler" -- ) -- if not clang_path: -- clang_path = [os.path.join(os.path.dirname(cxx_compiler.compiler), "clang")] -- -- clang_path = find_program( -- clang_path[0] if clang_path else "clang++", clang_search_path -- ) -- if not clang_path: -- return -- # Hack before bug 1617793: if the compiler is clang-cl, hack the target -- if cxx_compiler.type == "clang-cl": -- target = split_triplet("%s-pc-windows-msvc" % target.raw_cpu) -- flags = [] -- if sysroot_path: -- flags.extend(("--sysroot", sysroot_path)) -- info = check_compiler( -- configure_cache, [clang_path] + flags, "C++", target, android_version -- ) -- # Usually, one check_compiler pass would be enough, but when cross-compiling -- # and the host and target don't use the same default C++ standard, we don't -- # get the --std flag, so try again. This is the same thing as valid_compiler() -- # does in toolchain.configure. -- if info.flags: -- flags += info.flags -- info = check_compiler( -- configure_cache, [clang_path] + flags, "C++", target, android_version -- ) -- return namespace( -- path=clang_path, -- flags=flags + info.flags, -- ) -- -- --@depends("--with-libclang-path", bindgen_clang_compiler, host_library_name_info, host) --@checking("for libclang for bindgen", lambda x: x if x else "not found") --@imports("glob") --@imports(_from="os", _import="pathsep") --@imports(_from="os.path", _import="split", _as="pathsplit") --@imports("re") --def bindgen_libclang_path(libclang_path, clang, library_name_info, host): -- if not clang: -- if libclang_path: -- die( -- "--with-libclang-path is not valid without a clang compiler " -- "for bindgen" -- ) -- return -- -- # Try to ensure that the clang shared library that bindgen is going -- # to look for is actually present. The files that we search for -- # mirror the logic in clang-sys/build.rs. -- libclang_choices = [] -- if host.os == "WINNT": -- libclang_choices.append("libclang.dll") -- libclang_choices.append( -- "%sclang%s" % (library_name_info.dll.prefix, library_name_info.dll.suffix) -- ) -- if host.kernel == "Linux": -- libclang_choices.append("libclang.so.*") -- -- if host.os == "OpenBSD": -- libclang_choices.append("libclang.so.*.*") -- -- candidates = [] -- if not libclang_path: -- # Try to find libclang_path based on clang search dirs. -- clang_search_dirs = check_cmd_output(clang.path, "-print-search-dirs") -- for line in clang_search_dirs.splitlines(): -- name, _, value = line.partition(": =") -- if host.os == "WINNT" and name == "programs": -- # On Windows, libclang.dll is in bin/ rather than lib/, -- # so scan the programs search dirs. -- # To make matters complicated, clang before version 9 uses `:` -- # separate between paths (and `;` in newer versions) -- if pathsep in value: -- candidates.extend(value.split(pathsep)) -- else: -- for part in value.split(":"): -- # Assume that if previous "candidate" was of length 1, -- # it's a drive letter and the current part is the rest of -- # the corresponding full path. -- if candidates and len(candidates[-1]) == 1: -- candidates[-1] += ":" + part -- else: -- candidates.append(part) -- elif host.os != "WINNT" and name == "libraries": -- # On other platforms, use the directories from the libraries -- # search dirs that looks like $something/clang/$version. -- for dir in value.split(pathsep): -- dir, version = pathsplit(dir) -- if re.match(r"[0-9.]+", version): -- dir, name = pathsplit(dir) -- if name == "clang": -- candidates.append(dir) -- else: -- candidates.append(libclang_path[0]) -- -- for dir in candidates: -- for pattern in libclang_choices: -- log.debug('Trying "%s" in "%s"', pattern, dir) -- libs = glob.glob(os.path.join(dir, pattern)) -- if libs: -- return libs[0] -- -- --@depends(bindgen_clang_compiler, bindgen_libclang_path, build_project) --def bindgen_config_paths(clang, libclang, build_project): -- # XXX: we want this code to be run for both Gecko and JS, but we don't -- # necessarily want to force a bindgen/Rust dependency on JS just yet. -- # Actually, we don't want to force an error if we're not building the -- # browser generally. We therefore whitelist the projects that require -- # bindgen facilities at this point and leave it at that. -- if build_project in ("browser", "mobile/android"): -- if not clang: -- die( -- "Could not find clang to generate run bindings for C/C++. " -- "Please install the necessary packages, run `mach bootstrap`, " -- "or use --with-clang-path to give the location of clang." -- ) -- -- if not libclang: -- die( -- "Could not find libclang to generate rust bindings for C/C++. " -- "Please install the necessary packages, run `mach bootstrap`, " -- "or use --with-libclang-path to give the path containing it." -- ) -- -- if clang and libclang: -- return namespace( -- libclang=libclang, -- libclang_path=os.path.dirname(libclang), -- clang_path=clang.path, -- clang_flags=clang.flags, -- ) -- -- --@depends(bindgen_config_paths.libclang, when=bindgen_config_paths) --@checking("that libclang is new enough", lambda s: "yes" if s else "no") --@imports(_from="ctypes", _import="CDLL") --@imports(_from="textwrap", _import="dedent") --def min_libclang_version(libclang): -- try: -- lib = CDLL(libclang) -- # We want at least 5.0. The API we test below is enough for that. -- # Just accessing it should throw if not found. -- fun = lib.clang_getAddressSpace -- return True -- except: -- die( -- dedent( -- """\ -- The libclang located at {} is too old (need at least 5.0). -- -- Please make sure to update it or point to a newer libclang using -- --with-libclang-path. -- """.format( -- libclang -- ) -- ) -- ) -- return False -- -- --set_config("MOZ_LIBCLANG_PATH", bindgen_config_paths.libclang_path) --set_config("MOZ_CLANG_PATH", bindgen_config_paths.clang_path) -- -- --@depends( -- target, -- target_is_unix, -- cxx_compiler, -- bindgen_cflags_android, -- bindgen_config_paths.clang_flags, -- all_clang_arm_flags, --) --def basic_bindgen_cflags( -- target, is_unix, compiler_info, android_cflags, clang_flags, all_arm_flags --): -- args = [ -- "-x", -- "c++", -- "-fno-sized-deallocation", -- "-fno-aligned-new", -- "-DTRACING=1", -- "-DIMPL_LIBXUL", -- "-DMOZILLA_INTERNAL_API", -- "-DRUST_BINDGEN", -- ] -- -- if is_unix: -- args += ["-DOS_POSIX=1"] -- -- if target.os == "Android": -- args += android_cflags -- -- args += { -- "Android": ["-DOS_ANDROID=1"], -- "DragonFly": ["-DOS_BSD=1", "-DOS_DRAGONFLY=1"], -- "FreeBSD": ["-DOS_BSD=1", "-DOS_FREEBSD=1"], -- "GNU": ["-DOS_LINUX=1"], -- "NetBSD": ["-DOS_BSD=1", "-DOS_NETBSD=1"], -- "OpenBSD": ["-DOS_BSD=1", "-DOS_OPENBSD=1"], -- "OSX": ["-DOS_MACOSX=1"], -- "SunOS": ["-DOS_SOLARIS=1"], -- "WINNT": [ -- "-DOS_WIN=1", -- "-DWIN32=1", -- ], -- }.get(target.os, []) -- -- if compiler_info.type == "clang-cl": -- args += [ -- # To enable the builtin __builtin_offsetof so that CRT wouldn't -- # use reinterpret_cast in offsetof() which is not allowed inside -- # static_assert(). -- "-D_CRT_USE_BUILTIN_OFFSETOF", -- # Enable hidden attribute (which is not supported by MSVC and -- # thus not enabled by default with a MSVC-compatibile build) -- # to exclude hidden symbols from the generated file. -- "-DHAVE_VISIBILITY_HIDDEN_ATTRIBUTE=1", -- ] -- -- return args + (clang_flags or []) + (all_arm_flags or []) -- -- --option( -- env="BINDGEN_CFLAGS", -- nargs=1, -- help="Options bindgen should pass to the C/C++ parser", --) -- -- --@depends(basic_bindgen_cflags, "BINDGEN_CFLAGS") --@checking("bindgen cflags", lambda s: s if s else "no") --def bindgen_cflags(base_flags, extra_flags): -- flags = base_flags -- if extra_flags and len(extra_flags): -- flags += extra_flags[0].split() -- return " ".join(flags) -- -- --set_config("BINDGEN_SYSTEM_FLAGS", bindgen_cflags) diff --git a/mozjs-sys/etc/patches/0018-Don-t-include-rust.configure-anymore.patch b/mozjs-sys/etc/patches/0018-Don-t-include-rust.configure-anymore.patch index 9c5373d33b5..73f7e61436a 100644 --- a/mozjs-sys/etc/patches/0018-Don-t-include-rust.configure-anymore.patch +++ b/mozjs-sys/etc/patches/0018-Don-t-include-rust.configure-anymore.patch @@ -1,12 +1,14 @@ diff --git a/js/moz.configure b/js/moz.configure -index e65adafb73..d9590f1f8b 100644 +index 060830948..af30e7622 100644 --- a/js/moz.configure +++ b/js/moz.configure -@@ -54,7 +54,6 @@ set_config("JS_ENABLE_SMOOSH", enable_smoosh) +@@ -59,8 +59,6 @@ set_config("JS_ENABLE_SMOOSH", enable_smoosh) set_define("JS_ENABLE_SMOOSH", enable_smoosh) include("../build/moz.configure/nspr.configure", when="--enable-compile-environment") -include("../build/moz.configure/rust.configure", when="--enable-compile-environment") - include("../build/moz.configure/bindgen.configure", when="--enable-compile-environment") +-include("../build/moz.configure/bindgen.configure", when="--enable-compile-environment") set_config("JS_STANDALONE", js_standalone) + set_define("JS_STANDALONE", js_standalone) + \ No newline at end of file diff --git a/mozjs-sys/etc/patches/0019-Unused-Windows-code.patch b/mozjs-sys/etc/patches/0019-Unused-Windows-code.patch deleted file mode 100644 index 3449d9629c8..00000000000 --- a/mozjs-sys/etc/patches/0019-Unused-Windows-code.patch +++ /dev/null @@ -1,20 +0,0 @@ -diff --git a/mozglue/misc/moz.build b/mozglue/misc/moz.build -index 2e6669c2b..8a1ff7bc2 100644 ---- a/mozglue/misc/moz.build -+++ b/mozglue/misc/moz.build -@@ -33,7 +33,6 @@ EXPORTS.mozilla.glue += [ - if CONFIG["OS_ARCH"] == "WINNT": - EXPORTS.mozilla += [ - "GetKnownFolderPath.h", -- "PreXULSkeletonUI.h", - "StackWalk_windows.h", - "StackWalkThread.h", - "TimeStamp_windows.h", -@@ -106,7 +105,6 @@ if CONFIG["OS_ARCH"] == "WINNT": - SOURCES += [ - "/ipc/mscom/COMWrappers.cpp", - "/ipc/mscom/ProcessRuntime.cpp", -- "PreXULSkeletonUI.cpp", - ] - - elif CONFIG["HAVE_CLOCK_MONOTONIC"]: diff --git a/mozjs-sys/etc/patches/0023-no-winheap.patch b/mozjs-sys/etc/patches/0023-no-winheap.patch deleted file mode 100644 index 8614e9cf32b..00000000000 --- a/mozjs-sys/etc/patches/0023-no-winheap.patch +++ /dev/null @@ -1,19 +0,0 @@ -diff --git a/memory/mozalloc/moz.build b/memory/mozalloc/moz.build -index c5cd78460..bf7319981 100644 ---- a/memory/mozalloc/moz.build -+++ b/memory/mozalloc/moz.build -@@ -21,14 +21,6 @@ if CONFIG["WRAP_STL_INCLUDES"]: - "msvc_raise_wrappers.cpp", - ] - --if CONFIG["OS_TARGET"] == "WINNT": -- # Don't build winheap.cpp when mozglue is a static library. -- if CONFIG["MOZ_MEMORY"] or not CONFIG["JS_STANDALONE"]: -- # Keep this file separate to avoid #include'ing windows.h everywhere. -- SOURCES += [ -- "winheap.cpp", -- ] -- - UNIFIED_SOURCES += [ - "mozalloc.cpp", - "mozalloc_abort.cpp", diff --git a/mozjs-sys/etc/patches/0024-bindgen-fixes.patch b/mozjs-sys/etc/patches/0024-bindgen-fixes.patch index 8af5d38dafe..e008a864e1e 100644 --- a/mozjs-sys/etc/patches/0024-bindgen-fixes.patch +++ b/mozjs-sys/etc/patches/0024-bindgen-fixes.patch @@ -1,27 +1,28 @@ diff --git a/js/public/CompileOptions.h b/js/public/CompileOptions.h -index 9745ffbd9..c7c7e2203 100644 +index 01a84b239..d3a6a650d 100644 --- a/js/public/CompileOptions.h +++ b/js/public/CompileOptions.h -@@ -81,7 +81,7 @@ class JS_PUBLIC_API DecodeOptions; +@@ -221,7 +221,7 @@ class JS_PUBLIC_API PrefableCompileOptions { * Use this in code that needs to propagate compile options from one * compilation unit to another. */ -class JS_PUBLIC_API TransitiveCompileOptions { -+class JS_PUBLIC_API __attribute__ ((__packed__)) TransitiveCompileOptions { - friend class JS_PUBLIC_API DecodeOptions; - ++class JS_PUBLIC_API __attribute__((__packed__)) TransitiveCompileOptions { + friend class JS_PUBLIC_API ReadOnlyDecodeOptions; + protected: -@@ -227,7 +227,7 @@ class JS_PUBLIC_API TransitiveCompileOptions { +@@ -455,7 +455,8 @@ class JS_PUBLIC_API TransitiveCompileOptions { * is protected anyway); instead, create instances only of the derived classes: * CompileOptions and OwningCompileOptions. */ -class JS_PUBLIC_API ReadOnlyCompileOptions : public TransitiveCompileOptions { -+class JS_PUBLIC_API __attribute__ ((__packed__)) ReadOnlyCompileOptions : public TransitiveCompileOptions { ++class JS_PUBLIC_API __attribute__((__packed__)) ReadOnlyCompileOptions ++ : public TransitiveCompileOptions { public: // POD options. - unsigned lineno = 1; + diff --git a/js/public/Proxy.h b/js/public/Proxy.h -index 93239d588..920b55f57 100644 +index 45ba4a137..af1f8881d 100644 --- a/js/public/Proxy.h +++ b/js/public/Proxy.h @@ -156,7 +156,7 @@ class JS_PUBLIC_API Wrapper; @@ -34,24 +35,50 @@ index 93239d588..920b55f57 100644 * Sometimes it's desirable to designate groups of proxy handlers as * "similar". For this, we use the notion of a "family": A consumer-provided diff --git a/js/public/Wrapper.h b/js/public/Wrapper.h -index 807e9e4d6..140763fcc 100644 +index 197d76f11..0e39b6d2d 100644 --- a/js/public/Wrapper.h +++ b/js/public/Wrapper.h @@ -41,7 +41,7 @@ class MOZ_STACK_CLASS WrapperOptions : public ProxyOptions { - + // Base class for proxy handlers that want to forward all operations to an // object stored in the proxy's private slot. -class JS_PUBLIC_API ForwardingProxyHandler : public BaseProxyHandler { +class JS_PUBLIC_API __attribute__ ((__packed__)) ForwardingProxyHandler : public BaseProxyHandler { public: using BaseProxyHandler::BaseProxyHandler; - -@@ -132,7 +132,7 @@ class JS_PUBLIC_API ForwardingProxyHandler : public BaseProxyHandler { + +@@ -130,7 +130,7 @@ class JS_PUBLIC_API ForwardingProxyHandler : public BaseProxyHandler { * to add an override in CrossCompartmentWrapper. If you don't, you risk * compartment mismatches. See bug 945826 comment 0. */ -class JS_PUBLIC_API Wrapper : public ForwardingProxyHandler { +class JS_PUBLIC_API __attribute__ ((__packed__)) Wrapper : public ForwardingProxyHandler { unsigned mFlags; - + public: +diff --git a/js/public/CharacterEncoding.h b/js/public/CharacterEncoding.h +index 9d1df4664..86646e13d 100644 +--- a/js/public/CharacterEncoding.h ++++ b/js/public/CharacterEncoding.h +@@ -134,7 +134,7 @@ class UTF8CharsZ : public mozilla::RangedPtr { + * to others. This differs from UTF8CharsZ in that the chars are + * const and it disallows assignment. + */ +-class JS_PUBLIC_API ConstUTF8CharsZ { ++class JS_PUBLIC_API __attribute__((__packed__)) ConstUTF8CharsZ { + const char* data_; + + public: +diff --git a/js/public/ColumnNumber.h b/js/public/ColumnNumber.h +index 9fd007f4b..adfce9dd2 100644 +--- a/js/public/ColumnNumber.h ++++ b/js/public/ColumnNumber.h +@@ -304,7 +304,7 @@ struct LimitedColumnNumberOneOrigin : public detail::MaybeLimitedColumnNumber< + }; + + // Column number in 1-origin. +-struct ColumnNumberOneOrigin : public detail::MaybeLimitedColumnNumber<0> { ++struct __attribute__((__packed__)) ColumnNumberOneOrigin : public detail::MaybeLimitedColumnNumber<0> { + private: + using Base = detail::MaybeLimitedColumnNumber<0>; + diff --git a/mozjs-sys/etc/patches/0026-js-streams.patch b/mozjs-sys/etc/patches/0026-js-streams.patch index 918b9248e2e..e7032f53d71 100644 --- a/mozjs-sys/etc/patches/0026-js-streams.patch +++ b/mozjs-sys/etc/patches/0026-js-streams.patch @@ -1,11 +1,12 @@ diff --git a/js/moz.configure b/js/moz.configure -index 682a52de6..cf6b34dfe 100644 +index cccb64899..af30e7622 100644 --- a/js/moz.configure +++ b/js/moz.configure -@@ -172,6 +172,24 @@ def enable_decorators(value): +@@ -171,6 +171,25 @@ def enable_decorators(value): set_config("ENABLE_DECORATORS", enable_decorators) set_define("ENABLE_DECORATORS", enable_decorators) ++ +# Enable JS Streams +# =================================================== +option( @@ -24,32 +25,33 @@ index 682a52de6..cf6b34dfe 100644 +set_config("MOZ_JS_STREAMS", enable_js_streams) +set_define("MOZ_JS_STREAMS", enable_js_streams) + - # JIT support - # ======================================================= - @depends(target, "--enable-record-tuple") + # Enable explicit resource management + # =================================================== + option( diff --git a/js/public/ProtoKey.h b/js/public/ProtoKey.h -index 398e1f03f..a24bf2579 100644 +index fe8a191f0..d119308ce 100644 --- a/js/public/ProtoKey.h +++ b/js/public/ProtoKey.h -@@ -111,6 +111,16 @@ - REAL(AsyncFunction, CLASP(AsyncFunction)) \ - REAL(GeneratorFunction, CLASP(GeneratorFunction)) \ - REAL(AsyncGeneratorFunction, CLASP(AsyncGeneratorFunction)) \ -+ IF_JS_STREAMS(REAL(ReadableStream, &js::ReadableStream::class_)) \ -+ IF_JS_STREAMS(REAL(ReadableStreamDefaultReader, \ -+ &js::ReadableStreamDefaultReader::class_)) \ -+ IF_JS_STREAMS(REAL(ReadableStreamDefaultController, \ -+ &js::ReadableStreamDefaultController::class_)) \ -+ IF_JS_STREAMS(REAL(ReadableByteStreamController, \ -+ &js::ReadableByteStreamController::class_)) \ -+ IF_JS_STREAMS( \ -+ REAL(ByteLengthQueuingStrategy, &js::ByteLengthQueuingStrategy::class_)) \ -+ IF_JS_STREAMS(REAL(CountQueuingStrategy, &js::CountQueuingStrategy::class_)) \ - REAL(WebAssembly, OCLASP(WasmNamespace)) \ - REAL(WasmModule, OCLASP(WasmModule)) \ - REAL(WasmInstance, OCLASP(WasmInstance)) \ +@@ -134,6 +134,17 @@ + REAL(AsyncFunction, CLASP(AsyncFunction)) \ + REAL(GeneratorFunction, CLASP(GeneratorFunction)) \ + REAL(AsyncGeneratorFunction, CLASP(AsyncGeneratorFunction)) \ ++ IF_JS_STREAMS(REAL(ReadableStream, &js::ReadableStream::class_)) \ ++ IF_JS_STREAMS(REAL(ReadableStreamDefaultReader, \ ++ &js::ReadableStreamDefaultReader::class_)) \ ++ IF_JS_STREAMS(REAL(ReadableStreamDefaultController, \ ++ &js::ReadableStreamDefaultController::class_)) \ ++ IF_JS_STREAMS(REAL(ReadableByteStreamController, \ ++ &js::ReadableByteStreamController::class_)) \ ++ IF_JS_STREAMS(REAL(ByteLengthQueuingStrategy, \ ++ &js::ByteLengthQueuingStrategy::class_)) \ ++ IF_JS_STREAMS(REAL(CountQueuingStrategy, \ ++ &js::CountQueuingStrategy::class_)) \ + REAL(WebAssembly, OCLASP(WasmNamespace)) \ + REAL(WasmModule, OCLASP(WasmModule)) \ + REAL(WasmInstance, OCLASP(WasmInstance)) \ diff --git a/js/public/RealmOptions.h b/js/public/RealmOptions.h -index 2f5cef8d6..a08737a7f 100644 +index b3ab20915..f199a1ee3 100644 --- a/js/public/RealmOptions.h +++ b/js/public/RealmOptions.h @@ -172,6 +172,19 @@ class JS_PUBLIC_API RealmCreationOptions { @@ -69,17 +71,17 @@ index 2f5cef8d6..a08737a7f 100644 + return *this; + } + - WeakRefSpecifier getWeakRefsEnabled() const { return weakRefs_; } - RealmCreationOptions& setWeakRefsEnabled(WeakRefSpecifier spec) { - weakRefs_ = spec; -@@ -279,6 +292,7 @@ class JS_PUBLIC_API RealmCreationOptions { + bool getToSourceEnabled() const { return toSource_; } + RealmCreationOptions& setToSourceEnabled(bool flag) { + toSource_ = flag; +@@ -237,6 +250,7 @@ class JS_PUBLIC_API RealmCreationOptions { bool sharedMemoryAndAtomics_ = false; bool defineSharedArrayBufferConstructor_ = true; bool coopAndCoep_ = false; + bool streams_ = false; bool toSource_ = false; - bool propertyErrorMessageFix_ = false; - bool iteratorHelpers_ = false; + + bool secureContext_ = false; diff --git a/js/public/Stream.h b/js/public/Stream.h new file mode 100644 index 000000000..1b889d248 @@ -578,7 +580,7 @@ index 000000000..1b889d248 + +#endif // js_Stream_h diff --git a/js/public/TypeDecls.h b/js/public/TypeDecls.h -index 1686b1917..779cee75f 100644 +index d82405b17..adaf40e93 100644 --- a/js/public/TypeDecls.h +++ b/js/public/TypeDecls.h @@ -141,6 +141,13 @@ using jsid = JS::PropertyKey; @@ -1417,10 +1419,10 @@ index 000000000..7ecb95862 + +#endif /* builtin_Stream_h */ diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp -index d9034c886..73ef62ded 100644 +index 4e10adcbf..ef8f88598 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp -@@ -4110,6 +4110,12 @@ static bool RejectPromise(JSContext* cx, unsigned argc, Value* vp) { +@@ -4542,6 +4542,12 @@ static bool RejectPromise(JSContext* cx, unsigned argc, Value* vp) { return result; } @@ -1433,7 +1435,7 @@ index d9034c886..73ef62ded 100644 static unsigned finalizeCount = 0; static void finalize_counter_finalize(JS::GCContext* gcx, JSObject* obj) { -@@ -8857,6 +8863,10 @@ JS_FN_HELP("rejectPromise", RejectPromise, 2, 0, +@@ -9648,6 +9654,10 @@ JS_FN_HELP("rejectPromise", RejectPromise, 2, 0, "rejectPromise(promise, reason)", " Reject a Promise by calling the JSAPI function JS::RejectPromise."), @@ -1613,7 +1615,7 @@ index 000000000..3334471fb +#endif // builtin_streams_MiscellaneousOperations_inl_h diff --git a/js/src/builtin/streams/MiscellaneousOperations.cpp b/js/src/builtin/streams/MiscellaneousOperations.cpp new file mode 100644 -index 000000000..268960624 +index 000000000..be9beb5bb --- /dev/null +++ b/js/src/builtin/streams/MiscellaneousOperations.cpp @@ -0,0 +1,193 @@ @@ -2122,7 +2124,7 @@ index 000000000..fa73cdc7c +#endif // builtin_streams_QueueWithSizes_inl_h diff --git a/js/src/builtin/streams/QueueWithSizes.cpp b/js/src/builtin/streams/QueueWithSizes.cpp new file mode 100644 -index 000000000..db99d3a8a +index 000000000..baa8290cd --- /dev/null +++ b/js/src/builtin/streams/QueueWithSizes.cpp @@ -0,0 +1,171 @@ @@ -7798,13 +7800,13 @@ index 000000000..beb42ff8e + +#endif // builtin_streams_TeeState_h diff --git a/js/src/jsapi-tests/moz.build b/js/src/jsapi-tests/moz.build -index 41ee13969..1b9e8d7f3 100644 +index e24b9bf43..d31caa78b 100644 --- a/js/src/jsapi-tests/moz.build +++ b/js/src/jsapi-tests/moz.build -@@ -168,6 +168,11 @@ if not CONFIG["JS_CODEGEN_NONE"]: +@@ -173,6 +173,11 @@ if not CONFIG["JS_CODEGEN_NONE"]: "testsJit.cpp", ] - + +if CONFIG["MOZ_JS_STREAMS"]: + UNIFIED_SOURCES += [ + "testReadableStream.cpp", @@ -9015,10 +9017,10 @@ index 000000000..edae08265 + StreamTestFixture, + testReadableStream_ReadableStreamDefaultReaderRead_CrossCompartment) diff --git a/js/src/moz.build b/js/src/moz.build -index e2e313d7c..12cd2d053 100755 +index 539d98f6c..184cdfe10 100755 --- a/js/src/moz.build +++ b/js/src/moz.build -@@ -198,6 +198,7 @@ EXPORTS.js += [ +@@ -201,6 +201,7 @@ EXPORTS.js += [ "../public/SourceText.h", "../public/StableStringChars.h", "../public/Stack.h", @@ -9027,26 +9029,10 @@ index e2e313d7c..12cd2d053 100755 "../public/String.h", "../public/StructuredClone.h", diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp -index 6c64dde26..8c350a048 100644 +index 9b849a9b7..907374858 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp -@@ -618,6 +618,7 @@ bool shell::enableTestWasmAwaitTier2 = false; - bool shell::enableSourcePragmas = true; - bool shell::enableAsyncStacks = false; - bool shell::enableAsyncStackCaptureDebuggeeOnly = false; -+bool shell::enableStreams = false; - bool shell::enableWeakRefs = false; - bool shell::enableToSource = false; - bool shell::enablePropertyErrorMessageFix = false; -@@ -3884,6 +3885,7 @@ static void SetStandardRealmOptions(JS::RealmOptions& options) { - options.creationOptions() - .setSharedMemoryAndAtomicsEnabled(enableSharedMemory) - .setCoopAndCoepEnabled(false) -+ .setStreamsEnabled(enableStreams) - .setWeakRefsEnabled(enableWeakRefs - ? JS::WeakRefSpecifier::EnabledWithCleanupSome - : JS::WeakRefSpecifier::Disabled) -@@ -11366,6 +11368,9 @@ bool InitOptionParser(OptionParser& op) { +@@ -12147,6 +12147,9 @@ bool InitOptionParser(OptionParser& op) { !op.addBoolOption('\0', "less-debug-code", "Emit less machine code for " "checking assertions under DEBUG.") || @@ -9056,19 +9042,11 @@ index 6c64dde26..8c350a048 100644 !op.addBoolOption('\0', "disable-weak-refs", "Disable weak references") || !op.addBoolOption('\0', "disable-tosource", "Disable toSource/uneval") || !op.addBoolOption('\0', "disable-property-error-message-fix", -@@ -11888,6 +11893,7 @@ bool SetContextOptions(JSContext* cx, const OptionParser& op) { - enableAsyncStacks = !op.getBoolOption("no-async-stacks"); - enableAsyncStackCaptureDebuggeeOnly = - op.getBoolOption("async-stacks-capture-debuggee-only"); -+ enableStreams = !op.getBoolOption("no-streams"); - enableWeakRefs = !op.getBoolOption("disable-weak-refs"); - enableToSource = !op.getBoolOption("disable-tosource"); - enablePropertyErrorMessageFix = diff --git a/js/src/shell/jsshell.h b/js/src/shell/jsshell.h -index e6566a6da..385f46600 100644 +index 67536a8f6..d3dead828 100644 --- a/js/src/shell/jsshell.h +++ b/js/src/shell/jsshell.h -@@ -122,6 +122,11 @@ extern bool enableTestWasmAwaitTier2; +@@ -117,6 +117,11 @@ extern bool enableTestWasmAwaitTier2; extern bool enableSourcePragmas; extern bool enableAsyncStacks; extern bool enableAsyncStackCaptureDebuggeeOnly; @@ -9081,10 +9059,10 @@ index e6566a6da..385f46600 100644 extern bool enableToSource; extern bool enablePropertyErrorMessageFix; diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp -index 6924cce0a..3ad2a75a9 100644 +index cd50c9ddc..a42601331 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp -@@ -25,6 +25,11 @@ +@@ -26,6 +26,11 @@ #include "builtin/FinalizationRegistryObject.h" #include "builtin/MapObject.h" #include "builtin/ShadowRealm.h" @@ -9094,12 +9072,12 @@ index 6924cce0a..3ad2a75a9 100644 +#include "builtin/streams/ReadableStreamController.h" // js::Readable{StreamDefault,ByteStream}Controller +#include "builtin/streams/ReadableStreamReader.h" // js::ReadableStreamDefaultReader #include "builtin/Symbol.h" - #include "builtin/WeakMapObject.h" - #include "builtin/WeakRefObject.h" -@@ -176,6 +181,15 @@ bool GlobalObject::skipDeselectedConstructor(JSContext* cx, JSProtoKey key) { + #ifdef JS_HAS_TEMPORAL_API + # include "builtin/temporal/Calendar.h" +@@ -211,6 +216,15 @@ bool GlobalObject::skipDeselectedConstructor(JSContext* cx, JSProtoKey key) { + case JSProto_PluralRules: case JSProto_RelativeTimeFormat: return false; - #endif +#ifdef MOZ_JS_STREAMS + case JSProto_ReadableStream: + case JSProto_ReadableStreamDefaultReader: @@ -9110,5 +9088,5 @@ index 6924cce0a..3ad2a75a9 100644 + return !cx->realm()->creationOptions().getStreamsEnabled(); +#endif - // Return true if the given constructor has been disabled at run-time. - case JSProto_Atomics: + case JSProto_Segmenter: + # if defined(MOZ_ICU4X) diff --git a/mozjs-sys/etc/patches/0030-autoconf-add-ohos.patch b/mozjs-sys/etc/patches/0030-autoconf-add-ohos.patch index 72afb43d6d9..1e1528e0006 100644 --- a/mozjs-sys/etc/patches/0030-autoconf-add-ohos.patch +++ b/mozjs-sys/etc/patches/0030-autoconf-add-ohos.patch @@ -1,21 +1,22 @@ diff --git a/build/autoconf/config.sub b/build/autoconf/config.sub ---- a/build/autoconf/config.sub (revision 20f7934762a6a1d4751353c8d024a0185ba85547) -+++ b/build/autoconf/config.sub (revision 1d3dc1d7b53439ce8f799526ddbbe0f833b8ef0f) -@@ -1754,7 +1754,7 @@ +index 2c6a07ab3..273b0b934 100755 +--- a/build/autoconf/config.sub ++++ b/build/autoconf/config.sub +@@ -1768,7 +1768,7 @@ case $os in | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \ | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr* \ -- | fiwix* ) -+ | fiwix* | ohos* ) +- | fiwix* | mlibc* | cos* | mbr* | ironclad* ) ++ | fiwix* | mlibc* | cos* | mbr* | ironclad* | ohos* ) ;; # This one is extra strict with allowed versions sco3.2v2 | sco3.2v[4-9]* | sco5v6*) -@@ -1772,7 +1772,7 @@ - # (given a valid OS), if there is a kernel. - case $kernel-$os in - linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* \ -- | linux-musl* | linux-relibc* | linux-uclibc* ) -+ | linux-musl* | linux-relibc* | linux-uclibc* | linux-ohos* ) +@@ -1829,7 +1829,7 @@ esac + case $kernel-$os-$obj in + linux-gnu*- | linux-android*- | linux-dietlibc*- | linux-llvm*- \ + | linux-mlibc*- | linux-musl*- | linux-newlib*- \ +- | linux-relibc*- | linux-uclibc*- ) ++ | linux-relibc*- | linux-uclibc*- | linux-ohos* ) ;; - uclinux-uclibc* ) + uclinux-uclibc*- ) ;; diff --git a/mozjs-sys/etc/patches/0031-no-rust-unicode-bidi.patch b/mozjs-sys/etc/patches/0031-no-rust-unicode-bidi.patch new file mode 100644 index 00000000000..8d9043c99f2 --- /dev/null +++ b/mozjs-sys/etc/patches/0031-no-rust-unicode-bidi.patch @@ -0,0 +1,25 @@ +diff --git a/intl/components/src/Bidi.h b/intl/components/src/Bidi.h +index 8db74173e..9f771048b 100644 +--- a/intl/components/src/Bidi.h ++++ b/intl/components/src/Bidi.h +@@ -10,7 +10,7 @@ + // Use the Rust unicode-bidi crate to back the Bidi component. + // (Define to 0 to use the legacy ICU4C implementation instead, + // until that code is removed altogether.) +-#define USE_RUST_UNICODE_BIDI 1 ++#define USE_RUST_UNICODE_BIDI 0 + + #if USE_RUST_UNICODE_BIDI + # include "mozilla/intl/unicode_bidi_ffi_generated.h" +diff --git a/js/src/moz.build b/js/src/moz.build +index 90dca3e7f..539d98f6c 100755 +--- a/js/src/moz.build ++++ b/js/src/moz.build +@@ -643,7 +643,6 @@ if CONFIG["JS_HAS_CTYPES"]: + if CONFIG["JS_HAS_INTL_API"]: + if CONFIG["JS_STANDALONE"]: + DIRS += [ +- "../../intl/bidi", + "../../intl/components", + ] + diff --git a/mozjs-sys/etc/patches/D194086.patch b/mozjs-sys/etc/patches/D194086.patch deleted file mode 100644 index 3d564a63242..00000000000 --- a/mozjs-sys/etc/patches/D194086.patch +++ /dev/null @@ -1,34 +0,0 @@ -diff --git a/moz.configure b/moz.configure -index 3a9461200..9cf45c632 100755 ---- a/moz.configure -+++ b/moz.configure -@@ -697,12 +697,17 @@ check_prog( - - - @template --def plain_llvm_or_prefixed(name): -- @depends(llvm_tool(f"llvm-{name}"), toolchain_prefix) -+def plain_llvm_or_prefixed(name, llvm_name=None): -+ # look for a tool, using the following alternatives, in that order: -+ # 1. llvm-${llvm_name}, or llvm-${name} if ${llvm_name} is not provided -+ # 2. ${toolchain_prefix}${name} -+ # 3. ${name} -+ -+ @depends(llvm_tool("llvm-{}".format(llvm_name or name)), toolchain_prefix) - def plain_llvm_or_prefixed(llvm_tool, toolchain_prefix): - commands = [llvm_tool[0], name] - for prefix in toolchain_prefix or (): -- commands.insert(1, f"{prefix}{name}") -+ commands.insert(1, "{}{}".format(prefix, name)) - return tuple(commands) - - return plain_llvm_or_prefixed -@@ -717,7 +722,7 @@ check_prog( - - check_prog( - "INSTALL_NAME_TOOL", -- plain_llvm_or_prefixed("install-name-tool"), -+ plain_llvm_or_prefixed("install_name_tool", llvm_name="install-name-tool"), - when=compile_environment & target_is_darwin & js_standalone, - paths=clang_search_path, - ) diff --git a/mozjs-sys/makefile.cargo b/mozjs-sys/makefile.cargo index b4fa81134f8..3f9da03900e 100644 --- a/mozjs-sys/makefile.cargo +++ b/mozjs-sys/makefile.cargo @@ -77,7 +77,6 @@ ifneq ($(HOST),$(TARGET)) # gold or bfd linker on Android to support their 'elfhack'functionality in # non-developer builds, but NDK r25c ships with only lld. CONFIGURE_FLAGS += \ - --without-system-zlib \ --with-android-ndk=$(ANDROID_NDK_ROOT) \ --with-android-version=$(ANDROID_API_LEVEL) \ --enable-linker=lld \ diff --git a/mozjs-sys/mozjs/build/autoconf/config.sub b/mozjs-sys/mozjs/build/autoconf/config.sub index 2c6a07ab3c3..91422c1ee3d 100755 --- a/mozjs-sys/mozjs/build/autoconf/config.sub +++ b/mozjs-sys/mozjs/build/autoconf/config.sub @@ -1768,7 +1768,7 @@ case $os in | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \ | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr* \ - | fiwix* | mlibc* | cos* | mbr* | ironclad* ) + | fiwix* | mlibc* | cos* | mbr* | ironclad* | ohos* ) ;; # This one is extra strict with allowed versions sco3.2v2 | sco3.2v[4-9]* | sco5v6*) @@ -1829,7 +1829,7 @@ esac case $kernel-$os-$obj in linux-gnu*- | linux-android*- | linux-dietlibc*- | linux-llvm*- \ | linux-mlibc*- | linux-musl*- | linux-newlib*- \ - | linux-relibc*- | linux-uclibc*- ) + | linux-relibc*- | linux-uclibc*- | linux-ohos* ) ;; uclinux-uclibc*- ) ;; diff --git a/mozjs-sys/mozjs/build/moz.configure/android-ndk.configure b/mozjs-sys/mozjs/build/moz.configure/android-ndk.configure index 3d2115e9207..b63a523088a 100644 --- a/mozjs-sys/mozjs/build/moz.configure/android-ndk.configure +++ b/mozjs-sys/mozjs/build/moz.configure/android-ndk.configure @@ -22,6 +22,10 @@ option( help="location where the Android NDK can be found{|}", ) +option("--with-android-ndk-version", nargs=1, help="android NDK version") + +option("--with-android-platform", nargs=1, help="location of the Android platform") + option("--with-android-toolchain", nargs=1, help="location of the Android toolchain") option( @@ -87,17 +91,20 @@ def ndk(value): set_config("ANDROID_NDK", ndk) -@depends(ndk) +@depends("--with-android-ndk-version", ndk) @checking("for android ndk version") @imports(_from="__builtin__", _import="open") @imports(_from="mozboot.android", _import="NDK_VERSION") @imports(_from="mozboot.android", _import="get_ndk_version") @imports(_from="mozboot.android", _import="GetNdkVersionError") -def ndk_version(ndk): +def ndk_version(value, ndk): if not ndk: # Building 'js/src' for non-Android. return + if value: + return value[0] + try: major, minor, human = get_ndk_version(ndk) except GetNdkVersionError as e: @@ -150,14 +157,16 @@ def android_toolchain(host, ndk, toolchain): return toolchain die("You have to specify --with-android-toolchain=" "/path/to/ndk/toolchain.") - -@depends(target, android_toolchain) +@depends("--with-android-platform", target, android_toolchain) @checking("for android sysroot directory") @imports(_from="os.path", _import="isdir") -def android_sysroot(target, android_toolchain): +def android_sysroot(value, target, android_toolchain): if target.os != "Android": return + if value: + return value[0] + search_dirs = [ os.path.join(android_toolchain, "sysroot"), ] @@ -241,7 +250,7 @@ option( @imports(_from="os.path", _import="isfile") def stlport_libs(value, ndk): if value and len(value): - return value.split() + return value[0].split() if not ndk: return diff --git a/mozjs-sys/mozjs/build/moz.configure/toolchain.configure b/mozjs-sys/mozjs/build/moz.configure/toolchain.configure index 264027e57de..8a97d952dd1 100755 --- a/mozjs-sys/mozjs/build/moz.configure/toolchain.configure +++ b/mozjs-sys/mozjs/build/moz.configure/toolchain.configure @@ -1875,10 +1875,9 @@ def select_linker_tmpl(host_or_target): die("Unsupported linker " + linker) # Check the kind of linker - version_check = ["-Wl,--version"] cmd_base = c_compiler.wrapper + [c_compiler.compiler] + c_compiler.flags - def try_linker(linker): + def try_linker(linker, version_check="-Wl,--version"): # Generate the compiler flag if linker == "ld64": linker_flag = ["-fuse-ld=ld"] @@ -1886,7 +1885,7 @@ def select_linker_tmpl(host_or_target): linker_flag = ["-fuse-ld=" + linker] else: linker_flag = [] - cmd = cmd_base + linker_flag + version_check + cmd = cmd_base + linker_flag + [version_check] if toolchain_flags: cmd += toolchain_flags @@ -1902,11 +1901,14 @@ def select_linker_tmpl(host_or_target): # ensure consistent output. env["LC_ALL"] = "C" retcode, stdout, stderr = get_cmd_output(*cmd, env=env) - if retcode == 1 and "Logging ld64 options" in stderr: + if retcode == 1 and ("Logging ld64 options" in stderr or "PROGRAM:ld PROJECT:dyld" in stderr): kind = "ld64" elif retcode != 0: - return None + if version_check == "-Wl,--version": + return try_linker(linker, "-Wl,-v") + else: + return None elif "mold" in stdout: kind = "mold" diff --git a/mozjs-sys/mozjs/config/recurse.mk b/mozjs-sys/mozjs/config/recurse.mk index 7956578ac0e..7ff2ec20949 100644 --- a/mozjs-sys/mozjs/config/recurse.mk +++ b/mozjs-sys/mozjs/config/recurse.mk @@ -201,24 +201,6 @@ endif # Those need to depend on config/export for system wrappers. $(addprefix build/unix/stdc++compat/,target host) build/clang-plugin/host: config/export -# Rust targets, and export targets that run cbindgen need -# $topobjdir/.cargo/config.toml to be preprocessed first. Ideally, we'd only set it -# as a dependency of the rust targets, but unfortunately, that pushes Make to -# execute them much later than we'd like them to be when the file doesn't exist -# prior to Make running. So we also set it as a dependency of pre-export, which -# ensures it exists before recursing the rust targets and the export targets -# that run cbindgen, tricking Make into keeping them early. -# When $topobjdir/.cargo/config exists from an old build, we also remove it because -# cargo will prefer to use it rather than config.toml. -CARGO_CONFIG_DEPS = $(DEPTH)/.cargo/config.toml -ifneq (,$(wildcard $(DEPTH)/.cargo/config)) -CARGO_CONFIG_DEPS += $(MDDEPDIR)/cargo-config-cleanup.stub -endif -$(rust_targets): $(CARGO_CONFIG_DEPS) -ifndef TEST_MOZBUILD -recurse_pre-export: $(CARGO_CONFIG_DEPS) -endif - $(MDDEPDIR)/cargo-config-cleanup.stub: rm $(DEPTH)/.cargo/config touch $@ diff --git a/mozjs-sys/mozjs/intl/bidi/rust/unicode-bidi-ffi/Cargo.toml b/mozjs-sys/mozjs/intl/bidi/rust/unicode-bidi-ffi/Cargo.toml new file mode 100644 index 00000000000..b357fdd7253 --- /dev/null +++ b/mozjs-sys/mozjs/intl/bidi/rust/unicode-bidi-ffi/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "unicode-bidi-ffi" +version = "0.1.0" +license = "MPL-2.0" +authors = ["Jonathan Kew "] +edition = "2021" + +[dependencies] +unicode-bidi = { version = "0.3.15", features = ["smallvec"] } +icu_properties = { version = "1.4.0", features = ["bidi"] } diff --git a/mozjs-sys/mozjs/intl/components/src/Bidi.h b/mozjs-sys/mozjs/intl/components/src/Bidi.h index 8db74173ed4..9f771048b42 100644 --- a/mozjs-sys/mozjs/intl/components/src/Bidi.h +++ b/mozjs-sys/mozjs/intl/components/src/Bidi.h @@ -10,7 +10,7 @@ // Use the Rust unicode-bidi crate to back the Bidi component. // (Define to 0 to use the legacy ICU4C implementation instead, // until that code is removed altogether.) -#define USE_RUST_UNICODE_BIDI 1 +#define USE_RUST_UNICODE_BIDI 0 #if USE_RUST_UNICODE_BIDI # include "mozilla/intl/unicode_bidi_ffi_generated.h" diff --git a/mozjs-sys/mozjs/intl/icu_capi/Cargo.toml b/mozjs-sys/mozjs/intl/icu_capi/Cargo.toml new file mode 100644 index 00000000000..fc782311bc2 --- /dev/null +++ b/mozjs-sys/mozjs/intl/icu_capi/Cargo.toml @@ -0,0 +1,271 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.67" +name = "icu_capi" +version = "1.4.0" +authors = ["The ICU4X Project Developers"] +include = [ + "js/**/*", + "c/**/*", + "cpp/**/*", + "src/**/*", + "tests/**/*", + "Cargo.toml", + "LICENSE", + "README.md", +] +description = "C interface to ICU4X" +homepage = "https://icu4x.unicode.org" +readme = "README.md" +categories = ["internationalization"] +license-file = "LICENSE" +repository = "https://github.com/unicode-org/icu4x" + +[package.metadata.cargo-all-features] +denylist = [ + "bench", + "cpp_default", + "wasm_default", + "provider_test", +] +max_combination_size = 2 + +[package.metadata.docs.rs] +all-features = true + +[dependencies.diplomat] +version = "0.7" + +[dependencies.diplomat-runtime] +version = "0.7" + +[dependencies.fixed_decimal] +version = "0.5.5" +features = ["ryu"] +optional = true +default-features = false + +[dependencies.icu_calendar] +version = "~1.4.0" +optional = true +default-features = false + +[dependencies.icu_casemap] +version = "~1.4.0" +optional = true +default-features = false + +[dependencies.icu_collator] +version = "~1.4.0" +optional = true +default-features = false + +[dependencies.icu_collections] +version = "~1.4.0" +optional = true +default-features = false + +[dependencies.icu_datetime] +version = "~1.4.0" +optional = true +default-features = false + +[dependencies.icu_decimal] +version = "~1.4.0" +optional = true +default-features = false + +[dependencies.icu_displaynames] +version = "0.11.2" +optional = true +default-features = false + +[dependencies.icu_list] +version = "~1.4.0" +optional = true +default-features = false + +[dependencies.icu_locid] +version = "~1.4.0" +default-features = false + +[dependencies.icu_locid_transform] +version = "~1.4.0" +optional = true +default-features = false + +[dependencies.icu_normalizer] +version = "~1.4.0" +optional = true +default-features = false + +[dependencies.icu_plurals] +version = "~1.4.0" +optional = true +default-features = false + +[dependencies.icu_properties] +version = "~1.4.0" +features = ["bidi"] +optional = true +default-features = false + +[dependencies.icu_provider] +version = "~1.4.0" +default-features = false + +[dependencies.icu_provider_adapters] +version = "~1.4.0" +default-features = false + +[dependencies.icu_provider_blob] +version = "~1.4.0" +optional = true +default-features = false + +[dependencies.icu_segmenter] +version = "~1.4.0" +features = ["auto"] +optional = true +default-features = false + +[dependencies.icu_timezone] +version = "~1.4.0" +optional = true +default-features = false + +[dependencies.log] +version = "0.4" +optional = true + +[dependencies.serde] +version = "1.0" +optional = true +default-features = false + +[dependencies.tinystr] +version = "0.7.4" +default-features = false + +[dependencies.unicode-bidi] +version = "0.3.13" +optional = true +default-features = false + +[dependencies.writeable] +version = "0.5.4" +default-features = false + +[features] +any_provider = [] +buffer_provider = [ + "dep:icu_provider_blob", + "dep:serde", + "icu_calendar?/serde", + "icu_casemap?/serde", + "icu_collator?/serde", + "icu_datetime?/serde", + "icu_decimal?/serde", + "icu_displaynames?/serde", + "icu_list?/serde", + "icu_locid_transform?/serde", + "icu_locid/serde", + "icu_normalizer?/serde", + "icu_plurals?/serde", + "icu_properties?/serde", + "icu_provider/serde", + "icu_provider_adapters/serde", + "icu_segmenter?/serde", + "icu_timezone?/serde", +] +compiled_data = [ + "icu_segmenter?/compiled_data", +] +cpp_default = ["logging"] +default = [ + "compiled_data", + "default_components", + "logging", + "simple_logger", +] +default_components = [ + "icu_calendar", + "icu_casemap", + "icu_collator", + "icu_datetime", + "icu_decimal", + "icu_list", + "icu_locid_transform", + "icu_normalizer", + "icu_plurals", + "icu_properties", + "icu_segmenter", + "icu_timezone", +] +icu_calendar = ["dep:icu_calendar"] +icu_casemap = ["dep:icu_casemap"] +icu_collator = ["dep:icu_collator"] +icu_datetime = [ + "dep:icu_datetime", + "dep:icu_calendar", + "dep:icu_timezone", + "dep:icu_decimal", + "dep:icu_plurals", +] +icu_decimal = [ + "dep:icu_decimal", + "dep:fixed_decimal", +] +icu_displaynames = ["dep:icu_displaynames"] +icu_list = ["dep:icu_list"] +icu_locid_transform = ["dep:icu_locid_transform"] +icu_normalizer = ["dep:icu_normalizer"] +icu_plurals = [ + "dep:icu_plurals", + "dep:fixed_decimal", +] +icu_properties = [ + "dep:icu_properties", + "dep:icu_collections", + "dep:unicode-bidi", +] +icu_segmenter = ["dep:icu_segmenter"] +icu_timezone = [ + "dep:icu_timezone", + "dep:icu_calendar", +] +logging = [ + "icu_provider/logging", + "dep:log", + "diplomat-runtime/log", +] +provider_fs = [ + "dep:icu_provider_fs", + "buffer_provider", +] +provider_test = ["compiled_data"] +simple_logger = [ + "dep:simple_logger", + "logging", +] +wasm_default = ["logging"] + +[target."cfg(not(any(target_arch = \"wasm32\", target_os = \"none\")))".dependencies.icu_provider_fs] +version = "~1.4.0" +optional = true +default-features = false + +[target."cfg(not(target_arch = \"wasm32\"))".dependencies.simple_logger] +version = "4.1.0" +optional = true diff --git a/mozjs-sys/mozjs/intl/icu_segmenter_data/Cargo.toml b/mozjs-sys/mozjs/intl/icu_segmenter_data/Cargo.toml new file mode 100644 index 00000000000..d54633f8f98 --- /dev/null +++ b/mozjs-sys/mozjs/intl/icu_segmenter_data/Cargo.toml @@ -0,0 +1,26 @@ +# This crate is replacement crates to use baked data provider in ICU4X's +# icu_capi crate. +# +# To generate baked data, run /intl/update-icu4x.sh + +[package] +edition = "2021" +rust-version = "1.67" +name = "icu_segmenter_data" +version = "1.4.0" +authors = ["The ICU4X Project Developers"] +include = [ + "data/**/*", + "src/**/*", + "examples/**/*", + "benches/**/*", + "tests/**/*", + "Cargo.toml", + "LICENSE", + "README.md", +] +description = "Data for the icu_segmenter crate" +homepage = "https://icu4x.unicode.org" +categories = ["internationalization"] +license = "MPL-2.0" +repository = "https://github.com/unicode-org/icu4x" diff --git a/mozjs-sys/mozjs/js/moz.configure b/mozjs-sys/mozjs/js/moz.configure index 593b93aceee..af30e7622ff 100644 --- a/mozjs-sys/mozjs/js/moz.configure +++ b/mozjs-sys/mozjs/js/moz.configure @@ -59,8 +59,6 @@ set_config("JS_ENABLE_SMOOSH", enable_smoosh) set_define("JS_ENABLE_SMOOSH", enable_smoosh) include("../build/moz.configure/nspr.configure", when="--enable-compile-environment") -include("../build/moz.configure/rust.configure", when="--enable-compile-environment") -include("../build/moz.configure/bindgen.configure", when="--enable-compile-environment") set_config("JS_STANDALONE", js_standalone) set_define("JS_STANDALONE", js_standalone) @@ -173,6 +171,25 @@ def enable_decorators(value): set_config("ENABLE_DECORATORS", enable_decorators) set_define("ENABLE_DECORATORS", enable_decorators) + +# Enable JS Streams +# =================================================== +option( + "--enable-js-streams", + default=False, + help="Enable non-default JS Streams implementation", +) + + +@depends("--enable-js-streams") +def enable_js_streams(value): + if value: + return True + + +set_config("MOZ_JS_STREAMS", enable_js_streams) +set_define("MOZ_JS_STREAMS", enable_js_streams) + # Enable explicit resource management # =================================================== option( diff --git a/mozjs-sys/mozjs/js/public/CharacterEncoding.h b/mozjs-sys/mozjs/js/public/CharacterEncoding.h index 9d1df4664b4..86646e13d2e 100644 --- a/mozjs-sys/mozjs/js/public/CharacterEncoding.h +++ b/mozjs-sys/mozjs/js/public/CharacterEncoding.h @@ -134,7 +134,7 @@ class UTF8CharsZ : public mozilla::RangedPtr { * to others. This differs from UTF8CharsZ in that the chars are * const and it disallows assignment. */ -class JS_PUBLIC_API ConstUTF8CharsZ { +class JS_PUBLIC_API __attribute__((__packed__)) ConstUTF8CharsZ { const char* data_; public: diff --git a/mozjs-sys/mozjs/js/public/ColumnNumber.h b/mozjs-sys/mozjs/js/public/ColumnNumber.h index 9fd007f4bb4..adfce9dd2bc 100644 --- a/mozjs-sys/mozjs/js/public/ColumnNumber.h +++ b/mozjs-sys/mozjs/js/public/ColumnNumber.h @@ -304,7 +304,7 @@ struct LimitedColumnNumberOneOrigin : public detail::MaybeLimitedColumnNumber< }; // Column number in 1-origin. -struct ColumnNumberOneOrigin : public detail::MaybeLimitedColumnNumber<0> { +struct __attribute__((__packed__)) ColumnNumberOneOrigin : public detail::MaybeLimitedColumnNumber<0> { private: using Base = detail::MaybeLimitedColumnNumber<0>; diff --git a/mozjs-sys/mozjs/js/public/CompileOptions.h b/mozjs-sys/mozjs/js/public/CompileOptions.h index 01a84b23986..d3a6a650d27 100644 --- a/mozjs-sys/mozjs/js/public/CompileOptions.h +++ b/mozjs-sys/mozjs/js/public/CompileOptions.h @@ -221,7 +221,7 @@ class JS_PUBLIC_API PrefableCompileOptions { * Use this in code that needs to propagate compile options from one * compilation unit to another. */ -class JS_PUBLIC_API TransitiveCompileOptions { +class JS_PUBLIC_API __attribute__((__packed__)) TransitiveCompileOptions { friend class JS_PUBLIC_API ReadOnlyDecodeOptions; protected: @@ -455,7 +455,8 @@ class JS_PUBLIC_API TransitiveCompileOptions { * is protected anyway); instead, create instances only of the derived classes: * CompileOptions and OwningCompileOptions. */ -class JS_PUBLIC_API ReadOnlyCompileOptions : public TransitiveCompileOptions { +class JS_PUBLIC_API __attribute__((__packed__)) ReadOnlyCompileOptions + : public TransitiveCompileOptions { public: // POD options. diff --git a/mozjs-sys/mozjs/js/public/ProtoKey.h b/mozjs-sys/mozjs/js/public/ProtoKey.h index fe8a191f06e..d119308cebf 100644 --- a/mozjs-sys/mozjs/js/public/ProtoKey.h +++ b/mozjs-sys/mozjs/js/public/ProtoKey.h @@ -134,6 +134,17 @@ REAL(AsyncFunction, CLASP(AsyncFunction)) \ REAL(GeneratorFunction, CLASP(GeneratorFunction)) \ REAL(AsyncGeneratorFunction, CLASP(AsyncGeneratorFunction)) \ + IF_JS_STREAMS(REAL(ReadableStream, &js::ReadableStream::class_)) \ + IF_JS_STREAMS(REAL(ReadableStreamDefaultReader, \ + &js::ReadableStreamDefaultReader::class_)) \ + IF_JS_STREAMS(REAL(ReadableStreamDefaultController, \ + &js::ReadableStreamDefaultController::class_)) \ + IF_JS_STREAMS(REAL(ReadableByteStreamController, \ + &js::ReadableByteStreamController::class_)) \ + IF_JS_STREAMS(REAL(ByteLengthQueuingStrategy, \ + &js::ByteLengthQueuingStrategy::class_)) \ + IF_JS_STREAMS(REAL(CountQueuingStrategy, \ + &js::CountQueuingStrategy::class_)) \ REAL(WebAssembly, OCLASP(WasmNamespace)) \ REAL(WasmModule, OCLASP(WasmModule)) \ REAL(WasmInstance, OCLASP(WasmInstance)) \ diff --git a/mozjs-sys/mozjs/js/public/Proxy.h b/mozjs-sys/mozjs/js/public/Proxy.h index 45ba4a1376f..af1f8881d4a 100644 --- a/mozjs-sys/mozjs/js/public/Proxy.h +++ b/mozjs-sys/mozjs/js/public/Proxy.h @@ -156,7 +156,7 @@ class JS_PUBLIC_API Wrapper; * explicit override for the method in SecurityWrapper. See bug 945826 comment * 0. */ -class JS_PUBLIC_API BaseProxyHandler { +class JS_PUBLIC_API __attribute__ ((__packed__)) BaseProxyHandler { /* * Sometimes it's desirable to designate groups of proxy handlers as * "similar". For this, we use the notion of a "family": A consumer-provided diff --git a/mozjs-sys/mozjs/js/public/RealmOptions.h b/mozjs-sys/mozjs/js/public/RealmOptions.h index b3ab20915a0..f199a1ee312 100644 --- a/mozjs-sys/mozjs/js/public/RealmOptions.h +++ b/mozjs-sys/mozjs/js/public/RealmOptions.h @@ -172,6 +172,19 @@ class JS_PUBLIC_API RealmCreationOptions { bool getCoopAndCoepEnabled() const; RealmCreationOptions& setCoopAndCoepEnabled(bool flag); + bool getStreamsEnabled() const { return streams_; } + RealmCreationOptions& setStreamsEnabled(bool flag) { +#ifdef MOZ_JS_STREAMS +# ifdef MOZ_DOM_STREAMS +# error "JS and DOM streams shouldn't be simultaneously configured" +# endif + streams_ = flag; +#else + MOZ_ASSERT(!streams_); +#endif + return *this; + } + bool getToSourceEnabled() const { return toSource_; } RealmCreationOptions& setToSourceEnabled(bool flag) { toSource_ = flag; @@ -237,6 +250,7 @@ class JS_PUBLIC_API RealmCreationOptions { bool sharedMemoryAndAtomics_ = false; bool defineSharedArrayBufferConstructor_ = true; bool coopAndCoep_ = false; + bool streams_ = false; bool toSource_ = false; bool secureContext_ = false; diff --git a/mozjs-sys/mozjs/js/public/Stream.h b/mozjs-sys/mozjs/js/public/Stream.h new file mode 100644 index 00000000000..1b889d24884 --- /dev/null +++ b/mozjs-sys/mozjs/js/public/Stream.h @@ -0,0 +1,491 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * JSAPI functions and callbacks related to WHATWG Stream objects. + * + * Much of the API here mirrors the standard algorithms and standard JS methods + * of the objects defined in the Streams standard. One difference is that the + * functionality of the JS controller object is exposed to C++ as functions + * taking ReadableStream instances instead, for convenience. + */ + +#ifndef js_Stream_h +#define js_Stream_h + +#include + +#include "jstypes.h" + +#include "js/RootingAPI.h" +#include "js/TypeDecls.h" + +struct JSClass; + +namespace JS { + +/** + * Abstract base class for external underlying sources. + * + * The term "underlying source" is defined in the Streams spec: + * https://streams.spec.whatwg.org/#underlying-source + * + * A `ReadableStreamUnderlyingSource` is an underlying source that is + * implemented in C++ rather than JS. It can be passed to + * `JS::NewReadableExternalSourceStreamObject` to create a custom, + * embedding-defined ReadableStream. + * + * There are several API difference between this class and the standard API for + * underlying sources implemented in JS: + * + * - JS underlying sources can be either byte sources or non-byte sources. + * External underlying source are always byte sources. + * + * - The C++ API does not bother with controller objects. Instead of using + * controller methods, the underlying source directly calls API functions + * like JS::ReadableStream{UpdateDataAvailableFromSource,Close,Error}. + * + * - External readable streams are optimized to allow the embedding to + * interact with them with a minimum of overhead: chunks aren't enqueued as + * individual typed arrays; instead, the embedding only updates the amount + * of data available using + * JS::ReadableStreamUpdateDataAvailableFromSource. When JS requests data + * from a reader, writeIntoReadRequestBuffer is invoked, asking the + * embedding to write data directly into the buffer we're about to hand to + * JS. + * + * - The C++ API provides extra callbacks onClosed() and onErrored(). + * + * - This class has a `finalize()` method, because C++ cares about lifetimes. + * + * Additionally, ReadableStreamGetExternalUnderlyingSource can be used to get + * the pointer to the underlying source. This locks the stream until it is + * released again using JS::ReadableStreamReleaseExternalUnderlyingSource. + * + * Embeddings can use this to optimize away the JS `ReadableStream` overhead + * when an embedding-defined C++ stream is passed to an embedding-defined C++ + * consumer. For example, consider a ServiceWorker piping a `fetch` Response + * body to a TextDecoder. Instead of copying chunks of data into JS typed array + * buffers and creating a Promise per chunk, only to immediately resolve the + * Promises and read the data out again, the embedding can directly feed the + * incoming data to the TextDecoder. + * + * Compartment safety: All methods (except `finalize`) receive `cx` and + * `stream` arguments. SpiderMonkey enters the realm of the stream object + * before invoking these methods, so `stream` is never a wrapper. Other + * arguments may be wrappers. + */ +class JS_PUBLIC_API ReadableStreamUnderlyingSource { + public: + virtual ~ReadableStreamUnderlyingSource() = default; + + /** + * Invoked whenever a reader desires more data from this source. + * + * The given `desiredSize` is the absolute size, not a delta from the + * previous desired size. + */ + virtual void requestData(JSContext* cx, HandleObject stream, + size_t desiredSize) = 0; + + /** + * Invoked to cause the embedding to fill the given `buffer` with data from + * this underlying source. + * + * This is called only after the embedding has updated the amount of data + * available using JS::ReadableStreamUpdateDataAvailableFromSource. If at + * least one read request is pending when + * JS::ReadableStreamUpdateDataAvailableFromSource is called, this method + * is invoked immediately from under the call to + * JS::ReadableStreamUpdateDataAvailableFromSource. If not, it is invoked + * if and when a new read request is made. + * + */ + virtual void writeIntoReadRequestBuffer(JSContext* cx, HandleObject stream, + JS::Handle aChunk, + size_t length, + size_t* bytesWritten) = 0; + + /** + * Invoked in reaction to the ReadableStream being canceled. This is + * equivalent to the `cancel` method on non-external underlying sources + * provided to the ReadableStream constructor in JavaScript. + * + * The underlying source may free up some resources in this method, but + * `*this` must not be destroyed until `finalize()` is called. + * + * The given `reason` is the JS::Value that was passed as an argument to + * ReadableStream#cancel(). + * + * The returned JS::Value will be used to resolve the Promise returned by + * ReadableStream#cancel(). + */ + virtual Value cancel(JSContext* cx, HandleObject stream, + HandleValue reason) = 0; + + /** + * Invoked when the associated ReadableStream becomes closed. + * + * The underlying source may free up some resources in this method, but + * `*this` must not be destroyed until `finalize()` is called. + */ + virtual void onClosed(JSContext* cx, HandleObject stream) = 0; + + /** + * Invoked when the associated ReadableStream becomes errored. + * + * The underlying source may free up some resources in this method, but + * `*this` must not be destroyed until `finalize()` is called. + */ + virtual void onErrored(JSContext* cx, HandleObject stream, + HandleValue reason) = 0; + + /** + * Invoked when the associated ReadableStream object is finalized. The + * stream object is not passed as an argument, as it might not be in a + * valid state anymore. + * + * Note: Finalization can happen on a background thread, so the embedding + * must be prepared for `finalize()` to be invoked from any thread. + */ + virtual void finalize() = 0; +}; + +/** + * Returns a new instance of the ReadableStream builtin class in the current + * compartment, configured as a default stream. + * If a |proto| is passed, that gets set as the instance's [[Prototype]] + * instead of the original value of |ReadableStream.prototype|. + */ +extern JS_PUBLIC_API JSObject* NewReadableDefaultStreamObject( + JSContext* cx, HandleObject underlyingSource = nullptr, + HandleFunction size = nullptr, double highWaterMark = 1, + HandleObject proto = nullptr); + +/** + * Returns a new instance of the ReadableStream builtin class in the current + * compartment. + * + * The instance is a byte stream backed by an embedding-provided underlying + * source, using the virtual methods of `underlyingSource` as callbacks. The + * embedding must ensure that `*underlyingSource` lives as long as the new + * stream object. The JS engine will call the finalize() method when the stream + * object is destroyed. + * + * `nsISupportsObject_alreadyAddreffed` is an optional pointer that can be used + * to make the new stream participate in Gecko's cycle collection. Here are the + * rules for using this parameter properly: + * + * - `*underlyingSource` must not be a cycle-collected object. (It would lead + * to memory leaks as the cycle collector would not be able to collect + * cycles containing that object.) + * + * - `*underlyingSource` must not contain nsCOMPtrs that point to cycle- + * collected objects. (Same reason.) + * + * - `*underlyingSource` may contain a pointer to a single cycle-collected + * object. + * + * - The pointer may be stored in `*underlyingSource` as a raw pointer. + * + * - The pointer to the nsISupports interface of the same object must be + * passed as the `nsISupportsObject_alreadyAddreffed` parameter to this + * function. (This is how the cycle collector knows about it, so omitting + * this would again cause leaks.) + * + * If `proto` is non-null, it is used as the instance's [[Prototype]] instead + * of the original value of `ReadableStream.prototype`. + */ +extern JS_PUBLIC_API JSObject* NewReadableExternalSourceStreamObject( + JSContext* cx, ReadableStreamUnderlyingSource* underlyingSource, + void* nsISupportsObject_alreadyAddreffed = nullptr, + HandleObject proto = nullptr); + +/** + * Returns the embedding-provided underlying source of the given |stream|. + * + * Can be used to optimize operations if both the underlying source and the + * intended sink are embedding-provided. In that case it might be + * preferrable to pipe data directly from source to sink without interacting + * with the stream at all. + * + * Locks the stream until ReadableStreamReleaseExternalUnderlyingSource is + * called. + * + * Throws an exception if the stream is locked, i.e. if a reader has been + * acquired for the stream, or if ReadableStreamGetExternalUnderlyingSource + * has been used previously without releasing the external source again. + * + * Throws an exception if the stream isn't readable, i.e if it is errored or + * closed. This is different from ReadableStreamGetReader because we don't + * have a Promise to resolve/reject, which a reader provides. + * + * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper + * for one. + * + * Asserts that the stream has an embedding-provided underlying source. + */ +extern JS_PUBLIC_API bool ReadableStreamGetExternalUnderlyingSource( + JSContext* cx, HandleObject stream, + ReadableStreamUnderlyingSource** source); + +/** + * Releases the embedding-provided underlying source of the given |stream|, + * returning the stream into an unlocked state. + * + * Asserts that the stream was locked through + * ReadableStreamGetExternalUnderlyingSource. + * + * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper + * for one. + * + * Asserts that the stream has an embedding-provided underlying source. + */ +extern JS_PUBLIC_API bool ReadableStreamReleaseExternalUnderlyingSource( + JSContext* cx, HandleObject stream); + +/** + * Update the amount of data available at the underlying source of the given + * |stream|. + * + * Can only be used for streams with an embedding-provided underlying source. + * The JS engine will use the given value to satisfy read requests for the + * stream by invoking the writeIntoReadRequestBuffer method. + * + * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper + * for one. + */ +extern JS_PUBLIC_API bool ReadableStreamUpdateDataAvailableFromSource( + JSContext* cx, HandleObject stream, uint32_t availableData); + +/** + * Break the cycle between this object and the + * nsISupportsObject_alreadyAddreffed passed in + * NewReadableExternalSourceStreamObject(). + */ +extern JS_PUBLIC_API void ReadableStreamReleaseCCObject(JSObject* stream); + +/** + * Returns true if the given object is a ReadableStream object or an + * unwrappable wrapper for one, false otherwise. + */ +extern JS_PUBLIC_API bool IsReadableStream(JSObject* obj); + +/** + * Returns true if the given object is a ReadableStreamDefaultReader or + * ReadableStreamBYOBReader object or an unwrappable wrapper for one, false + * otherwise. + */ +extern JS_PUBLIC_API bool IsReadableStreamReader(JSObject* obj); + +/** + * Returns true if the given object is a ReadableStreamDefaultReader object + * or an unwrappable wrapper for one, false otherwise. + */ +extern JS_PUBLIC_API bool IsReadableStreamDefaultReader(JSObject* obj); + +enum class ReadableStreamMode { Default, Byte, ExternalSource }; + +/** + * Returns the stream's ReadableStreamMode. If the mode is |Byte| or + * |ExternalSource|, it's possible to acquire a BYOB reader for more optimized + * operations. + * + * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper + * for one. + */ +extern JS_PUBLIC_API bool ReadableStreamGetMode(JSContext* cx, + HandleObject stream, + ReadableStreamMode* mode); + +enum class ReadableStreamReaderMode { Default, Byob }; + +/** + * Returns true if the given ReadableStream is readable, false if not. + * + * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper + * for one. + */ +extern JS_PUBLIC_API bool ReadableStreamIsReadable(JSContext* cx, + HandleObject stream, + bool* result); + +/** + * Returns true if the given ReadableStream is locked, false if not. + * + * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper + * for one. + */ +extern JS_PUBLIC_API bool ReadableStreamIsLocked(JSContext* cx, + HandleObject stream, + bool* result); + +/** + * Returns true if the given ReadableStream is disturbed, false if not. + * + * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper + * for one. + */ +extern JS_PUBLIC_API bool ReadableStreamIsDisturbed(JSContext* cx, + HandleObject stream, + bool* result); + +/** + * Cancels the given ReadableStream with the given reason and returns a + * Promise resolved according to the result. + * + * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper + * for one. + */ +extern JS_PUBLIC_API JSObject* ReadableStreamCancel(JSContext* cx, + HandleObject stream, + HandleValue reason); + +/** + * Creates a reader of the type specified by the mode option and locks the + * stream to the new reader. + * + * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper + * for one. The returned object will always be created in the + * current cx compartment. + */ +extern JS_PUBLIC_API JSObject* ReadableStreamGetReader( + JSContext* cx, HandleObject stream, ReadableStreamReaderMode mode); + +/** + * Tees the given ReadableStream and stores the two resulting streams in + * outparams. Returns false if the operation fails, e.g. because the stream is + * locked. + * + * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper + * for one. + */ +extern JS_PUBLIC_API bool ReadableStreamTee(JSContext* cx, HandleObject stream, + MutableHandleObject branch1Stream, + MutableHandleObject branch2Stream); + +/** + * Retrieves the desired combined size of additional chunks to fill the given + * ReadableStream's queue. Stores the result in |value| and sets |hasValue| to + * true on success, returns false on failure. + * + * If the stream is errored, the call will succeed but no value will be stored + * in |value| and |hasValue| will be set to false. + * + * Note: This is semantically equivalent to the |desiredSize| getter on + * the stream controller's prototype in JS. We expose it with the stream + * itself as a target for simplicity. + * + * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper + * for one. + */ +extern JS_PUBLIC_API bool ReadableStreamGetDesiredSize(JSContext* cx, + JSObject* stream, + bool* hasValue, + double* value); + +/** + * Close the given ReadableStream. This is equivalent to `controller.close()` + * in JS. + * + * This can fail with or without an exception pending under a variety of + * circumstances. On failure, the stream may or may not be closed, and + * downstream consumers may or may not have been notified. + * + * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper + * for one. + */ +extern JS_PUBLIC_API bool ReadableStreamClose(JSContext* cx, + HandleObject stream); + +/** + * Returns true if the given ReadableStream reader is locked, false otherwise. + * + * Asserts that |reader| is a ReadableStreamDefaultReader or + * ReadableStreamBYOBReader object or an unwrappable wrapper for one. + */ +extern JS_PUBLIC_API bool ReadableStreamReaderIsClosed(JSContext* cx, + HandleObject reader, + bool* result); + +/** + * Enqueues the given chunk in the given ReadableStream. + * + * Throws a TypeError and returns false if the enqueing operation fails. + * + * Note: This is semantically equivalent to the |enqueue| method on + * the stream controller's prototype in JS. We expose it with the stream + * itself as a target for simplicity. + * + * If the ReadableStream has an underlying byte source, the given chunk must + * be a typed array or a DataView. Consider using + * ReadableByteStreamEnqueueBuffer. + * + * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper + * for one. + */ +extern JS_PUBLIC_API bool ReadableStreamEnqueue(JSContext* cx, + HandleObject stream, + HandleValue chunk); + +/** + * Errors the given ReadableStream, causing all future interactions to fail + * with the given error value. + * + * Throws a TypeError and returns false if the erroring operation fails. + * + * Note: This is semantically equivalent to the |error| method on + * the stream controller's prototype in JS. We expose it with the stream + * itself as a target for simplicity. + * + * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper + * for one. + */ +extern JS_PUBLIC_API bool ReadableStreamError(JSContext* cx, + HandleObject stream, + HandleValue error); + +/** + * C++ equivalent of `reader.cancel(reason)` + * (both and + * ). + * + * `reader` must be a stream reader created using `JS::ReadableStreamGetReader` + * or an unwrappable wrapper for one. (This function is meant to support using + * C++ to read from streams. It's not meant to allow C++ code to operate on + * readers created by scripts.) + */ +extern JS_PUBLIC_API bool ReadableStreamReaderCancel(JSContext* cx, + HandleObject reader, + HandleValue reason); + +/** + * C++ equivalent of `reader.releaseLock()` + * (both and + * ). + * + * `reader` must be a stream reader created using `JS::ReadableStreamGetReader` + * or an unwrappable wrapper for one. + */ +extern JS_PUBLIC_API bool ReadableStreamReaderReleaseLock(JSContext* cx, + HandleObject reader); + +/** + * C++ equivalent of the `reader.read()` method on default readers + * (). + * + * The result is a new Promise object, or null on OOM. + * + * `reader` must be the result of calling `JS::ReadableStreamGetReader` with + * `ReadableStreamReaderMode::Default` mode, or an unwrappable wrapper for such + * a reader. + */ +extern JS_PUBLIC_API JSObject* ReadableStreamDefaultReaderRead( + JSContext* cx, HandleObject reader); + +} // namespace JS + +#endif // js_Stream_h diff --git a/mozjs-sys/mozjs/js/public/TypeDecls.h b/mozjs-sys/mozjs/js/public/TypeDecls.h index d82405b17c0..adaf40e93ce 100644 --- a/mozjs-sys/mozjs/js/public/TypeDecls.h +++ b/mozjs-sys/mozjs/js/public/TypeDecls.h @@ -141,6 +141,13 @@ using jsid = JS::PropertyKey; # define IF_RECORD_TUPLE(x, ...) __VA_ARGS__ #endif +// Follows the same pattern as IF_RECORD_TUPLE +#ifdef MOZ_JS_STREAMS +# define IF_JS_STREAMS(x, ...) x +#else +# define IF_JS_STREAMS(x, ...) __VA_ARGS__ +#endif + // Follows the same pattern as IF_RECORD_TUPLE #ifdef ENABLE_DECORATORS # define IF_DECORATORS(x, ...) x diff --git a/mozjs-sys/mozjs/js/public/Wrapper.h b/mozjs-sys/mozjs/js/public/Wrapper.h index 197d76f11fc..0e39b6d2d14 100644 --- a/mozjs-sys/mozjs/js/public/Wrapper.h +++ b/mozjs-sys/mozjs/js/public/Wrapper.h @@ -41,7 +41,7 @@ class MOZ_STACK_CLASS WrapperOptions : public ProxyOptions { // Base class for proxy handlers that want to forward all operations to an // object stored in the proxy's private slot. -class JS_PUBLIC_API ForwardingProxyHandler : public BaseProxyHandler { +class JS_PUBLIC_API __attribute__ ((__packed__)) ForwardingProxyHandler : public BaseProxyHandler { public: using BaseProxyHandler::BaseProxyHandler; @@ -130,7 +130,7 @@ class JS_PUBLIC_API ForwardingProxyHandler : public BaseProxyHandler { * to add an override in CrossCompartmentWrapper. If you don't, you risk * compartment mismatches. See bug 945826 comment 0. */ -class JS_PUBLIC_API Wrapper : public ForwardingProxyHandler { +class JS_PUBLIC_API __attribute__ ((__packed__)) Wrapper : public ForwardingProxyHandler { unsigned mFlags; public: diff --git a/mozjs-sys/mozjs/js/src/build/moz.build b/mozjs-sys/mozjs/js/src/build/moz.build index 3af67922d41..73cb0911354 100644 --- a/mozjs-sys/mozjs/js/src/build/moz.build +++ b/mozjs-sys/mozjs/js/src/build/moz.build @@ -39,6 +39,7 @@ if CONFIG["JS_SHARED_LIBRARY"]: SYMBOLS_FILE = "!symverscript" else: Library("js") + USE_LIBS += ["mozglue"] FORCE_STATIC_LIB = True STATIC_LIBRARY_NAME = "js_static" @@ -108,7 +109,6 @@ GeneratedFile( script="/config/run_spidermonkey_checks.py", inputs=[ "!%sjs_static.%s" % (CONFIG["LIB_PREFIX"], CONFIG["LIB_SUFFIX"]), - "/config/check_spidermonkey_style.py", "/config/check_macroassembler_style.py", "/config/check_js_opcode.py", ], diff --git a/mozjs-sys/mozjs/js/src/builtin/Stream.cpp b/mozjs-sys/mozjs/js/src/builtin/Stream.cpp new file mode 100644 index 00000000000..c108b2cd0d2 --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/Stream.cpp @@ -0,0 +1,775 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "builtin/Stream.h" + +#include "js/Stream.h" + +#include // int32_t + +#include "builtin/streams/ClassSpecMacro.h" // JS_STREAMS_CLASS_SPEC +#include "builtin/streams/MiscellaneousOperations.h" // js::CreateAlgorithmFromUnderlyingMethod, js::InvokeOrNoop, js::IsMaybeWrapped, js::PromiseCall, js::PromiseRejectedWithPendingError +#include "builtin/streams/PullIntoDescriptor.h" // js::PullIntoDescriptor +#include "builtin/streams/QueueWithSizes.h" // js::{EnqueueValueWithSize,ResetQueue} +#include "builtin/streams/ReadableStream.h" // js::ReadableStream, js::SetUpExternalReadableByteStreamController +#include "builtin/streams/ReadableStreamController.h" // js::ReadableStream{,Default}Controller, js::ReadableStreamDefaultControllerPullSteps, js::ReadableStreamControllerStart{,Failed}Handler +#include "builtin/streams/ReadableStreamDefaultControllerOperations.h" // js::ReadableStreamControllerClearAlgorithms +#include "builtin/streams/ReadableStreamInternals.h" // js::ReadableStream{AddReadOrReadIntoRequest,CloseInternal,CreateReadResult,ErrorInternal,FulfillReadOrReadIntoRequest,GetNumReadRequests,HasDefaultReader} +#include "builtin/streams/ReadableStreamReader.h" // js::ReadableStream{,Default}Reader, js::CreateReadableStreamDefaultReader, js::ReadableStreamReaderGeneric{Cancel,Initialize,Release}, js::ReadableStreamDefaultReaderRead +#include "js/ArrayBuffer.h" // JS::NewArrayBuffer +#include "js/experimental/TypedData.h" // JS_GetArrayBufferViewData, JS_NewUint8Array{,WithBuffer} +#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* +#include "js/PropertySpec.h" +#include "vm/Interpreter.h" +#include "vm/JSContext.h" +#include "vm/PlainObject.h" // js::PlainObject +#include "vm/PromiseObject.h" // js::PromiseObject, js::PromiseResolvedWithUndefined +#include "vm/SelfHosting.h" + +#include "builtin/HandlerFunction-inl.h" // js::NewHandler +#include "builtin/streams/ReadableStreamReader-inl.h" // js::Unwrap{ReaderFromStream{,NoThrow},StreamFromReader} +#include "vm/Compartment-inl.h" +#include "vm/List-inl.h" // js::ListObject, js::StoreNewListInFixedSlot +#include "vm/NativeObject-inl.h" + +using namespace js; + +#if 0 // disable user-defined byte streams + +class ByteStreamChunk : public NativeObject +{ + private: + enum Slots { + Slot_Buffer = 0, + Slot_ByteOffset, + Slot_ByteLength, + SlotCount + }; + + public: + static const JSClass class_; + + ArrayBufferObject* buffer() { + return &getFixedSlot(Slot_Buffer).toObject().as(); + } + uint32_t byteOffset() { return getFixedSlot(Slot_ByteOffset).toInt32(); } + void SetByteOffset(uint32_t offset) { + setFixedSlot(Slot_ByteOffset, Int32Value(offset)); + } + uint32_t byteLength() { return getFixedSlot(Slot_ByteLength).toInt32(); } + void SetByteLength(uint32_t length) { + setFixedSlot(Slot_ByteLength, Int32Value(length)); + } + + static ByteStreamChunk* create(JSContext* cx, HandleObject buffer, uint32_t byteOffset, + uint32_t byteLength) + { + Rooted chunk(cx, NewBuiltinClassInstance(cx)); + if (!chunk) { + return nullptr; + } + + chunk->setFixedSlot(Slot_Buffer, ObjectValue(*buffer)); + chunk->setFixedSlot(Slot_ByteOffset, Int32Value(byteOffset)); + chunk->setFixedSlot(Slot_ByteLength, Int32Value(byteLength)); + return chunk; + } +}; + +const JSClass ByteStreamChunk::class_ = { + "ByteStreamChunk", + JSCLASS_HAS_RESERVED_SLOTS(SlotCount) +}; + +#endif // user-defined byte streams + +/*** 3.3. ReadableStreamAsyncIteratorPrototype ******************************/ + +// Not implemented. + +/*** 3.7. Class ReadableStreamBYOBReader ************************************/ + +// Not implemented. + +/*** 3.11. Class ReadableByteStreamController *******************************/ + +#if 0 // disable user-defined byte streams + +/** + * Streams spec, 3.10.3 + * new ReadableByteStreamController ( stream, underlyingSource, + * highWaterMark ) + * Steps 3 - 16. + * + * Note: All arguments must be same-compartment with cx. ReadableStream + * controllers are always created in the same compartment as the stream. + */ +[[nodiscard]] static ReadableByteStreamController* +CreateReadableByteStreamController(JSContext* cx, + Handle stream, + HandleValue underlyingByteSource, + HandleValue highWaterMarkVal) +{ + cx->check(stream, underlyingByteSource, highWaterMarkVal); + + Rooted controller(cx, + NewBuiltinClassInstance(cx)); + if (!controller) { + return nullptr; + } + + // Step 3: Set this.[[controlledReadableStream]] to stream. + controller->setStream(stream); + + // Step 4: Set this.[[underlyingByteSource]] to underlyingByteSource. + controller->setUnderlyingSource(underlyingByteSource); + + // Step 5: Set this.[[pullAgain]], and this.[[pulling]] to false. + controller->setFlags(0); + + // Step 6: Perform ! ReadableByteStreamControllerClearPendingPullIntos(this). + if (!ReadableByteStreamControllerClearPendingPullIntos(cx, controller)) { + return nullptr; + } + + // Step 7: Perform ! ResetQueue(this). + if (!ResetQueue(cx, controller)) { + return nullptr; + } + + // Step 8: Set this.[[started]] and this.[[closeRequested]] to false. + // These should be false by default, unchanged since step 5. + MOZ_ASSERT(controller->flags() == 0); + + // Step 9: Set this.[[strategyHWM]] to + // ? ValidateAndNormalizeHighWaterMark(highWaterMark). + double highWaterMark; + if (!ValidateAndNormalizeHighWaterMark(cx, highWaterMarkVal, &highWaterMark)) { + return nullptr; + } + controller->setStrategyHWM(highWaterMark); + + // Step 10: Let autoAllocateChunkSize be + // ? GetV(underlyingByteSource, "autoAllocateChunkSize"). + RootedValue autoAllocateChunkSize(cx); + if (!GetProperty(cx, underlyingByteSource, cx->names().autoAllocateChunkSize, + &autoAllocateChunkSize)) + { + return nullptr; + } + + // Step 11: If autoAllocateChunkSize is not undefined, + if (!autoAllocateChunkSize.isUndefined()) { + // Step a: If ! IsInteger(autoAllocateChunkSize) is false, or if + // autoAllocateChunkSize ≤ 0, throw a RangeError exception. + if (!IsInteger(autoAllocateChunkSize) || autoAllocateChunkSize.toNumber() <= 0) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNKSIZE); + return nullptr; + } + } + + // Step 12: Set this.[[autoAllocateChunkSize]] to autoAllocateChunkSize. + controller->setAutoAllocateChunkSize(autoAllocateChunkSize); + + // Step 13: Set this.[[pendingPullIntos]] to a new empty List. + if (!StoreNewListInFixedSlot(cx, controller, + ReadableByteStreamController::Slot_PendingPullIntos)) { + return nullptr; + } + + // Step 14: Let controller be this (implicit). + + // Step 15: Let startResult be + // ? InvokeOrNoop(underlyingSource, "start", « this »). + RootedValue startResult(cx); + RootedValue controllerVal(cx, ObjectValue(*controller)); + if (!InvokeOrNoop(cx, underlyingByteSource, cx->names().start, controllerVal, &startResult)) { + return nullptr; + } + + // Step 16: Let startPromise be a promise resolved with startResult: + RootedObject startPromise(cx, PromiseObject::unforgeableResolve(cx, startResult)); + if (!startPromise) { + return nullptr; + } + + RootedObject onStartFulfilled(cx, NewHandler(cx, ReadableStreamControllerStartHandler, controller)); + if (!onStartFulfilled) { + return nullptr; + } + + RootedObject onStartRejected(cx, NewHandler(cx, ControllerStartFailedHandler, controller)); + if (!onStartRejected) { + return nullptr; + } + + if (!JS::AddPromiseReactions(cx, startPromise, onStartFulfilled, onStartRejected)) { + return nullptr; + } + + return controller; +} + +#endif // user-defined byte streams + +/** + * Streams spec, 3.11.3. + * new ReadableByteStreamController ( stream, underlyingByteSource, + * highWaterMark ) + */ +bool ReadableByteStreamController::constructor(JSContext* cx, unsigned argc, + Value* vp) { + // Step 1: Throw a TypeError exception. + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_BOGUS_CONSTRUCTOR, + "ReadableByteStreamController"); + return false; +} + +// Disconnect the source from a controller without calling finalize() on it, +// unless this class is reset(). This ensures that finalize() will not be called +// on the source if setting up the controller fails. +class MOZ_RAII AutoClearUnderlyingSource { + Rooted controller_; + + public: + AutoClearUnderlyingSource(JSContext* cx, ReadableStreamController* controller) + : controller_(cx, controller) {} + + ~AutoClearUnderlyingSource() { + if (controller_) { + ReadableStreamController::clearUnderlyingSource( + controller_, /* finalizeSource */ false); + } + } + + void reset() { controller_ = nullptr; } +}; + +/** + * Version of SetUpReadableByteStreamController that's specialized for handling + * external, embedding-provided, underlying sources. + */ +[[nodiscard]] bool js::SetUpExternalReadableByteStreamController( + JSContext* cx, Handle stream, + JS::ReadableStreamUnderlyingSource* source) { + // Done elsewhere in the standard: Create the controller object. + Rooted controller( + cx, NewBuiltinClassInstance(cx)); + if (!controller) { + return false; + } + + AutoClearUnderlyingSource autoClear(cx, controller); + + // Step 1: Assert: stream.[[readableStreamController]] is undefined. + MOZ_ASSERT(!stream->hasController()); + + // Step 2: If autoAllocateChunkSize is not undefined, [...] + // (It's treated as undefined.) + + // Step 3: Set controller.[[controlledReadableByteStream]] to stream. + controller->setStream(stream); + + // Step 4: Set controller.[[pullAgain]] and controller.[[pulling]] to false. + controller->setFlags(0); + MOZ_ASSERT(!controller->pullAgain()); + MOZ_ASSERT(!controller->pulling()); + + // Step 5: Perform + // ! ReadableByteStreamControllerClearPendingPullIntos(controller). + // Omitted. This step is apparently redundant; see + // . + + // Step 6: Perform ! ResetQueue(this). + controller->setQueueTotalSize(0); + + // Step 7: Set controller.[[closeRequested]] and controller.[[started]] to + // false (implicit). + MOZ_ASSERT(!controller->closeRequested()); + MOZ_ASSERT(!controller->started()); + + // Step 8: Set controller.[[strategyHWM]] to + // ? ValidateAndNormalizeHighWaterMark(highWaterMark). + controller->setStrategyHWM(0); + + // Step 9: Set controller.[[pullAlgorithm]] to pullAlgorithm. + // Step 10: Set controller.[[cancelAlgorithm]] to cancelAlgorithm. + // (These algorithms are given by source's virtual methods.) + controller->setExternalSource(source); + + // Step 11: Set controller.[[autoAllocateChunkSize]] to + // autoAllocateChunkSize (implicit). + MOZ_ASSERT(controller->autoAllocateChunkSize().isUndefined()); + + // Step 12: Set this.[[pendingPullIntos]] to a new empty List. + if (!StoreNewListInFixedSlot( + cx, controller, + ReadableByteStreamController::Slot_PendingPullIntos)) { + return false; + } + + // Step 13: Set stream.[[readableStreamController]] to controller. + stream->setController(controller); + + // Step 14: Let startResult be the result of performing startAlgorithm. + // (For external sources, this algorithm does nothing and returns undefined.) + // Step 15: Let startPromise be a promise resolved with startResult. + Rooted startPromise(cx, PromiseResolvedWithUndefined(cx)); + if (!startPromise) { + return false; + } + + // Step 16: Upon fulfillment of startPromise, [...] + // Step 17: Upon rejection of startPromise with reason r, [...] + RootedObject onStartFulfilled( + cx, NewHandler(cx, ReadableStreamControllerStartHandler, controller)); + if (!onStartFulfilled) { + return false; + } + RootedObject onStartRejected( + cx, + NewHandler(cx, ReadableStreamControllerStartFailedHandler, controller)); + if (!onStartRejected) { + return false; + } + if (!JS::AddPromiseReactions(cx, startPromise, onStartFulfilled, + onStartRejected)) { + return false; + } + + autoClear.reset(); + return true; +} + +static const JSPropertySpec ReadableByteStreamController_properties[] = { + JS_PS_END}; + +static const JSFunctionSpec ReadableByteStreamController_methods[] = { + JS_FS_END}; + +static void ReadableByteStreamControllerFinalize(JS::GCContext* gcx, + JSObject* obj) { + ReadableByteStreamController& controller = + obj->as(); + + if (controller.getFixedSlot(ReadableStreamController::Slot_Flags) + .isUndefined()) { + return; + } + + if (!controller.hasExternalSource()) { + return; + } + + controller.externalSource()->finalize(); +} + +static const JSClassOps ReadableByteStreamControllerClassOps = { + nullptr, // addProperty + nullptr, // delProperty + nullptr, // enumerate + nullptr, // newEnumerate + nullptr, // resolve + nullptr, // mayResolve + ReadableByteStreamControllerFinalize, // finalize + nullptr, // call + nullptr, // construct + nullptr, // trace +}; + +JS_STREAMS_CLASS_SPEC(ReadableByteStreamController, 0, SlotCount, + ClassSpec::DontDefineConstructor, + JSCLASS_BACKGROUND_FINALIZE, + &ReadableByteStreamControllerClassOps); + +// Streams spec, 3.11.5.1. [[CancelSteps]] () +// Unified with 3.9.5.1 above. + +[[nodiscard]] static bool ReadableByteStreamControllerHandleQueueDrain( + JSContext* cx, Handle unwrappedController); + +/** + * Streams spec, 3.11.5.2. [[PullSteps]] ( forAuthorCode ) + */ +[[nodiscard]] static PromiseObject* ReadableByteStreamControllerPullSteps( + JSContext* cx, Handle unwrappedController) { + // Step 1: Let stream be this.[[controlledReadableByteStream]]. + Rooted unwrappedStream(cx, unwrappedController->stream()); + + // Step 2: Assert: ! ReadableStreamHasDefaultReader(stream) is true. +#ifdef DEBUG + bool result; + if (!ReadableStreamHasDefaultReader(cx, unwrappedStream, &result)) { + return nullptr; + } + MOZ_ASSERT(result); +#endif + + RootedValue val(cx); + // Step 3: If this.[[queueTotalSize]] > 0, + double queueTotalSize = unwrappedController->queueTotalSize(); + if (queueTotalSize > 0) { + // Step 3.a: Assert: ! ReadableStreamGetNumReadRequests(_stream_) is 0. + MOZ_ASSERT(ReadableStreamGetNumReadRequests(unwrappedStream) == 0); + + RootedObject view(cx); + + MOZ_RELEASE_ASSERT(unwrappedStream->mode() == + JS::ReadableStreamMode::ExternalSource); +#if 0 // disable user-defined byte streams + if (unwrappedStream->mode() == JS::ReadableStreamMode::ExternalSource) +#endif // user-defined byte streams + { + JS::ReadableStreamUnderlyingSource* source = + unwrappedController->externalSource(); + + view = JS_NewUint8Array(cx, queueTotalSize); + if (!view) { + return nullptr; + } + + size_t bytesWritten; + { + AutoRealm ar(cx, unwrappedStream); + source->writeIntoReadRequestBuffer(cx, unwrappedStream, view, + queueTotalSize, &bytesWritten); + } + + queueTotalSize = queueTotalSize - bytesWritten; + } + +#if 0 // disable user-defined byte streams + else { + // Step 3.b: Let entry be the first element of this.[[queue]]. + // Step 3.c: Remove entry from this.[[queue]], shifting all other + // elements downward (so that the second becomes the + // first, and so on). + Rooted unwrappedQueue(cx, unwrappedController->queue()); + Rooted unwrappedEntry(cx, + UnwrapAndDowncastObject( + cx, &unwrappedQueue->popFirstAs(cx))); + if (!unwrappedEntry) { + return nullptr; + } + + queueTotalSize = queueTotalSize - unwrappedEntry->byteLength(); + + // Step 3.f: Let view be ! Construct(%Uint8Array%, + // « entry.[[buffer]], + // entry.[[byteOffset]], + // entry.[[byteLength]] »). + // (reordered) + RootedObject buffer(cx, unwrappedEntry->buffer()); + if (!cx->compartment()->wrap(cx, &buffer)) { + return nullptr; + } + + uint32_t byteOffset = unwrappedEntry->byteOffset(); + view = JS_NewUint8ArrayWithBuffer(cx, buffer, byteOffset, unwrappedEntry->byteLength()); + if (!view) { + return nullptr; + } + } +#endif // user-defined byte streams + + // Step 3.d: Set this.[[queueTotalSize]] to + // this.[[queueTotalSize]] − entry.[[byteLength]]. + // (reordered) + unwrappedController->setQueueTotalSize(queueTotalSize); + + // Step 3.e: Perform ! ReadableByteStreamControllerHandleQueueDrain(this). + // (reordered) + if (!ReadableByteStreamControllerHandleQueueDrain(cx, + unwrappedController)) { + return nullptr; + } + + // Step 3.g: Return a promise resolved with + // ! ReadableStreamCreateReadResult(view, false, forAuthorCode). + val.setObject(*view); + ReadableStreamReader* unwrappedReader = + UnwrapReaderFromStream(cx, unwrappedStream); + if (!unwrappedReader) { + return nullptr; + } + Rooted readResult( + cx, ReadableStreamCreateReadResult(cx, val, false, + unwrappedReader->forAuthorCode())); + if (!readResult) { + return nullptr; + } + val.setObject(*readResult); + + return PromiseObject::unforgeableResolveWithNonPromise(cx, val); + } + + // Step 4: Let autoAllocateChunkSize be this.[[autoAllocateChunkSize]]. + val = unwrappedController->autoAllocateChunkSize(); + + // Step 5: If autoAllocateChunkSize is not undefined, + if (!val.isUndefined()) { + double autoAllocateChunkSize = val.toNumber(); + + // Step 5.a: Let buffer be + // Construct(%ArrayBuffer%, « autoAllocateChunkSize »). + JSObject* bufferObj = JS::NewArrayBuffer(cx, autoAllocateChunkSize); + + // Step 5.b: If buffer is an abrupt completion, + // return a promise rejected with buffer.[[Value]]. + if (!bufferObj) { + return PromiseRejectedWithPendingError(cx); + } + + RootedArrayBufferObject buffer(cx, &bufferObj->as()); + + // Step 5.c: Let pullIntoDescriptor be + // Record {[[buffer]]: buffer.[[Value]], + // [[byteOffset]]: 0, + // [[byteLength]]: autoAllocateChunkSize, + // [[bytesFilled]]: 0, + // [[elementSize]]: 1, + // [[ctor]]: %Uint8Array%, + // [[readerType]]: `"default"`}. + RootedObject pullIntoDescriptor( + cx, PullIntoDescriptor::create(cx, buffer, 0, autoAllocateChunkSize, 0, + 1, nullptr, ReaderType::Default)); + if (!pullIntoDescriptor) { + return PromiseRejectedWithPendingError(cx); + } + + // Step 5.d: Append pullIntoDescriptor as the last element of + // this.[[pendingPullIntos]]. + if (!AppendToListInFixedSlot( + cx, unwrappedController, + ReadableByteStreamController::Slot_PendingPullIntos, + pullIntoDescriptor)) { + return nullptr; + } + } + + // Step 6: Let promise be ! ReadableStreamAddReadRequest(stream, + // forAuthorCode). + Rooted promise( + cx, ReadableStreamAddReadOrReadIntoRequest(cx, unwrappedStream)); + if (!promise) { + return nullptr; + } + + // Step 7: Perform ! ReadableByteStreamControllerCallPullIfNeeded(this). + if (!ReadableStreamControllerCallPullIfNeeded(cx, unwrappedController)) { + return nullptr; + } + + // Step 8: Return promise. + return promise; +} + +/** + * Unified implementation of ReadableStream controllers' [[PullSteps]] internal + * methods. + * Streams spec, 3.9.5.2. [[PullSteps]] ( forAuthorCode ) + * and + * Streams spec, 3.11.5.2. [[PullSteps]] ( forAuthorCode ) + */ +[[nodiscard]] PromiseObject* js::ReadableStreamControllerPullSteps( + JSContext* cx, Handle unwrappedController) { + if (unwrappedController->is()) { + Rooted unwrappedDefaultController( + cx, &unwrappedController->as()); + return ReadableStreamDefaultControllerPullSteps(cx, + unwrappedDefaultController); + } + + Rooted unwrappedByteController( + cx, &unwrappedController->as()); + return ReadableByteStreamControllerPullSteps(cx, unwrappedByteController); +} + +/*** 3.13. Readable stream BYOB controller abstract operations **************/ + +// Streams spec, 3.13.1. IsReadableStreamBYOBRequest ( x ) +// Implemented via is() + +// Streams spec, 3.13.2. IsReadableByteStreamController ( x ) +// Implemented via is() + +// Streams spec, 3.13.3. +// ReadableByteStreamControllerCallPullIfNeeded ( controller ) +// Unified with 3.9.2 above. + +[[nodiscard]] static bool ReadableByteStreamControllerInvalidateBYOBRequest( + JSContext* cx, Handle unwrappedController); + +/** + * Streams spec, 3.13.5. + * ReadableByteStreamControllerClearPendingPullIntos ( controller ) + */ +[[nodiscard]] bool js::ReadableByteStreamControllerClearPendingPullIntos( + JSContext* cx, Handle unwrappedController) { + // Step 1: Perform + // ! ReadableByteStreamControllerInvalidateBYOBRequest(controller). + if (!ReadableByteStreamControllerInvalidateBYOBRequest(cx, + unwrappedController)) { + return false; + } + + // Step 2: Set controller.[[pendingPullIntos]] to a new empty List. + return StoreNewListInFixedSlot( + cx, unwrappedController, + ReadableByteStreamController::Slot_PendingPullIntos); +} + +/** + * Streams spec, 3.13.6. ReadableByteStreamControllerClose ( controller ) + */ +[[nodiscard]] bool js::ReadableByteStreamControllerClose( + JSContext* cx, Handle unwrappedController) { + // Step 1: Let stream be controller.[[controlledReadableByteStream]]. + Rooted unwrappedStream(cx, unwrappedController->stream()); + + // Step 2: Assert: controller.[[closeRequested]] is false. + MOZ_ASSERT(!unwrappedController->closeRequested()); + + // Step 3: Assert: stream.[[state]] is "readable". + MOZ_ASSERT(unwrappedStream->readable()); + + // Step 4: If controller.[[queueTotalSize]] > 0, + if (unwrappedController->queueTotalSize() > 0) { + // Step a: Set controller.[[closeRequested]] to true. + unwrappedController->setCloseRequested(); + + // Step b: Return. + return true; + } + + // Step 5: If controller.[[pendingPullIntos]] is not empty, + Rooted unwrappedPendingPullIntos( + cx, unwrappedController->pendingPullIntos()); + if (unwrappedPendingPullIntos->length() != 0) { + // Step a: Let firstPendingPullInto be the first element of + // controller.[[pendingPullIntos]]. + Rooted unwrappedFirstPendingPullInto( + cx, UnwrapAndDowncastObject( + cx, &unwrappedPendingPullIntos->get(0).toObject())); + if (!unwrappedFirstPendingPullInto) { + return false; + } + + // Step b: If firstPendingPullInto.[[bytesFilled]] > 0, + if (unwrappedFirstPendingPullInto->bytesFilled() > 0) { + // Step i: Let e be a new TypeError exception. + JS_ReportErrorNumberASCII( + cx, GetErrorMessage, nullptr, + JSMSG_READABLEBYTESTREAMCONTROLLER_CLOSE_PENDING_PULL); + RootedValue e(cx); + Rooted stack(cx); + if (!cx->isExceptionPending() || + !GetAndClearExceptionAndStack(cx, &e, &stack)) { + // Uncatchable error. Die immediately without erroring the + // stream. + return false; + } + + // Step ii: Perform ! ReadableByteStreamControllerError(controller, e). + if (!ReadableStreamControllerError(cx, unwrappedController, e)) { + return false; + } + + // Step iii: Throw e. + cx->setPendingException(e, stack); + return false; + } + } + + // Step 6: Perform ! ReadableByteStreamControllerClearAlgorithms(controller). + ReadableStreamControllerClearAlgorithms(unwrappedController); + + // Step 7: Perform ! ReadableStreamClose(stream). + return ReadableStreamCloseInternal(cx, unwrappedStream); +} + +// Streams spec, 3.13.11. ReadableByteStreamControllerError ( controller, e ) +// Unified with 3.10.7 above. + +// Streams spec 3.13.14. +// ReadableByteStreamControllerGetDesiredSize ( controller ) +// Unified with 3.10.8 above. + +/** + * Streams spec, 3.13.15. + * ReadableByteStreamControllerHandleQueueDrain ( controller ) + */ +[[nodiscard]] static bool ReadableByteStreamControllerHandleQueueDrain( + JSContext* cx, Handle unwrappedController) { + MOZ_ASSERT(unwrappedController->is()); + + // Step 1: Assert: controller.[[controlledReadableStream]].[[state]] + // is "readable". + Rooted unwrappedStream(cx, unwrappedController->stream()); + MOZ_ASSERT(unwrappedStream->readable()); + + // Step 2: If controller.[[queueTotalSize]] is 0 and + // controller.[[closeRequested]] is true, + if (unwrappedController->queueTotalSize() == 0 && + unwrappedController->closeRequested()) { + // Step a: Perform + // ! ReadableByteStreamControllerClearAlgorithms(controller). + ReadableStreamControllerClearAlgorithms(unwrappedController); + + // Step b: Perform + // ! ReadableStreamClose(controller.[[controlledReadableStream]]). + return ReadableStreamCloseInternal(cx, unwrappedStream); + } + + // Step 3: Otherwise, + // Step a: Perform ! ReadableByteStreamControllerCallPullIfNeeded(controller). + return ReadableStreamControllerCallPullIfNeeded(cx, unwrappedController); +} + +enum BYOBRequestSlots { + BYOBRequestSlot_Controller, + BYOBRequestSlot_View, + BYOBRequestSlotCount +}; + +/** + * Streams spec 3.13.16. + * ReadableByteStreamControllerInvalidateBYOBRequest ( controller ) + */ +[[nodiscard]] static bool ReadableByteStreamControllerInvalidateBYOBRequest( + JSContext* cx, Handle unwrappedController) { + // Step 1: If controller.[[byobRequest]] is undefined, return. + RootedValue unwrappedBYOBRequestVal(cx, unwrappedController->byobRequest()); + if (unwrappedBYOBRequestVal.isUndefined()) { + return true; + } + + Rooted unwrappedBYOBRequest( + cx, UnwrapAndDowncastValue(cx, unwrappedBYOBRequestVal)); + if (!unwrappedBYOBRequest) { + return false; + } + + // Step 2: Set controller.[[byobRequest]] + // .[[associatedReadableByteStreamController]] + // to undefined. + unwrappedBYOBRequest->setFixedSlot(BYOBRequestSlot_Controller, + UndefinedValue()); + + // Step 3: Set controller.[[byobRequest]].[[view]] to undefined. + unwrappedBYOBRequest->setFixedSlot(BYOBRequestSlot_View, UndefinedValue()); + + // Step 4: Set controller.[[byobRequest]] to undefined. + unwrappedController->clearBYOBRequest(); + + return true; +} + +// Streams spec, 3.13.25. +// ReadableByteStreamControllerShouldCallPull ( controller ) +// Unified with 3.10.3 above. diff --git a/mozjs-sys/mozjs/js/src/builtin/Stream.h b/mozjs-sys/mozjs/js/src/builtin/Stream.h new file mode 100644 index 00000000000..7ecb9586237 --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/Stream.h @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef builtin_Stream_h +#define builtin_Stream_h + +#include "jstypes.h" // JS_PUBLIC_API +#include "js/RootingAPI.h" // JS::Handle + +struct JS_PUBLIC_API JSContext; + +namespace js { + +class PromiseObject; +class ReadableByteStreamController; +class ReadableStreamController; + +[[nodiscard]] extern bool ReadableByteStreamControllerClearPendingPullIntos( + JSContext* cx, + JS::Handle unwrappedController); + +[[nodiscard]] extern bool ReadableByteStreamControllerClose( + JSContext* cx, + JS::Handle unwrappedController); + +[[nodiscard]] extern PromiseObject* ReadableStreamControllerPullSteps( + JSContext* cx, JS::Handle unwrappedController); + +} // namespace js + +#endif /* builtin_Stream_h */ diff --git a/mozjs-sys/mozjs/js/src/builtin/TestingFunctions.cpp b/mozjs-sys/mozjs/js/src/builtin/TestingFunctions.cpp index 4e10adcbf53..ef8f885986b 100644 --- a/mozjs-sys/mozjs/js/src/builtin/TestingFunctions.cpp +++ b/mozjs-sys/mozjs/js/src/builtin/TestingFunctions.cpp @@ -4542,6 +4542,12 @@ static bool RejectPromise(JSContext* cx, unsigned argc, Value* vp) { return result; } +static bool StreamsAreEnabled(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + args.rval().setBoolean(cx->realm()->creationOptions().getStreamsEnabled()); + return true; +} + static unsigned finalizeCount = 0; static void finalize_counter_finalize(JS::GCContext* gcx, JSObject* obj) { @@ -9648,6 +9654,10 @@ JS_FN_HELP("rejectPromise", RejectPromise, 2, 0, "rejectPromise(promise, reason)", " Reject a Promise by calling the JSAPI function JS::RejectPromise."), +JS_FN_HELP("streamsAreEnabled", StreamsAreEnabled, 0, 0, +"streamsAreEnabled()", +" Returns a boolean indicating whether WHATWG Streams are enabled for the current realm."), + JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver, 0, 0, "makeFinalizeObserver()", " Get a special object whose finalization increases the counter returned\n" diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/ClassSpecMacro.h b/mozjs-sys/mozjs/js/src/builtin/streams/ClassSpecMacro.h new file mode 100644 index 00000000000..17d1fda9307 --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/ClassSpecMacro.h @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* A JS_STREAMS_CLASS_SPEC macro for defining streams classes. */ + +#ifndef builtin_streams_ClassSpecMacro_h +#define builtin_streams_ClassSpecMacro_h + +#include "gc/AllocKind.h" // js::gc::AllocKind +#include "js/Class.h" // js::ClassSpec, JSClass, JSCLASS_HAS_{CACHED_PROTO,RESERVED_SLOTS}, JS_NULL_CLASS_OPS +#include "js/ProtoKey.h" // JSProto_* +#include "vm/GlobalObject.h" // js::GenericCreate{Constructor,Prototype} + +#define JS_STREAMS_CLASS_SPEC(cls, nCtorArgs, nSlots, specFlags, classFlags, \ + classOps) \ + const js::ClassSpec cls::classSpec_ = { \ + js::GenericCreateConstructor, \ + js::GenericCreatePrototype, \ + nullptr, \ + nullptr, \ + cls##_methods, \ + cls##_properties, \ + nullptr, \ + specFlags}; \ + \ + const JSClass cls::class_ = {#cls, \ + JSCLASS_HAS_RESERVED_SLOTS(nSlots) | \ + JSCLASS_HAS_CACHED_PROTO(JSProto_##cls) | \ + classFlags, \ + classOps, &cls::classSpec_}; \ + \ + const JSClass cls::protoClass_ = {#cls ".prototype", \ + JSCLASS_HAS_CACHED_PROTO(JSProto_##cls), \ + JS_NULL_CLASS_OPS, &cls::classSpec_}; + +#endif // builtin_streams_ClassSpecMacro_h diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/MiscellaneousOperations-inl.h b/mozjs-sys/mozjs/js/src/builtin/streams/MiscellaneousOperations-inl.h new file mode 100644 index 00000000000..3334471fb7d --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/MiscellaneousOperations-inl.h @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Miscellaneous operations. */ + +#ifndef builtin_streams_MiscellaneousOperations_inl_h +#define builtin_streams_MiscellaneousOperations_inl_h + +#include "builtin/streams/MiscellaneousOperations.h" + +#include "mozilla/Assertions.h" // MOZ_ASSERT + +#include "js/CallAndConstruct.h" // JS::IsCallable +#include "js/Promise.h" // JS::{Resolve,Reject}Promise +#include "js/RootingAPI.h" // JS::Rooted, JS::{,Mutable}Handle +#include "js/Value.h" // JS::UndefinedHandleValue, JS::Value +#include "vm/Compartment.h" // JS::Compartment +#include "vm/Interpreter.h" // js::Call +#include "vm/JSContext.h" // JSContext +#include "vm/JSObject.h" // JSObject +#include "vm/PromiseObject.h" // js::PromiseObject + +#include "vm/Compartment-inl.h" // JS::Compartment::wrap +#include "vm/JSContext-inl.h" // JSContext::check +#include "vm/JSObject-inl.h" // js::IsCallable + +namespace js { + +/** + * Streams spec, 6.3.5. PromiseCall ( F, V, args ) + * There must be 0-2 |args| arguments, all convertible to JS::Handle. + */ +template +[[nodiscard]] inline JSObject* PromiseCall(JSContext* cx, + JS::Handle F, + JS::Handle V, + Args&&... args) { + cx->check(F); + cx->check(V); + cx->check(args...); + + // Step 1: Assert: ! IsCallable(F) is true. + MOZ_ASSERT(IsCallable(F)); + + // Step 2: Assert: V is not undefined. + MOZ_ASSERT(!V.isUndefined()); + + // Step 3: Assert: args is a List (implicit). + // Step 4: Let returnValue be Call(F, V, args). + JS::Rooted rval(cx); + if (!Call(cx, F, V, args..., &rval)) { + // Step 5: If returnValue is an abrupt completion, return a promise rejected + // with returnValue.[[Value]]. + return PromiseRejectedWithPendingError(cx); + } + + // Step 6: Otherwise, return a promise resolved with returnValue.[[Value]]. + return PromiseObject::unforgeableResolve(cx, rval); +} + +/** + * Resolve the unwrapped promise |unwrappedPromise| with |value|. + */ +[[nodiscard]] inline bool ResolveUnwrappedPromiseWithValue( + JSContext* cx, JSObject* unwrappedPromise, JS::Handle value) { + cx->check(value); + + JS::Rooted promise(cx, unwrappedPromise); + if (!cx->compartment()->wrap(cx, &promise)) { + return false; + } + + return JS::ResolvePromise(cx, promise, value); +} + +/** + * Resolve the unwrapped promise |unwrappedPromise| with |undefined|. + */ +[[nodiscard]] inline bool ResolveUnwrappedPromiseWithUndefined( + JSContext* cx, JSObject* unwrappedPromise) { + return ResolveUnwrappedPromiseWithValue(cx, unwrappedPromise, + JS::UndefinedHandleValue); +} + +/** + * Reject the unwrapped promise |unwrappedPromise| with |error|, overwriting + * |*unwrappedPromise| with its wrapped form. + */ +[[nodiscard]] inline bool RejectUnwrappedPromiseWithError( + JSContext* cx, JS::MutableHandle unwrappedPromise, + JS::Handle error) { + cx->check(error); + + if (!cx->compartment()->wrap(cx, unwrappedPromise)) { + return false; + } + + return JS::RejectPromise(cx, unwrappedPromise, error); +} + +/** + * Reject the unwrapped promise |unwrappedPromise| with |error|. + */ +[[nodiscard]] inline bool RejectUnwrappedPromiseWithError( + JSContext* cx, JSObject* unwrappedPromise, JS::Handle error) { + JS::Rooted promise(cx, unwrappedPromise); + return RejectUnwrappedPromiseWithError(cx, &promise, error); +} + +} // namespace js + +#endif // builtin_streams_MiscellaneousOperations_inl_h diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/MiscellaneousOperations.cpp b/mozjs-sys/mozjs/js/src/builtin/streams/MiscellaneousOperations.cpp new file mode 100644 index 00000000000..be9beb5bb49 --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/MiscellaneousOperations.cpp @@ -0,0 +1,193 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Miscellaneous operations. */ + +#include "builtin/streams/MiscellaneousOperations.h" + +#include "mozilla/Assertions.h" // MOZ_ASSERT +#include "mozilla/FloatingPoint.h" // std::isnan + +#include "js/CallAndConstruct.h" // JS::IsCallable +#include "js/Conversions.h" // JS::ToNumber +#include "js/ErrorReport.h" // JS_ReportErrorNumberASCII +#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* +#include "js/RootingAPI.h" // JS::{,Mutable}Handle, JS::Rooted +#include "vm/Interpreter.h" // js::{Call,GetAndClearException} +#include "vm/JSContext.h" // JSContext +#include "vm/ObjectOperations.h" // js::GetProperty +#include "vm/PromiseObject.h" // js::PromiseObject +#include "vm/StringType.h" // js::PropertyName + +#include "vm/JSContext-inl.h" // JSContext::check +#include "vm/JSObject-inl.h" // js::IsCallable + +using JS::Handle; +using JS::MutableHandle; +using JS::ToNumber; +using JS::Value; + +[[nodiscard]] js::PromiseObject* js::PromiseRejectedWithPendingError( + JSContext* cx) { + Rooted exn(cx); + if (!cx->isExceptionPending() || !GetAndClearException(cx, &exn)) { + // Uncatchable error. This happens when a slow script is killed or a + // worker is terminated. Propagate the uncatchable error. This will + // typically kill off the calling asynchronous process: the caller + // can't hook its continuation to the new rejected promise. + return nullptr; + } + return PromiseObject::unforgeableReject(cx, exn); +} + +/*** 6.3. Miscellaneous operations ******************************************/ + +/** + * Streams spec, 6.3.1. + * CreateAlgorithmFromUnderlyingMethod ( underlyingObject, methodName, + * algoArgCount, extraArgs ) + * + * This function only partly implements the standard algorithm. We do not + * actually create a new JSFunction completely encapsulating the new algorithm. + * Instead, this just gets the specified method and checks for errors. It's the + * caller's responsibility to make sure that later, when the algorithm is + * "performed", the appropriate steps are carried out. + */ +[[nodiscard]] bool js::CreateAlgorithmFromUnderlyingMethod( + JSContext* cx, Handle underlyingObject, + const char* methodNameForErrorMessage, Handle methodName, + MutableHandle method) { + cx->check(underlyingObject); + cx->check(methodName); + cx->check(method); + + // Step 1: Assert: underlyingObject is not undefined. + MOZ_ASSERT(!underlyingObject.isUndefined()); + + // Step 2: Assert: ! IsPropertyKey(methodName) is true (implicit). + // Step 3: Assert: algoArgCount is 0 or 1 (omitted). + // Step 4: Assert: extraArgs is a List (omitted). + + // Step 5: Let method be ? GetV(underlyingObject, methodName). + if (!GetProperty(cx, underlyingObject, methodName, method)) { + return false; + } + + // Step 6: If method is not undefined, + if (!method.isUndefined()) { + // Step a: If ! IsCallable(method) is false, throw a TypeError + // exception. + if (!IsCallable(method)) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_NOT_FUNCTION, methodNameForErrorMessage); + return false; + } + + // Step b: If algoArgCount is 0, return an algorithm that performs the + // following steps: + // Step i: Return ! PromiseCall(method, underlyingObject, + // extraArgs). + // Step c: Otherwise, return an algorithm that performs the following + // steps, taking an arg argument: + // Step i: Let fullArgs be a List consisting of arg followed by the + // elements of extraArgs in order. + // Step ii: Return ! PromiseCall(method, underlyingObject, + // fullArgs). + // (These steps are deferred to the code that performs the algorithm. + // See Perform{Write,Close}Algorithm, ReadableStreamControllerCancelSteps, + // and ReadableStreamControllerCallPullIfNeeded.) + return true; + } + + // Step 7: Return an algorithm which returns a promise resolved with + // undefined (implicit). + return true; +} + +/** + * Streams spec, 6.3.2. InvokeOrNoop ( O, P, args ) + * As it happens, all callers pass exactly one argument. + */ +[[nodiscard]] bool js::InvokeOrNoop(JSContext* cx, Handle O, + Handle P, Handle arg, + MutableHandle rval) { + cx->check(O, P, arg); + + // Step 1: Assert: O is not undefined. + MOZ_ASSERT(!O.isUndefined()); + + // Step 2: Assert: ! IsPropertyKey(P) is true (implicit). + // Step 3: Assert: args is a List (implicit). + // Step 4: Let method be ? GetV(O, P). + Rooted method(cx); + if (!GetProperty(cx, O, P, &method)) { + return false; + } + + // Step 5: If method is undefined, return. + if (method.isUndefined()) { + return true; + } + + // Step 6: Return ? Call(method, O, args). + return Call(cx, method, O, arg, rval); +} + +/** + * Streams spec, 6.3.7. ValidateAndNormalizeHighWaterMark ( highWaterMark ) + */ +[[nodiscard]] bool js::ValidateAndNormalizeHighWaterMark( + JSContext* cx, Handle highWaterMarkVal, double* highWaterMark) { + // Step 1: Set highWaterMark to ? ToNumber(highWaterMark). + if (!ToNumber(cx, highWaterMarkVal, highWaterMark)) { + return false; + } + + // Step 2: If highWaterMark is NaN or highWaterMark < 0, throw a RangeError + // exception. + if (std::isnan(*highWaterMark) || *highWaterMark < 0) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_STREAM_INVALID_HIGHWATERMARK); + return false; + } + + // Step 3: Return highWaterMark. + return true; +} + +/** + * Streams spec, 6.3.8. MakeSizeAlgorithmFromSizeFunction ( size ) + * + * The standard makes a big deal of turning JavaScript functions (grubby, + * touched by users, covered with germs) into algorithms (pristine, + * respectable, purposeful). We don't bother. Here we only check for errors and + * leave `size` unchanged. Then, in ReadableStreamDefaultControllerEnqueue and + * WritableStreamDefaultControllerGetChunkSize where this value is used, we + * check for undefined and behave as if we had "made" an "algorithm" for it. + */ +[[nodiscard]] bool js::MakeSizeAlgorithmFromSizeFunction(JSContext* cx, + Handle size) { + cx->check(size); + + // Step 1: If size is undefined, return an algorithm that returns 1. + if (size.isUndefined()) { + // Deferred. Size algorithm users must check for undefined. + return true; + } + + // Step 2: If ! IsCallable(size) is false, throw a TypeError exception. + if (!IsCallable(size)) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_FUNCTION, + "ReadableStream argument options.size"); + return false; + } + + // Step 3: Return an algorithm that performs the following steps, taking a + // chunk argument: + // a. Return ? Call(size, undefined, « chunk »). + // Deferred. Size algorithm users must know how to call the size function. + return true; +} diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/MiscellaneousOperations.h b/mozjs-sys/mozjs/js/src/builtin/streams/MiscellaneousOperations.h new file mode 100644 index 00000000000..f52815c9970 --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/MiscellaneousOperations.h @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Miscellaneous operations. */ + +#ifndef builtin_streams_MiscellaneousOperations_h +#define builtin_streams_MiscellaneousOperations_h + +#include "jstypes.h" // JS_PUBLIC_API +#include "js/CallArgs.h" // JS::CallArgs +#include "js/RootingAPI.h" // JS::{,Mutable}Handle +#include "js/Value.h" // JS::Value +#include "vm/JSObject.h" // JSObject +#include "vm/PromiseObject.h" // js::PromiseObject + +struct JS_PUBLIC_API JSContext; + +namespace js { + +class PropertyName; + +[[nodiscard]] extern PromiseObject* PromiseRejectedWithPendingError( + JSContext* cx); + +[[nodiscard]] inline bool ReturnPromiseRejectedWithPendingError( + JSContext* cx, const JS::CallArgs& args) { + PromiseObject* promise = PromiseRejectedWithPendingError(cx); + if (!promise) { + return false; + } + + args.rval().setObject(*promise); + return true; +} + +/** + * Streams spec, 6.3.1. + * CreateAlgorithmFromUnderlyingMethod ( underlyingObject, methodName, + * algoArgCount, extraArgs ) + * + * This function only partly implements the standard algorithm. We do not + * actually create a new JSFunction completely encapsulating the new algorithm. + * Instead, this just gets the specified method and checks for errors. It's the + * caller's responsibility to make sure that later, when the algorithm is + * "performed", the appropriate steps are carried out. + */ +[[nodiscard]] extern bool CreateAlgorithmFromUnderlyingMethod( + JSContext* cx, JS::Handle underlyingObject, + const char* methodNameForErrorMessage, JS::Handle methodName, + JS::MutableHandle method); + +/** + * Streams spec, 6.3.2. InvokeOrNoop ( O, P, args ) + * As it happens, all callers pass exactly one argument. + */ +[[nodiscard]] extern bool InvokeOrNoop(JSContext* cx, JS::Handle O, + JS::Handle P, + JS::Handle arg, + JS::MutableHandle rval); + +/** + * Streams spec, 6.3.7. ValidateAndNormalizeHighWaterMark ( highWaterMark ) + */ +[[nodiscard]] extern bool ValidateAndNormalizeHighWaterMark( + JSContext* cx, JS::Handle highWaterMarkVal, + double* highWaterMark); + +/** + * Streams spec, 6.3.8. MakeSizeAlgorithmFromSizeFunction ( size ) + */ +[[nodiscard]] extern bool MakeSizeAlgorithmFromSizeFunction( + JSContext* cx, JS::Handle size); + +template +inline bool IsMaybeWrapped(const JS::Handle v) { + return v.isObject() && v.toObject().canUnwrapAs(); +} + +} // namespace js + +#endif // builtin_streams_MiscellaneousOperations_h diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/PullIntoDescriptor.cpp b/mozjs-sys/mozjs/js/src/builtin/streams/PullIntoDescriptor.cpp new file mode 100644 index 00000000000..1b6c99a45b6 --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/PullIntoDescriptor.cpp @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Pull descriptor objects for tracking byte stream pull-into requests. */ + +#include "builtin/streams/PullIntoDescriptor.h" + +#include // uint32_t + +#include "js/Class.h" // JSClass, JSCLASS_HAS_RESERVED_SLOTS +#include "js/RootingAPI.h" // JS::Handle, JS::Rooted + +#include "vm/JSObject-inl.h" // js::NewBuiltinClassInstance + +using js::PullIntoDescriptor; + +using JS::Handle; +using JS::Int32Value; +using JS::ObjectOrNullValue; +using JS::ObjectValue; +using JS::Rooted; + +/* static */ PullIntoDescriptor* PullIntoDescriptor::create( + JSContext* cx, Handle buffer, uint32_t byteOffset, + uint32_t byteLength, uint32_t bytesFilled, uint32_t elementSize, + Handle ctor, ReaderType readerType) { + Rooted descriptor( + cx, NewBuiltinClassInstance(cx)); + if (!descriptor) { + return nullptr; + } + + descriptor->setFixedSlot(Slot_buffer, ObjectValue(*buffer)); + descriptor->setFixedSlot(Slot_Ctor, ObjectOrNullValue(ctor)); + descriptor->setFixedSlot(Slot_ByteOffset, Int32Value(byteOffset)); + descriptor->setFixedSlot(Slot_ByteLength, Int32Value(byteLength)); + descriptor->setFixedSlot(Slot_BytesFilled, Int32Value(bytesFilled)); + descriptor->setFixedSlot(Slot_ElementSize, Int32Value(elementSize)); + descriptor->setFixedSlot(Slot_ReaderType, + Int32Value(static_cast(readerType))); + return descriptor; +} + +const JSClass PullIntoDescriptor::class_ = { + "PullIntoDescriptor", JSCLASS_HAS_RESERVED_SLOTS(SlotCount)}; diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/PullIntoDescriptor.h b/mozjs-sys/mozjs/js/src/builtin/streams/PullIntoDescriptor.h new file mode 100644 index 00000000000..41378dd92e0 --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/PullIntoDescriptor.h @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Pull descriptor objects for tracking byte stream pull-into requests. */ + +#ifndef builtin_streams_PullIntoDescriptor_h +#define builtin_streams_PullIntoDescriptor_h + +#include // int32_t, uint32_t + +#include "js/Class.h" // JSClass +#include "vm/ArrayBufferObject.h" // js::ArrayBufferObject; +#include "vm/NativeObject.h" // js::NativeObject + +namespace js { + +enum class ReaderType : int32_t { Default = 0, BYOB = 1 }; + +class PullIntoDescriptor : public NativeObject { + private: + enum Slots { + Slot_buffer, + Slot_ByteOffset, + Slot_ByteLength, + Slot_BytesFilled, + Slot_ElementSize, + Slot_Ctor, + Slot_ReaderType, + SlotCount + }; + + public: + static const JSClass class_; + + ArrayBufferObject* buffer() { + return &getFixedSlot(Slot_buffer).toObject().as(); + } + void setBuffer(ArrayBufferObject* buffer) { + setFixedSlot(Slot_buffer, ObjectValue(*buffer)); + } + JSObject* ctor() { return getFixedSlot(Slot_Ctor).toObjectOrNull(); } + uint32_t byteOffset() const { + return getFixedSlot(Slot_ByteOffset).toInt32(); + } + uint32_t byteLength() const { + return getFixedSlot(Slot_ByteLength).toInt32(); + } + uint32_t bytesFilled() const { + return getFixedSlot(Slot_BytesFilled).toInt32(); + } + void setBytesFilled(int32_t bytes) { + setFixedSlot(Slot_BytesFilled, Int32Value(bytes)); + } + uint32_t elementSize() const { + return getFixedSlot(Slot_ElementSize).toInt32(); + } + ReaderType readerType() const { + int32_t n = getFixedSlot(Slot_ReaderType).toInt32(); + MOZ_ASSERT(n == int32_t(ReaderType::Default) || + n == int32_t(ReaderType::BYOB)); + return ReaderType(n); + } + + static PullIntoDescriptor* create(JSContext* cx, + JS::Handle buffer, + uint32_t byteOffset, uint32_t byteLength, + uint32_t bytesFilled, uint32_t elementSize, + JS::Handle ctor, + ReaderType readerType); +}; + +} // namespace js + +#endif // builtin_streams_PullIntoDescriptor_h diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/QueueWithSizes-inl.h b/mozjs-sys/mozjs/js/src/builtin/streams/QueueWithSizes-inl.h new file mode 100644 index 00000000000..fa73cdc7cac --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/QueueWithSizes-inl.h @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Queue-with-sizes operations. */ + +#ifndef builtin_streams_QueueWithSizes_inl_h +#define builtin_streams_QueueWithSizes_inl_h + +#include "builtin/streams/QueueWithSizes.h" + +#include "mozilla/Assertions.h" // MOZ_ASSERT + +#include "js/RootingAPI.h" // JS::Handle +#include "js/Value.h" // JS::Value +#include "vm/List.h" // js::ListObject + +#include "vm/List-inl.h" // js::ListObject::* + +struct JS_PUBLIC_API JSContext; + +namespace js { + +namespace detail { + +// The *internal* representation of a queue-with-sizes is a List of even length +// where elements (2 * n, 2 * n + 1) represent the nth (value, size) element in +// the queue. + +inline JS::Value QueueFirstValue(ListObject* unwrappedQueue) { + MOZ_ASSERT(!unwrappedQueue->isEmpty(), + "can't examine first value in an empty queue-with-sizes"); + MOZ_ASSERT((unwrappedQueue->length() % 2) == 0, + "queue-with-sizes must consist of (value, size) element pairs and " + "so must have even length"); + return unwrappedQueue->get(0); +} + +inline double QueueFirstSize(ListObject* unwrappedQueue) { + MOZ_ASSERT(!unwrappedQueue->isEmpty(), + "can't examine first value in an empty queue-with-sizes"); + MOZ_ASSERT((unwrappedQueue->length() % 2) == 0, + "queue-with-sizes must consist of (value, size) element pairs and " + "so must have even length"); + return unwrappedQueue->get(1).toDouble(); +} + +inline void QueueRemoveFirstValueAndSize(ListObject* unwrappedQueue, + JSContext* cx) { + MOZ_ASSERT(!unwrappedQueue->isEmpty(), + "can't remove first value from an empty queue-with-sizes"); + MOZ_ASSERT((unwrappedQueue->length() % 2) == 0, + "queue-with-sizes must consist of (value, size) element pairs and " + "so must have even length"); + unwrappedQueue->popFirstPair(cx); +} + +[[nodiscard]] inline bool QueueAppendValueAndSize( + JSContext* cx, JS::Handle unwrappedQueue, + JS::Handle value, double size) { + return unwrappedQueue->appendValueAndSize(cx, value, size); +} + +} // namespace detail + +/** + * Streams spec, 6.2.3. PeekQueueValue ( container ) nothrow + */ +inline JS::Value PeekQueueValue(ListObject* queue) { + return detail::QueueFirstValue(queue); +} + +} // namespace js + +#endif // builtin_streams_QueueWithSizes_inl_h diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/QueueWithSizes.cpp b/mozjs-sys/mozjs/js/src/builtin/streams/QueueWithSizes.cpp new file mode 100644 index 00000000000..baa8290cdbf --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/QueueWithSizes.cpp @@ -0,0 +1,171 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Queue-with-sizes operations. */ + +#include "builtin/streams/QueueWithSizes-inl.h" + +#include "mozilla/Assertions.h" // MOZ_ASSERT +#include "mozilla/FloatingPoint.h" // mozilla::Is{Infinite,NaN} + +#include "builtin/streams/StreamController.h" // js::StreamController +#include "js/Class.h" // JSClass, JSCLASS_HAS_RESERVED_SLOTS +#include "js/Conversions.h" // JS::ToNumber +#include "js/ErrorReport.h" // JS_ReportErrorNumberASCII +#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* +#include "js/RootingAPI.h" // JS::Rooted +#include "js/Value.h" // JS::Value, JS::{Number,Object}Value +#include "vm/Compartment.h" // JSCompartment +#include "vm/JSContext.h" // JSContext +#include "vm/List.h" // js::ListObject +#include "vm/NativeObject.h" // js::NativeObject + +#include "vm/Compartment-inl.h" // JSCompartment::wrap +#include "vm/JSContext-inl.h" // JSContext::check +#include "vm/JSObject-inl.h" // js::NewBuiltinClassInstance +#include "vm/List-inl.h" // js::ListObject::*, js::StoreNewListInFixedSlot +#include "vm/Realm-inl.h" // js::AutoRealm + +using JS::Handle; +using JS::MutableHandle; +using JS::NumberValue; +using JS::ObjectValue; +using JS::Rooted; +using JS::ToNumber; +using JS::Value; + +/*** 6.2. Queue-with-sizes operations ***************************************/ + +/** + * Streams spec, 6.2.1. DequeueValue ( container ) nothrow + */ +[[nodiscard]] bool js::DequeueValue( + JSContext* cx, Handle unwrappedContainer, + MutableHandle chunk) { + // Step 1: Assert: container has [[queue]] and [[queueTotalSize]] internal + // slots (implicit). + // Step 2: Assert: queue is not empty. + Rooted unwrappedQueue(cx, unwrappedContainer->queue()); + + // Step 3. Let pair be the first element of queue. + double chunkSize = detail::QueueFirstSize(unwrappedQueue); + chunk.set(detail::QueueFirstValue(unwrappedQueue)); + + // Step 4. Remove pair from queue, shifting all other elements downward + // (so that the second becomes the first, and so on). + detail::QueueRemoveFirstValueAndSize(unwrappedQueue, cx); + + // Step 5: Set container.[[queueTotalSize]] to + // container.[[queueTotalSize]] − pair.[[size]]. + // Step 6: If container.[[queueTotalSize]] < 0, set + // container.[[queueTotalSize]] to 0. + // (This can occur due to rounding errors.) + double totalSize = unwrappedContainer->queueTotalSize(); + totalSize -= chunkSize; + if (totalSize < 0) { + totalSize = 0; + } + unwrappedContainer->setQueueTotalSize(totalSize); + + // Step 7: Return pair.[[value]]. + return cx->compartment()->wrap(cx, chunk); +} + +void js::DequeueValue(StreamController* unwrappedContainer, JSContext* cx) { + // Step 1: Assert: container has [[queue]] and [[queueTotalSize]] internal + // slots (implicit). + // Step 2: Assert: queue is not empty. + ListObject* unwrappedQueue = unwrappedContainer->queue(); + + // Step 3. Let pair be the first element of queue. + // (The value is being discarded, so all we must extract is the size.) + double chunkSize = detail::QueueFirstSize(unwrappedQueue); + + // Step 4. Remove pair from queue, shifting all other elements downward + // (so that the second becomes the first, and so on). + detail::QueueRemoveFirstValueAndSize(unwrappedQueue, cx); + + // Step 5: Set container.[[queueTotalSize]] to + // container.[[queueTotalSize]] − pair.[[size]]. + // Step 6: If container.[[queueTotalSize]] < 0, set + // container.[[queueTotalSize]] to 0. + // (This can occur due to rounding errors.) + double totalSize = unwrappedContainer->queueTotalSize(); + totalSize -= chunkSize; + if (totalSize < 0) { + totalSize = 0; + } + unwrappedContainer->setQueueTotalSize(totalSize); + + // Step 7: Return pair.[[value]]. (omitted because not used) +} + +/** + * Streams spec, 6.2.2. EnqueueValueWithSize ( container, value, size ) throws + */ +[[nodiscard]] bool js::EnqueueValueWithSize( + JSContext* cx, Handle unwrappedContainer, + Handle value, Handle sizeVal) { + cx->check(value, sizeVal); + + // Step 1: Assert: container has [[queue]] and [[queueTotalSize]] internal + // slots (implicit). + // Step 2: Let size be ? ToNumber(size). + double size; + if (!ToNumber(cx, sizeVal, &size)) { + return false; + } + + // Step 3: If ! IsFiniteNonNegativeNumber(size) is false, throw a RangeError + // exception. + if (size < 0 || std::isnan(size) || std::isinf(size)) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_NUMBER_MUST_BE_FINITE_NON_NEGATIVE, "size"); + return false; + } + + // Step 4: Append Record {[[value]]: value, [[size]]: size} as the last + // element of container.[[queue]]. + { + AutoRealm ar(cx, unwrappedContainer); + Rooted unwrappedQueue(cx, unwrappedContainer->queue()); + Rooted wrappedVal(cx, value); + if (!cx->compartment()->wrap(cx, &wrappedVal)) { + return false; + } + + if (!detail::QueueAppendValueAndSize(cx, unwrappedQueue, wrappedVal, + size)) { + return false; + } + } + + // Step 5: Set container.[[queueTotalSize]] to + // container.[[queueTotalSize]] + size. + unwrappedContainer->setQueueTotalSize(unwrappedContainer->queueTotalSize() + + size); + + return true; +} + +/** + * Streams spec, 6.2.4. ResetQueue ( container ) nothrow + */ +[[nodiscard]] bool js::ResetQueue( + JSContext* cx, Handle unwrappedContainer) { + // Step 1: Assert: container has [[queue]] and [[queueTotalSize]] internal + // slots (implicit). + // Step 2: Set container.[[queue]] to a new empty List. + if (!StoreNewListInFixedSlot(cx, unwrappedContainer, + StreamController::Slot_Queue)) { + return false; + } + + // Step 3: Set container.[[queueTotalSize]] to 0. + unwrappedContainer->setQueueTotalSize(0); + + return true; +} diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/QueueWithSizes.h b/mozjs-sys/mozjs/js/src/builtin/streams/QueueWithSizes.h new file mode 100644 index 00000000000..da4f490c76d --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/QueueWithSizes.h @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Queue-with-sizes operations. */ + +#ifndef builtin_streams_QueueWithSizes_h +#define builtin_streams_QueueWithSizes_h + +#include "mozilla/Assertions.h" // MOZ_ASSERT + +#include "jstypes.h" // JS_PUBLIC_API +#include "js/RootingAPI.h" // JS::{,Mutable}Handle +#include "js/Value.h" // JS::Value +#include "vm/List.h" // js::ListObject + +struct JS_PUBLIC_API JSContext; + +namespace js { + +class StreamController; + +/** + * Streams spec, 6.2.1. DequeueValue ( container ) nothrow + */ +[[nodiscard]] extern bool DequeueValue( + JSContext* cx, JS::Handle unwrappedContainer, + JS::MutableHandle chunk); + +/** + * Streams spec, 6.2.1. DequeueValue ( container ) nothrow + * when the dequeued value is ignored. + */ +extern void DequeueValue(StreamController* unwrappedContainer, JSContext* cx); + +/** + * Streams spec, 6.2.2. EnqueueValueWithSize ( container, value, size ) throws + */ +[[nodiscard]] extern bool EnqueueValueWithSize( + JSContext* cx, JS::Handle unwrappedContainer, + JS::Handle value, JS::Handle sizeVal); + +/** + * Streams spec, 6.2.4. ResetQueue ( container ) nothrow + */ +[[nodiscard]] extern bool ResetQueue( + JSContext* cx, JS::Handle unwrappedContainer); + +inline bool QueueIsEmpty(ListObject* unwrappedQueue) { + if (unwrappedQueue->isEmpty()) { + return true; + } + + MOZ_ASSERT((unwrappedQueue->length() % 2) == 0, + "queue-with-sizes must consist of (value, size) element pairs and " + "so must have even length"); + return false; +} + +} // namespace js + +#endif // builtin_streams_QueueWithSizes_h diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/QueueingStrategies.cpp b/mozjs-sys/mozjs/js/src/builtin/streams/QueueingStrategies.cpp new file mode 100644 index 00000000000..2b03d2fec7b --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/QueueingStrategies.cpp @@ -0,0 +1,231 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Queuing strategies. */ + +#include "builtin/streams/QueueingStrategies.h" + +#include "builtin/streams/ClassSpecMacro.h" // JS_STREAMS_CLASS_SPEC +#include "js/CallArgs.h" // JS::CallArgs{,FromVp} +#include "js/Class.h" // JS::ObjectOpResult, JS_NULL_CLASS_OPS +#include "js/Conversions.h" // JS::ToNumber +#include "js/PropertySpec.h" // JS{Property,Function}Spec, JS_FN, JS_FS_END, JS_PS_END +#include "js/ProtoKey.h" // JSProto_{ByteLength,Count}QueuingStrategy +#include "js/RootingAPI.h" // JS::{Handle,Rooted} +#include "vm/Interpreter.h" // js::GetProperty +#include "vm/JSObject.h" // js::GetPrototypeFromBuiltinConstructor +#include "vm/ObjectOperations.h" // js::{Define,Get}Property +#include "vm/Runtime.h" // JSAtomState +#include "vm/StringType.h" // js::NameToId, PropertyName + +#include "vm/Compartment-inl.h" // js::UnwrapAndTypeCheckThis +#include "vm/JSObject-inl.h" // js::NewObjectWithClassProto +#include "vm/NativeObject-inl.h" // js::ThrowIfNotConstructing + +using js::ByteLengthQueuingStrategy; +using js::CountQueuingStrategy; +using js::PropertyName; +using js::UnwrapAndTypeCheckThis; + +using JS::CallArgs; +using JS::CallArgsFromVp; +using JS::Handle; +using JS::ObjectOpResult; +using JS::Rooted; +using JS::ToNumber; +using JS::ToObject; +using JS::Value; + +/*** 6.1. Queuing strategies ************************************************/ + +// Streams spec, 6.1.2.2. new ByteLengthQueuingStrategy({ highWaterMark }) +bool js::ByteLengthQueuingStrategy::constructor(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + if (!ThrowIfNotConstructing(cx, args, "ByteLengthQueuingStrategy")) { + return false; + } + + // Implicit in the spec: Create the new strategy object. + Rooted proto(cx); + if (!GetPrototypeFromBuiltinConstructor( + cx, args, JSProto_ByteLengthQueuingStrategy, &proto)) { + return false; + } + Rooted strategy( + cx, NewObjectWithClassProto(cx, proto)); + if (!strategy) { + return false; + } + + // Implicit in the spec: Argument destructuring. + RootedObject argObj(cx, ToObject(cx, args.get(0))); + if (!argObj) { + return false; + } + + // https://heycam.github.io/webidl/#es-dictionary + // 3.2.17. Dictionary types + // Step 4.1.2: Let esMemberValue be an ECMAScript value, + // depending on Type(esDict): ? Get(esDict, key) + RootedValue highWaterMarkV(cx); + if (!GetProperty(cx, argObj, argObj, cx->names().highWaterMark, + &highWaterMarkV)) { + return false; + } + // Step 4.1.5: Otherwise, if esMemberValue is undefined and + // member is required, then throw a TypeError. + if (highWaterMarkV.isUndefined()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_STREAM_MISSING_HIGHWATERMARK); + return false; + } + // Step 4.1.3: If esMemberValue is not undefined, then: + // Let idlMemberValue be the result of converting esMemberValue to + // an IDL value whose type is the type member is declared to be of. + double highWaterMark; + if (!ToNumber(cx, highWaterMarkV, &highWaterMark)) { + return false; + } + + // Step 1: Set this.[[highWaterMark]] to init["highWaterMark"]. + strategy->setHighWaterMark(highWaterMark); + + args.rval().setObject(*strategy); + return true; +} + +static bool ByteLengthQueuingStrategy_highWaterMark(JSContext* cx, + unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + Rooted unwrappedStrategy( + cx, UnwrapAndTypeCheckThis( + cx, args, "get highWaterMark")); + if (!unwrappedStrategy) { + return false; + } + + // Step 1: Return this.[[highWaterMark]]. + args.rval().setDouble(unwrappedStrategy->highWaterMark()); + return true; +} + +// Streams spec 6.1.2.3.1. size ( chunk ) +static bool ByteLengthQueuingStrategy_size(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + // Step 1: Return ? GetV(chunk, "byteLength"). + return GetProperty(cx, args.get(0), cx->names().byteLength, args.rval()); +} + +static const JSPropertySpec ByteLengthQueuingStrategy_properties[] = { + JS_PSG("highWaterMark", ByteLengthQueuingStrategy_highWaterMark, + JSPROP_ENUMERATE), + JS_STRING_SYM_PS(toStringTag, "ByteLengthQueuingStrategy", JSPROP_READONLY), + JS_PS_END}; + +static const JSFunctionSpec ByteLengthQueuingStrategy_methods[] = { + JS_FN("size", ByteLengthQueuingStrategy_size, 1, 0), JS_FS_END}; + +JS_STREAMS_CLASS_SPEC(ByteLengthQueuingStrategy, 1, SlotCount, 0, 0, + JS_NULL_CLASS_OPS); + +// Streams spec, 6.1.3.2. new CountQueuingStrategy({ highWaterMark }) +bool js::CountQueuingStrategy::constructor(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + if (!ThrowIfNotConstructing(cx, args, "CountQueuingStrategy")) { + return false; + } + + // Implicit in the spec: Create the new strategy object. + RootedObject proto(cx); + if (!GetPrototypeFromBuiltinConstructor( + cx, args, JSProto_CountQueuingStrategy, &proto)) { + return false; + } + Rooted strategy( + cx, NewObjectWithClassProto(cx, proto)); + if (!strategy) { + return false; + } + + // Implicit in the spec: Argument destructuring. + RootedObject argObj(cx, ToObject(cx, args.get(0))); + if (!argObj) { + return false; + } + // https://heycam.github.io/webidl/#es-dictionary + // 3.2.17. Dictionary types + // Step 4.1.2: Let esMemberValue be an ECMAScript value, + // depending on Type(esDict): ? Get(esDict, key) + RootedValue highWaterMarkV(cx); + if (!GetProperty(cx, argObj, argObj, cx->names().highWaterMark, + &highWaterMarkV)) { + return false; + } + // Step 4.1.5: Otherwise, if esMemberValue is undefined and + // member is required, then throw a TypeError. + if (highWaterMarkV.isUndefined()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_STREAM_MISSING_HIGHWATERMARK); + return false; + } + // Step 4.1.3: If esMemberValue is not undefined, then: + // Let idlMemberValue be the result of converting esMemberValue to + // an IDL value whose type is the type member is declared to be of. + double highWaterMark; + if (!ToNumber(cx, highWaterMarkV, &highWaterMark)) { + return false; + } + + // Step 1: Set this.[[highWaterMark]] to init["highWaterMark"]. + strategy->setHighWaterMark(highWaterMark); + + args.rval().setObject(*strategy); + return true; +} + +static bool CountQueuingStrategy_highWaterMark(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + Rooted unwrappedStrategy( + cx, UnwrapAndTypeCheckThis(cx, args, + "get highWaterMark")); + if (!unwrappedStrategy) { + return false; + } + + // Step 1: Return this.[[highWaterMark]]. + args.rval().setDouble(unwrappedStrategy->highWaterMark()); + return true; +} + +// Streams spec 6.1.3.3.1. size ( chunk ) +static bool CountQueuingStrategy_size(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + // Step 1: Return 1. + args.rval().setInt32(1); + return true; +} + +static const JSPropertySpec CountQueuingStrategy_properties[] = { + JS_PSG("highWaterMark", CountQueuingStrategy_highWaterMark, + JSPROP_ENUMERATE), + JS_STRING_SYM_PS(toStringTag, "CountQueuingStrategy", JSPROP_READONLY), + JS_PS_END}; + +static const JSFunctionSpec CountQueuingStrategy_methods[] = { + JS_FN("size", CountQueuingStrategy_size, 0, 0), JS_FS_END}; + +JS_STREAMS_CLASS_SPEC(CountQueuingStrategy, 1, SlotCount, 0, 0, + JS_NULL_CLASS_OPS); diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/QueueingStrategies.h b/mozjs-sys/mozjs/js/src/builtin/streams/QueueingStrategies.h new file mode 100644 index 00000000000..6da83e770b3 --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/QueueingStrategies.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Queuing strategies. */ + +#ifndef builtin_stream_QueueingStrategies_h +#define builtin_stream_QueueingStrategies_h + +#include "js/Class.h" // JSClass, js::ClassSpec +#include "js/Value.h" // JS::Value +#include "vm/JSContext.h" // JSContext +#include "vm/NativeObject.h" // js::NativeObject + +namespace js { + +class ByteLengthQueuingStrategy : public NativeObject { + public: + enum Slots { Slot_HighWaterMark, SlotCount }; + + double highWaterMark() const { + return getFixedSlot(Slot_HighWaterMark).toDouble(); + } + void setHighWaterMark(double value) { + setFixedSlot(Slot_HighWaterMark, JS::DoubleValue(value)); + } + + static bool constructor(JSContext* cx, unsigned argc, JS::Value* vp); + static const ClassSpec classSpec_; + static const JSClass class_; + static const ClassSpec protoClassSpec_; + static const JSClass protoClass_; +}; + +class CountQueuingStrategy : public NativeObject { + public: + enum Slots { Slot_HighWaterMark, SlotCount }; + + double highWaterMark() const { + return getFixedSlot(Slot_HighWaterMark).toDouble(); + } + void setHighWaterMark(double value) { + setFixedSlot(Slot_HighWaterMark, JS::DoubleValue(value)); + } + + static bool constructor(JSContext* cx, unsigned argc, JS::Value* vp); + static const ClassSpec classSpec_; + static const JSClass class_; + static const ClassSpec protoClassSpec_; + static const JSClass protoClass_; +}; + +} // namespace js + +#endif // builtin_stream_QueueingStrategies_h diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStream.cpp b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStream.cpp new file mode 100644 index 00000000000..a8445aa65eb --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStream.cpp @@ -0,0 +1,439 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Class ReadableStream. */ + +#include "builtin/streams/ReadableStream.h" + +#include "mozilla/Maybe.h" // mozilla::Maybe, mozilla::Some + +#include "jspubtd.h" // JSProto_ReadableStream + +#include "builtin/Array.h" // js::NewDenseFullyAllocatedArray +#include "builtin/streams/ClassSpecMacro.h" // JS_STREAMS_CLASS_SPEC +#include "builtin/streams/MiscellaneousOperations.h" // js::MakeSizeAlgorithmFromSizeFunction, js::ValidateAndNormalizeHighWaterMark, js::ReturnPromiseRejectedWithPendingError +#include "builtin/streams/ReadableStreamController.h" // js::ReadableStream{,Default}Controller, js::ReadableByteStreamController +#include "builtin/streams/ReadableStreamDefaultControllerOperations.h" // js::SetUpReadableStreamDefaultControllerFromUnderlyingSource +#include "builtin/streams/ReadableStreamInternals.h" // js::ReadableStreamCancel +#include "builtin/streams/ReadableStreamOperations.h" // js::ReadableStream{PipeTo,Tee} +#include "builtin/streams/ReadableStreamReader.h" // js::CreateReadableStream{BYOB,Default}Reader, js::ForAuthorCodeBool +#include "js/CallArgs.h" // JS::CallArgs{,FromVp} +#include "js/Class.h" // JSCLASS_SLOT0_IS_NSISUPPORTS, JS_NULL_CLASS_OPS +#include "js/Conversions.h" // JS::ToBoolean +#include "js/ErrorReport.h" // JS_ReportErrorNumberASCII +#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* +#include "js/PropertySpec.h" // JS{Function,Property}Spec, JS_FN, JS_PSG, JS_{FS,PS}_END +#include "js/RootingAPI.h" // JS::Handle, JS::Rooted, js::CanGC +#include "js/Stream.h" // JS::ReadableStream{Mode,UnderlyingSource} +#include "js/Value.h" // JS::Value +#include "vm/Interpreter.h" // js::GetProperty +#include "vm/JSContext.h" // JSContext +#include "vm/JSObject.h" // js::GetPrototypeFromBuiltinConstructor +#include "vm/ObjectOperations.h" // js::GetProperty +#include "vm/PlainObject.h" // js::PlainObject +#include "vm/Runtime.h" // JSAtomState, JSRuntime +#include "vm/StringType.h" // js::EqualStrings, js::ToString + +#include "vm/Compartment-inl.h" // js::UnwrapAndTypeCheck{Argument,This,Value} +#include "vm/JSObject-inl.h" // js::NewBuiltinClassInstance +#include "vm/NativeObject-inl.h" // js::ThrowIfNotConstructing + +using mozilla::Maybe; +using mozilla::Some; + +using js::CanGC; +using js::ClassSpec; +using js::CreateReadableStreamDefaultReader; +using js::EqualStrings; +using js::ForAuthorCodeBool; +using js::GetErrorMessage; +using js::NativeObject; +using js::NewBuiltinClassInstance; +using js::NewDenseFullyAllocatedArray; +using js::PlainObject; +using js::ReadableStream; +using js::ReadableStreamTee; +using js::ReturnPromiseRejectedWithPendingError; +using js::ToString; +using js::UnwrapAndTypeCheckArgument; +using js::UnwrapAndTypeCheckThis; +using js::UnwrapAndTypeCheckValue; + +using JS::CallArgs; +using JS::CallArgsFromVp; +using JS::Handle; +using JS::ObjectValue; +using JS::Rooted; +using JS::Value; + +/*** 3.2. Class ReadableStream **********************************************/ + +JS::ReadableStreamMode ReadableStream::mode() const { + ReadableStreamController* controller = this->controller(); + if (controller->is()) { + return JS::ReadableStreamMode::Default; + } + return controller->as().hasExternalSource() + ? JS::ReadableStreamMode::ExternalSource + : JS::ReadableStreamMode::Byte; +} + +ReadableStream* ReadableStream::createExternalSourceStream( + JSContext* cx, JS::ReadableStreamUnderlyingSource* source, + void* nsISupportsObject_alreadyAddreffed /* = nullptr */, + Handle proto /* = nullptr */) { + Rooted stream( + cx, create(cx, nsISupportsObject_alreadyAddreffed, proto)); + if (!stream) { + return nullptr; + } + + if (!SetUpExternalReadableByteStreamController(cx, stream, source)) { + return nullptr; + } + + return stream; +} + +/** + * Streams spec, 3.2.3. new ReadableStream(underlyingSource = {}, strategy = {}) + */ +bool ReadableStream::constructor(JSContext* cx, unsigned argc, JS::Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + if (!ThrowIfNotConstructing(cx, args, "ReadableStream")) { + return false; + } + + // Implicit in the spec: argument default values. + Rooted underlyingSource(cx, args.get(0)); + if (underlyingSource.isUndefined()) { + JSObject* emptyObj = NewPlainObject(cx); + if (!emptyObj) { + return false; + } + underlyingSource = ObjectValue(*emptyObj); + } + + Rooted strategy(cx, args.get(1)); + if (strategy.isUndefined()) { + JSObject* emptyObj = NewPlainObject(cx); + if (!emptyObj) { + return false; + } + strategy = ObjectValue(*emptyObj); + } + + // Implicit in the spec: Set this to + // OrdinaryCreateFromConstructor(NewTarget, ...). + // Step 1: Perform ! InitializeReadableStream(this). + Rooted proto(cx); + if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_ReadableStream, + &proto)) { + return false; + } + Rooted stream(cx, + ReadableStream::create(cx, nullptr, proto)); + if (!stream) { + return false; + } + + // Step 2: Let size be ? GetV(strategy, "size"). + Rooted size(cx); + if (!GetProperty(cx, strategy, cx->names().size, &size)) { + return false; + } + + // Step 3: Let highWaterMark be ? GetV(strategy, "highWaterMark"). + Rooted highWaterMarkVal(cx); + if (!GetProperty(cx, strategy, cx->names().highWaterMark, + &highWaterMarkVal)) { + return false; + } + + // Step 4: Let type be ? GetV(underlyingSource, "type"). + Rooted type(cx); + if (!GetProperty(cx, underlyingSource, cx->names().type, &type)) { + return false; + } + + // Step 5: Let typeString be ? ToString(type). + Rooted typeString(cx, ToString(cx, type)); + if (!typeString) { + return false; + } + + // Step 6: If typeString is "bytes", + bool equal; + if (!EqualStrings(cx, typeString, cx->names().bytes, &equal)) { + return false; + } + if (equal) { + // The rest of step 6 is unimplemented, since we don't support + // user-defined byte streams yet. + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_READABLESTREAM_BYTES_TYPE_NOT_IMPLEMENTED); + return false; + } + + // Step 7: Otherwise, if type is undefined, + if (type.isUndefined()) { + // Step 7.a: Let sizeAlgorithm be ? MakeSizeAlgorithmFromSizeFunction(size). + if (!MakeSizeAlgorithmFromSizeFunction(cx, size)) { + return false; + } + + // Step 7.b: If highWaterMark is undefined, let highWaterMark be 1. + double highWaterMark; + if (highWaterMarkVal.isUndefined()) { + highWaterMark = 1; + } else { + // Step 7.c: Set highWaterMark to ? + // ValidateAndNormalizeHighWaterMark(highWaterMark). + if (!ValidateAndNormalizeHighWaterMark(cx, highWaterMarkVal, + &highWaterMark)) { + return false; + } + } + + // Step 7.d: Perform + // ? SetUpReadableStreamDefaultControllerFromUnderlyingSource( + // this, underlyingSource, highWaterMark, sizeAlgorithm). + if (!SetUpReadableStreamDefaultControllerFromUnderlyingSource( + cx, stream, underlyingSource, highWaterMark, size)) { + return false; + } + + args.rval().setObject(*stream); + return true; + } + + // Step 8: Otherwise, throw a RangeError exception. + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_READABLESTREAM_UNDERLYINGSOURCE_TYPE_WRONG); + return false; +} + +/** + * Streams spec, 3.2.5.1. get locked + */ +[[nodiscard]] static bool ReadableStream_locked(JSContext* cx, unsigned argc, + JS::Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + // Step 1: If ! IsReadableStream(this) is false, throw a TypeError exception. + Rooted unwrappedStream( + cx, UnwrapAndTypeCheckThis(cx, args, "get locked")); + if (!unwrappedStream) { + return false; + } + + // Step 2: Return ! IsReadableStreamLocked(this). + args.rval().setBoolean(unwrappedStream->locked()); + return true; +} + +/** + * Streams spec, 3.2.5.2. cancel ( reason ) + */ +[[nodiscard]] static bool ReadableStream_cancel(JSContext* cx, unsigned argc, + JS::Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + // Step 1: If ! IsReadableStream(this) is false, return a promise rejected + // with a TypeError exception. + Rooted unwrappedStream( + cx, UnwrapAndTypeCheckThis(cx, args, "cancel")); + if (!unwrappedStream) { + return ReturnPromiseRejectedWithPendingError(cx, args); + } + + // Step 2: If ! IsReadableStreamLocked(this) is true, return a promise + // rejected with a TypeError exception. + if (unwrappedStream->locked()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_READABLESTREAM_LOCKED_METHOD, "cancel"); + return ReturnPromiseRejectedWithPendingError(cx, args); + } + + // Step 3: Return ! ReadableStreamCancel(this, reason). + Rooted cancelPromise( + cx, js::ReadableStreamCancel(cx, unwrappedStream, args.get(0))); + if (!cancelPromise) { + return false; + } + args.rval().setObject(*cancelPromise); + return true; +} + +// Streams spec, 3.2.5.3. +// getIterator({ preventCancel } = {}) +// +// Not implemented. + +/** + * https://streams.spec.whatwg.org/#rs-get-reader + * ReadableStreamReader getReader(optional ReadableStreamGetReaderOptions + * options = {}); + */ +[[nodiscard]] static bool ReadableStream_getReader(JSContext* cx, unsigned argc, + JS::Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + // Implicit |this| check. + Rooted unwrappedStream( + cx, UnwrapAndTypeCheckThis(cx, args, "getReader")); + if (!unwrappedStream) { + return false; + } + + // Implicit in the spec: Dictionary destructuring. + // https://heycam.github.io/webidl/#es-dictionary + // 3.2.17. Dictionary types + + Rooted optionsVal(cx, args.get(0)); + // Step 1. + if (!optionsVal.isNullOrUndefined() && !optionsVal.isObject()) { + ReportValueError(cx, JSMSG_CANT_CONVERT_TO, JSDVG_IGNORE_STACK, optionsVal, + nullptr, "dictionary"); + return false; + } + + Maybe mode; + // Step 4: ... + // + // - Optimized for one dictionary member. + // - Treat non-object options as non-existing "mode" member. + if (optionsVal.isObject()) { + Rooted modeVal(cx); + if (!GetProperty(cx, optionsVal, cx->names().mode, &modeVal)) { + return false; + } + + // Step 4.1.3: If esMemberValue is not undefined, then: ... + if (!modeVal.isUndefined()) { + // https://heycam.github.io/webidl/#es-enumeration + // 3.2.18. Enumeration types + + // Step 1: Let S be the result of calling ToString(V). + Rooted modeStr(cx, ToString(cx, modeVal)); + if (!modeStr) { + return false; + } + + // Step 2: If S is not one of E's enumeration values, + // then throw a TypeError. + // + // Note: We only have one valid value "byob". + bool equal; + if (!EqualStrings(cx, modeStr, cx->names().byob, &equal)) { + return false; + } + if (!equal) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_READABLESTREAM_INVALID_READER_MODE); + return false; + } + + mode = Some(JS::ReadableStreamReaderMode::Byob); + } + } + + // Step 1: If options["mode"] does not exist, + // return ? AcquireReadableStreamDefaultReader(this). + Rooted reader(cx); + if (mode.isNothing()) { + reader = CreateReadableStreamDefaultReader(cx, unwrappedStream, + ForAuthorCodeBool::Yes); + } else { + // Step 2: Assert: options["mode"] is "byob". + MOZ_ASSERT(mode.value() == JS::ReadableStreamReaderMode::Byob); + + // Step 3: Return ? AcquireReadableStreamBYOBReader(this). + reader = CreateReadableStreamBYOBReader(cx, unwrappedStream, + ForAuthorCodeBool::Yes); + } + + if (!reader) { + return false; + } + + args.rval().setObject(*reader); + return true; +} + +/** + * Streams spec, 3.2.5.7. tee() + */ +static bool ReadableStream_tee(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + // Step 1: If ! IsReadableStream(this) is false, throw a TypeError exception. + Rooted unwrappedStream( + cx, UnwrapAndTypeCheckThis(cx, args, "tee")); + if (!unwrappedStream) { + return false; + } + + // Step 2: Let branches be ? ReadableStreamTee(this, false). + Rooted branch1(cx); + Rooted branch2(cx); + if (!ReadableStreamTee(cx, unwrappedStream, false, &branch1, &branch2)) { + return false; + } + + // Step 3: Return ! CreateArrayFromList(branches). + Rooted branches(cx, NewDenseFullyAllocatedArray(cx, 2)); + if (!branches) { + return false; + } + branches->setDenseInitializedLength(2); + branches->initDenseElement(0, ObjectValue(*branch1)); + branches->initDenseElement(1, ObjectValue(*branch2)); + + args.rval().setObject(*branches); + return true; +} + +// Streams spec, 3.2.5.8. +// [@@asyncIterator]({ preventCancel } = {}) +// +// Not implemented. + +static const JSFunctionSpec ReadableStream_methods[] = { + JS_FN("cancel", ReadableStream_cancel, 0, JSPROP_ENUMERATE), + JS_FN("getReader", ReadableStream_getReader, 0, JSPROP_ENUMERATE), + // pipeTo is only conditionally supported right now, so it must be manually + // added below if desired. + JS_FN("tee", ReadableStream_tee, 0, JSPROP_ENUMERATE), JS_FS_END}; + +static const JSPropertySpec ReadableStream_properties[] = { + JS_PSG("locked", ReadableStream_locked, JSPROP_ENUMERATE), + JS_STRING_SYM_PS(toStringTag, "ReadableStream", JSPROP_READONLY), + JS_PS_END}; + +const ClassSpec ReadableStream::classSpec_ = { + js::GenericCreateConstructor, + js::GenericCreatePrototype, + nullptr, + nullptr, + ReadableStream_methods, + ReadableStream_properties, + nullptr, + 0}; + +const JSClass ReadableStream::class_ = { + "ReadableStream", + JSCLASS_HAS_RESERVED_SLOTS(ReadableStream::SlotCount) | + JSCLASS_HAS_CACHED_PROTO(JSProto_ReadableStream) | + JSCLASS_SLOT0_IS_NSISUPPORTS, + JS_NULL_CLASS_OPS, &ReadableStream::classSpec_}; + +const JSClass ReadableStream::protoClass_ = { + "ReadableStream.prototype", + JSCLASS_HAS_CACHED_PROTO(JSProto_ReadableStream), JS_NULL_CLASS_OPS, + &ReadableStream::classSpec_}; diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStream.h b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStream.h new file mode 100644 index 00000000000..f59cfbc99d5 --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStream.h @@ -0,0 +1,144 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Class ReadableStream. */ + +#ifndef builtin_streams_ReadableStream_h +#define builtin_streams_ReadableStream_h + +#include "mozilla/Assertions.h" // MOZ_ASSERT{,_IF} + +#include // uint32_t + +#include "jstypes.h" // JS_PUBLIC_API +#include "js/Class.h" // JSClass, js::ClassSpec +#include "js/RootingAPI.h" // JS::Handle +#include "js/Stream.h" // JS::ReadableStream{Mode,UnderlyingSource} +#include "js/Value.h" // JS::Int32Value, JS::ObjectValue, JS::UndefinedValue +#include "vm/NativeObject.h" // js::NativeObject + +class JS_PUBLIC_API JSObject; + +namespace js { + +class ReadableStreamController; + +class ReadableStream : public NativeObject { + public: + /** + * Memory layout of Stream instances. + * + * See https://streams.spec.whatwg.org/#rs-internal-slots for details on + * the stored state. [[state]] and [[disturbed]] are stored in + * StreamSlot_State as ReadableStream::State enum values. + * + * Of the stored values, Reader and StoredError might be cross-compartment + * wrappers. This can happen if the Reader was created by applying a + * different compartment's ReadableStream.prototype.getReader method. + * + * A stream's associated controller is always created from under the + * stream's constructor and thus cannot be in a different compartment. + */ + enum Slots { + /** + * Optional pointer to make the stream participate in Gecko's cycle + * collection. See also JSCLASS_SLOT0_IS_NSISUPPORTS. + */ + Slot_ISupports, + + Slot_Controller, + Slot_Reader, + Slot_State, + Slot_StoredError, + SlotCount + }; + + private: + enum StateBits { + Readable = 0, + Closed = 1, + Errored = 2, + StateMask = 0x000000ff, + Disturbed = 0x00000100 + }; + + uint32_t stateBits() const { return getFixedSlot(Slot_State).toInt32(); } + void initStateBits(uint32_t stateBits) { + MOZ_ASSERT((stateBits & ~Disturbed) <= Errored); + setFixedSlot(Slot_State, JS::Int32Value(stateBits)); + } + void setStateBits(uint32_t stateBits) { +#ifdef DEBUG + bool wasDisturbed = disturbed(); + bool wasClosedOrErrored = closed() || errored(); +#endif + initStateBits(stateBits); + MOZ_ASSERT_IF(wasDisturbed, disturbed()); + MOZ_ASSERT_IF(wasClosedOrErrored, !readable()); + } + + StateBits state() const { return StateBits(stateBits() & StateMask); } + void setState(StateBits state) { + MOZ_ASSERT(state <= Errored); + uint32_t current = stateBits() & ~StateMask; + setStateBits(current | state); + } + + public: + bool readable() const { return state() == Readable; } + bool closed() const { return state() == Closed; } + void setClosed() { setState(Closed); } + bool errored() const { return state() == Errored; } + void setErrored() { setState(Errored); } + bool disturbed() const { return stateBits() & Disturbed; } + void setDisturbed() { setStateBits(stateBits() | Disturbed); } + + bool hasController() const { + return !getFixedSlot(Slot_Controller).isUndefined(); + } + inline ReadableStreamController* controller() const; + inline void setController(ReadableStreamController* controller); + void clearController() { + setFixedSlot(Slot_Controller, JS::UndefinedValue()); + } + + bool hasReader() const { return !getFixedSlot(Slot_Reader).isUndefined(); } + void setReader(JSObject* reader) { + setFixedSlot(Slot_Reader, JS::ObjectValue(*reader)); + } + void clearReader() { setFixedSlot(Slot_Reader, JS::UndefinedValue()); } + + JS::Value storedError() const { return getFixedSlot(Slot_StoredError); } + void setStoredError(JS::Handle value) { + setFixedSlot(Slot_StoredError, value); + } + + JS::ReadableStreamMode mode() const; + + bool locked() const; + + [[nodiscard]] static ReadableStream* create( + JSContext* cx, void* nsISupportsObject_alreadyAddreffed = nullptr, + JS::Handle proto = nullptr); + static ReadableStream* createExternalSourceStream( + JSContext* cx, JS::ReadableStreamUnderlyingSource* source, + void* nsISupportsObject_alreadyAddreffed = nullptr, + JS::Handle proto = nullptr); + + static bool constructor(JSContext* cx, unsigned argc, Value* vp); + static const ClassSpec classSpec_; + static const JSClass class_; + static const ClassSpec protoClassSpec_; + static const JSClass protoClass_; +}; + +[[nodiscard]] extern bool SetUpExternalReadableByteStreamController( + JSContext* cx, JS::Handle stream, + JS::ReadableStreamUnderlyingSource* source); + +} // namespace js + +#endif // builtin_streams_ReadableStream_h diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamBYOBReader.cpp b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamBYOBReader.cpp new file mode 100644 index 00000000000..eb0210dc906 --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamBYOBReader.cpp @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Class ReadableStreamBYOBReader. + * + * Byte streams and BYOB readers are unimplemented, so this is skeletal -- yet + * helpful to ensure certain trivial tests of the functionality in wpt, that + * don't actually test fully-constructed byte streams/BYOB readers, pass. 🙄 + */ + +#include "builtin/streams/ReadableStream.h" // js::ReadableStream +#include "builtin/streams/ReadableStreamReader.h" // js::CreateReadableStreamBYOBReader, js::ForAuthorCodeBool +#include "js/ErrorReport.h" // JS_ReportErrorNumberASCII +#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* + +using JS::Handle; + +/*** 3.7. Class ReadableStreamBYOBReader *********************************/ + +/** + * Stream spec, 3.7.3. new ReadableStreamBYOBReader ( stream ) + * Steps 2-5. + */ +[[nodiscard]] JSObject* js::CreateReadableStreamBYOBReader( + JSContext* cx, Handle unwrappedStream, + ForAuthorCodeBool forAuthorCode, Handle proto /* = nullptr */) { + // Step 2: If ! IsReadableByteStreamController( + // stream.[[readableStreamController]]) is false, throw a + // TypeError exception. + // We don't implement byte stream controllers yet, so always throw here. Note + // that JSMSG_READABLESTREAM_BYTES_TYPE_NOT_IMPLEMENTED can't be used here + // because it's a RangeError (and sadly wpt actually tests this and we have a + // spurious failure if we don't make this a TypeError). + JS_ReportErrorNumberASCII( + cx, GetErrorMessage, nullptr, + JSMSG_READABLESTREAM_BYOB_READER_FOR_NON_BYTE_STREAM); + + // Step 3: If ! IsReadableStreamLocked(stream) is true, throw a TypeError + // exception. + // Step 4: Perform ! ReadableStreamReaderGenericInitialize(this, stream). + // Step 5: Set this.[[readIntoRequests]] to a new empty List. + // Steps 3-5 are presently unreachable. + return nullptr; +} diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamController.h b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamController.h new file mode 100644 index 00000000000..df347356d62 --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamController.h @@ -0,0 +1,266 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* ReadableStream controller classes and functions. */ + +#ifndef builtin_streams_ReadableStreamController_h +#define builtin_streams_ReadableStreamController_h + +#include "mozilla/Assertions.h" // MOZ_ASSERT + +#include // uint32_t + +#include "builtin/streams/ReadableStream.h" // js::ReadableStream +#include "builtin/streams/StreamController.h" // js::StreamController +#include "js/Class.h" // JSClass, js::ClassSpec +#include "js/RootingAPI.h" // JS::Handle +#include "js/Stream.h" // JS::ReadableStreamUnderlyingSource +#include "js/Value.h" // JS::Value, JS::{Number,Object,Private,Undefined}Value, JS::UndefinedHandleValue +#include "vm/List.h" // js::ListObject +#include "vm/NativeObject.h" // js::NativeObject + +namespace js { + +class PromiseObject; + +class ReadableStreamController : public StreamController { + public: + /** + * Memory layout for ReadableStream controllers, starting after the slots + * reserved for queue container usage. + * + * Storage of the internal slots listed in the standard is fairly + * straightforward except for [[pullAlgorithm]] and [[cancelAlgorithm]]. + * These algorithms are not stored as JSFunction objects. Rather, there are + * three cases: + * + * - Streams created with `new ReadableStream`: The methods are stored + * in Slot_PullMethod and Slot_CancelMethod. The underlying source + * object (`this` for these methods) is in Slot_UnderlyingSource. + * + * - External source streams. Slot_UnderlyingSource is a PrivateValue + * pointing to the JS::ReadableStreamUnderlyingSource object. The + * algorithms are implemented using the .pull() and .cancel() methods + * of that object. Slot_Pull/CancelMethod are undefined. + * + * - Tee streams. Slot_UnderlyingSource is a TeeState object. The + * pull/cancel algorithms are implemented as separate functions in + * Stream.cpp. Slot_Pull/CancelMethod are undefined. + * + * UnderlyingSource, PullMethod, and CancelMethod can be wrappers to objects + * in other compartments. + * + * StrategyHWM and Flags are both primitive (numeric) values. + */ + enum Slots { + Slot_Stream = StreamController::SlotCount, + Slot_UnderlyingSource, + Slot_PullMethod, + Slot_CancelMethod, + Slot_StrategyHWM, + Slot_Flags, + SlotCount + }; + + enum ControllerFlags { + Flag_Started = 1 << 0, + Flag_Pulling = 1 << 1, + Flag_PullAgain = 1 << 2, + Flag_CloseRequested = 1 << 3, + Flag_TeeBranch1 = 1 << 4, + Flag_TeeBranch2 = 1 << 5, + Flag_ExternalSource = 1 << 6, + Flag_SourceLocked = 1 << 7, + }; + + ReadableStream* stream() const { + return &getFixedSlot(Slot_Stream).toObject().as(); + } + void setStream(ReadableStream* stream) { + setFixedSlot(Slot_Stream, JS::ObjectValue(*stream)); + } + JS::Value underlyingSource() const { + return getFixedSlot(Slot_UnderlyingSource); + } + void setUnderlyingSource(const JS::Value& underlyingSource) { + setFixedSlot(Slot_UnderlyingSource, underlyingSource); + } + JS::Value pullMethod() const { return getFixedSlot(Slot_PullMethod); } + void setPullMethod(const JS::Value& pullMethod) { + setFixedSlot(Slot_PullMethod, pullMethod); + } + JS::Value cancelMethod() const { return getFixedSlot(Slot_CancelMethod); } + void setCancelMethod(const JS::Value& cancelMethod) { + setFixedSlot(Slot_CancelMethod, cancelMethod); + } + JS::ReadableStreamUnderlyingSource* externalSource() const { + static_assert(alignof(JS::ReadableStreamUnderlyingSource) >= 2, + "External underling sources are stored as PrivateValues, " + "so they must have even addresses"); + MOZ_ASSERT(hasExternalSource()); + return static_cast( + underlyingSource().toPrivate()); + } + void setExternalSource(JS::ReadableStreamUnderlyingSource* underlyingSource) { + setUnderlyingSource(JS::PrivateValue(underlyingSource)); + addFlags(Flag_ExternalSource); + } + static void clearUnderlyingSource( + JS::Handle controller, + bool finalizeSource = true) { + if (controller->hasExternalSource()) { + if (finalizeSource) { + controller->externalSource()->finalize(); + } + controller->setFlags(controller->flags() & ~Flag_ExternalSource); + } + controller->setUnderlyingSource(JS::UndefinedHandleValue); + } + double strategyHWM() const { + return getFixedSlot(Slot_StrategyHWM).toNumber(); + } + void setStrategyHWM(double highWaterMark) { + setFixedSlot(Slot_StrategyHWM, NumberValue(highWaterMark)); + } + uint32_t flags() const { return getFixedSlot(Slot_Flags).toInt32(); } + void setFlags(uint32_t flags) { setFixedSlot(Slot_Flags, Int32Value(flags)); } + void addFlags(uint32_t flags) { setFlags(this->flags() | flags); } + void removeFlags(uint32_t flags) { setFlags(this->flags() & ~flags); } + bool started() const { return flags() & Flag_Started; } + void setStarted() { addFlags(Flag_Started); } + bool pulling() const { return flags() & Flag_Pulling; } + void setPulling() { addFlags(Flag_Pulling); } + void clearPullFlags() { removeFlags(Flag_Pulling | Flag_PullAgain); } + bool pullAgain() const { return flags() & Flag_PullAgain; } + void setPullAgain() { addFlags(Flag_PullAgain); } + bool closeRequested() const { return flags() & Flag_CloseRequested; } + void setCloseRequested() { addFlags(Flag_CloseRequested); } + bool isTeeBranch1() const { return flags() & Flag_TeeBranch1; } + void setTeeBranch1() { + MOZ_ASSERT(!isTeeBranch2()); + addFlags(Flag_TeeBranch1); + } + bool isTeeBranch2() const { return flags() & Flag_TeeBranch2; } + void setTeeBranch2() { + MOZ_ASSERT(!isTeeBranch1()); + addFlags(Flag_TeeBranch2); + } + bool hasExternalSource() const { return flags() & Flag_ExternalSource; } + bool sourceLocked() const { return flags() & Flag_SourceLocked; } + void setSourceLocked() { addFlags(Flag_SourceLocked); } + void clearSourceLocked() { removeFlags(Flag_SourceLocked); } +}; + +class ReadableStreamDefaultController : public ReadableStreamController { + private: + /** + * Memory layout for ReadableStreamDefaultControllers, starting after the + * slots shared among all types of controllers. + * + * StrategySize is treated as an opaque value when stored. The only use site + * ensures that it's wrapped into the current cx compartment. + */ + enum Slots { + Slot_StrategySize = ReadableStreamController::SlotCount, + SlotCount + }; + + public: + JS::Value strategySize() const { return getFixedSlot(Slot_StrategySize); } + void setStrategySize(const JS::Value& size) { + setFixedSlot(Slot_StrategySize, size); + } + + static bool constructor(JSContext* cx, unsigned argc, JS::Value* vp); + static const ClassSpec classSpec_; + static const JSClass class_; + static const ClassSpec protoClassSpec_; + static const JSClass protoClass_; +}; + +class ReadableByteStreamController : public ReadableStreamController { + public: + /** + * Memory layout for ReadableByteStreamControllers, starting after the + * slots shared among all types of controllers. + * + * PendingPullIntos is guaranteed to be in the same compartment as the + * controller, but might contain wrappers for objects from other + * compartments. + * + * AutoAllocateSize is a primitive (numeric) value. + */ + enum Slots { + Slot_BYOBRequest = ReadableStreamController::SlotCount, + Slot_PendingPullIntos, + Slot_AutoAllocateSize, + SlotCount + }; + + JS::Value byobRequest() const { return getFixedSlot(Slot_BYOBRequest); } + void clearBYOBRequest() { + setFixedSlot(Slot_BYOBRequest, JS::UndefinedValue()); + } + ListObject* pendingPullIntos() const { + return &getFixedSlot(Slot_PendingPullIntos).toObject().as(); + } + JS::Value autoAllocateChunkSize() const { + return getFixedSlot(Slot_AutoAllocateSize); + } + void setAutoAllocateChunkSize(const JS::Value& size) { + setFixedSlot(Slot_AutoAllocateSize, size); + } + + static bool constructor(JSContext* cx, unsigned argc, JS::Value* vp); + static const ClassSpec classSpec_; + static const JSClass class_; + static const ClassSpec protoClassSpec_; + static const JSClass protoClass_; +}; + +[[nodiscard]] extern bool CheckReadableStreamControllerCanCloseOrEnqueue( + JSContext* cx, JS::Handle unwrappedController, + const char* action); + +[[nodiscard]] extern JSObject* ReadableStreamControllerCancelSteps( + JSContext* cx, JS::Handle unwrappedController, + JS::Handle reason); + +extern PromiseObject* ReadableStreamDefaultControllerPullSteps( + JSContext* cx, + JS::Handle unwrappedController); + +extern bool ReadableStreamControllerStartHandler(JSContext* cx, unsigned argc, + JS::Value* vp); + +extern bool ReadableStreamControllerStartFailedHandler(JSContext* cx, + unsigned argc, + JS::Value* vp); + +} // namespace js + +template <> +inline bool JSObject::is() const { + return is() || + is(); +} + +namespace js { + +inline ReadableStreamController* ReadableStream::controller() const { + return &getFixedSlot(Slot_Controller) + .toObject() + .as(); +} + +inline void ReadableStream::setController( + ReadableStreamController* controller) { + setFixedSlot(Slot_Controller, JS::ObjectValue(*controller)); +} + +} // namespace js + +#endif // builtin_streams_ReadableStreamController_h diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamDefaultController.cpp b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamDefaultController.cpp new file mode 100644 index 00000000000..7589373e2d6 --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamDefaultController.cpp @@ -0,0 +1,513 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Class ReadableStreamDefaultController. */ + +#include "mozilla/Assertions.h" // MOZ_ASSERT{,_IF} + +#include "jsfriendapi.h" // js::AssertSameCompartment + +#include "builtin/streams/ClassSpecMacro.h" // JS_STREAMS_CLASS_SPEC +#include "builtin/streams/MiscellaneousOperations.h" // js::IsMaybeWrapped +#include "builtin/streams/PullIntoDescriptor.h" // js::PullIntoDescriptor +#include "builtin/streams/QueueWithSizes.h" // js::{DequeueValue,ResetQueue} +#include "builtin/streams/ReadableStream.h" // js::ReadableStream, js::SetUpExternalReadableByteStreamController +#include "builtin/streams/ReadableStreamController.h" // js::ReadableStream{,Default}Controller, js::ReadableByteStreamController, js::CheckReadableStreamControllerCanCloseOrEnqueue, js::ReadableStreamControllerCancelSteps, js::ReadableStreamDefaultControllerPullSteps, js::ReadableStreamControllerStart{,Failed}Handler +#include "builtin/streams/ReadableStreamDefaultControllerOperations.h" // js::ReadableStreamController{CallPullIfNeeded,ClearAlgorithms,Error,GetDesiredSizeUnchecked}, js::ReadableStreamDefaultController{Close,Enqueue} +#include "builtin/streams/ReadableStreamInternals.h" // js::ReadableStream{AddReadOrReadIntoRequest,CloseInternal,CreateReadResult} +#include "builtin/streams/ReadableStreamOperations.h" // js::ReadableStreamTee_Cancel +#include "builtin/streams/ReadableStreamReader.h" // js::ReadableStream{,Default}Reader +#include "builtin/streams/StreamController.h" // js::StreamController +#include "builtin/streams/TeeState.h" // js::TeeState +#include "js/ArrayBuffer.h" // JS::NewArrayBuffer +#include "js/Class.h" // js::ClassSpec +#include "js/ErrorReport.h" // JS_ReportErrorNumberASCII +#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* +#include "js/PropertySpec.h" +#include "vm/Interpreter.h" +#include "vm/JSContext.h" +#include "vm/PlainObject.h" // js::PlainObject +#include "vm/PromiseObject.h" // js::PromiseObject, js::PromiseResolvedWithUndefined +#include "vm/SelfHosting.h" + +#include "builtin/HandlerFunction-inl.h" // js::TargetFromHandler +#include "builtin/streams/MiscellaneousOperations-inl.h" // js::PromiseCall +#include "builtin/streams/ReadableStreamReader-inl.h" // js::UnwrapReaderFromStream +#include "vm/Compartment-inl.h" // JS::Compartment::wrap, js::UnwrapAnd{DowncastObject,TypeCheckThis} +#include "vm/JSContext-inl.h" // JSContext::check +#include "vm/Realm-inl.h" // js::AutoRealm + +using js::ClassSpec; +using js::PromiseObject; +using js::ReadableStream; +using js::ReadableStreamController; +using js::ReadableStreamControllerCallPullIfNeeded; +using js::ReadableStreamControllerClearAlgorithms; +using js::ReadableStreamControllerError; +using js::ReadableStreamControllerGetDesiredSizeUnchecked; +using js::ReadableStreamDefaultController; +using js::ReadableStreamDefaultControllerClose; +using js::ReadableStreamDefaultControllerEnqueue; +using js::TargetFromHandler; +using js::UnwrapAndTypeCheckThis; + +using JS::CallArgs; +using JS::CallArgsFromVp; +using JS::Handle; +using JS::ObjectValue; +using JS::Rooted; +using JS::Value; + +/*** 3.9. Class ReadableStreamDefaultController *****************************/ + +/** + * Streams spec, 3.10.11. SetUpReadableStreamDefaultController, step 11 + * and + * Streams spec, 3.13.26. SetUpReadableByteStreamController, step 16: + * Upon fulfillment of startPromise, [...] + */ +bool js::ReadableStreamControllerStartHandler(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + Rooted controller( + cx, TargetFromHandler(args)); + + // Step a: Set controller.[[started]] to true. + controller->setStarted(); + + // Step b: Assert: controller.[[pulling]] is false. + MOZ_ASSERT(!controller->pulling()); + + // Step c: Assert: controller.[[pullAgain]] is false. + MOZ_ASSERT(!controller->pullAgain()); + + // Step d: Perform + // ! ReadableStreamDefaultControllerCallPullIfNeeded(controller) + // (or ReadableByteStreamControllerCallPullIfNeeded(controller)). + if (!ReadableStreamControllerCallPullIfNeeded(cx, controller)) { + return false; + } + args.rval().setUndefined(); + return true; +} + +/** + * Streams spec, 3.10.11. SetUpReadableStreamDefaultController, step 12 + * and + * Streams spec, 3.13.26. SetUpReadableByteStreamController, step 17: + * Upon rejection of startPromise with reason r, [...] + */ +bool js::ReadableStreamControllerStartFailedHandler(JSContext* cx, + unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + Rooted controller( + cx, TargetFromHandler(args)); + + // Step a: Perform + // ! ReadableStreamDefaultControllerError(controller, r) + // (or ReadableByteStreamControllerError(controller, r)). + if (!ReadableStreamControllerError(cx, controller, args.get(0))) { + return false; + } + + args.rval().setUndefined(); + return true; +} + +/** + * Streams spec, 3.9.3. + * new ReadableStreamDefaultController( stream, underlyingSource, size, + * highWaterMark ) + */ +bool ReadableStreamDefaultController::constructor(JSContext* cx, unsigned argc, + Value* vp) { + // Step 1: Throw a TypeError. + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_BOGUS_CONSTRUCTOR, + "ReadableStreamDefaultController"); + return false; +} + +/** + * Streams spec, 3.9.4.1. get desiredSize + */ +static bool ReadableStreamDefaultController_desiredSize(JSContext* cx, + unsigned argc, + Value* vp) { + // Step 1: If ! IsReadableStreamDefaultController(this) is false, throw a + // TypeError exception. + CallArgs args = CallArgsFromVp(argc, vp); + Rooted unwrappedController( + cx, UnwrapAndTypeCheckThis( + cx, args, "get desiredSize")); + if (!unwrappedController) { + return false; + } + + // 3.10.8. ReadableStreamDefaultControllerGetDesiredSize, steps 1-4. + // 3.10.8. Step 1: Let stream be controller.[[controlledReadableStream]]. + ReadableStream* unwrappedStream = unwrappedController->stream(); + + // 3.10.8. Step 2: Let state be stream.[[state]]. + // 3.10.8. Step 3: If state is "errored", return null. + if (unwrappedStream->errored()) { + args.rval().setNull(); + return true; + } + + // 3.10.8. Step 4: If state is "closed", return 0. + if (unwrappedStream->closed()) { + args.rval().setInt32(0); + return true; + } + + // Step 2: Return ! ReadableStreamDefaultControllerGetDesiredSize(this). + args.rval().setNumber( + ReadableStreamControllerGetDesiredSizeUnchecked(unwrappedController)); + return true; +} + +/** + * Unified implementation of step 2 of 3.9.4.2 and 3.9.4.3, + * and steps 2-3 of 3.11.4.3. + */ +[[nodiscard]] bool js::CheckReadableStreamControllerCanCloseOrEnqueue( + JSContext* cx, Handle unwrappedController, + const char* action) { + // 3.9.4.2. close(), step 2, and + // 3.9.4.3. enqueue(chunk), step 2: + // If ! ReadableStreamDefaultControllerCanCloseOrEnqueue(this) is false, + // throw a TypeError exception. + // RSDCCanCloseOrEnqueue returns false in two cases: (1) + // controller.[[closeRequested]] is true; (2) the stream is not readable, + // i.e. already closed or errored. This amounts to exactly the same thing as + // 3.11.4.3 steps 2-3 below, and we want different error messages for the two + // cases anyway. + + // 3.11.4.3. Step 2: If this.[[closeRequested]] is true, throw a TypeError + // exception. + if (unwrappedController->closeRequested()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_READABLESTREAMCONTROLLER_CLOSED, action); + return false; + } + + // 3.11.4.3. Step 3: If this.[[controlledReadableByteStream]].[[state]] is + // not "readable", throw a TypeError exception. + ReadableStream* unwrappedStream = unwrappedController->stream(); + if (!unwrappedStream->readable()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE, + action); + return false; + } + + return true; +} + +/** + * Streams spec, 3.9.4.2 close() + */ +static bool ReadableStreamDefaultController_close(JSContext* cx, unsigned argc, + Value* vp) { + // Step 1: If ! IsReadableStreamDefaultController(this) is false, throw a + // TypeError exception. + CallArgs args = CallArgsFromVp(argc, vp); + Rooted unwrappedController( + cx, UnwrapAndTypeCheckThis(cx, args, + "close")); + if (!unwrappedController) { + return false; + } + + // Step 2: If ! ReadableStreamDefaultControllerCanCloseOrEnqueue(this) is + // false, throw a TypeError exception. + if (!CheckReadableStreamControllerCanCloseOrEnqueue(cx, unwrappedController, + "close")) { + return false; + } + + // Step 3: Perform ! ReadableStreamDefaultControllerClose(this). + if (!ReadableStreamDefaultControllerClose(cx, unwrappedController)) { + return false; + } + + args.rval().setUndefined(); + return true; +} + +/** + * Streams spec, 3.9.4.3. enqueue ( chunk ) + */ +static bool ReadableStreamDefaultController_enqueue(JSContext* cx, + unsigned argc, Value* vp) { + // Step 1: If ! IsReadableStreamDefaultController(this) is false, throw a + // TypeError exception. + CallArgs args = CallArgsFromVp(argc, vp); + Rooted unwrappedController( + cx, UnwrapAndTypeCheckThis(cx, args, + "enqueue")); + if (!unwrappedController) { + return false; + } + + // Step 2: If ! ReadableStreamDefaultControllerCanCloseOrEnqueue(this) is + // false, throw a TypeError exception. + if (!CheckReadableStreamControllerCanCloseOrEnqueue(cx, unwrappedController, + "enqueue")) { + return false; + } + + // Step 3: Return ! ReadableStreamDefaultControllerEnqueue(this, chunk). + if (!ReadableStreamDefaultControllerEnqueue(cx, unwrappedController, + args.get(0))) { + return false; + } + args.rval().setUndefined(); + return true; +} + +/** + * Streams spec, 3.9.4.4. error ( e ) + */ +static bool ReadableStreamDefaultController_error(JSContext* cx, unsigned argc, + Value* vp) { + // Step 1: If ! IsReadableStreamDefaultController(this) is false, throw a + // TypeError exception. + CallArgs args = CallArgsFromVp(argc, vp); + Rooted unwrappedController( + cx, UnwrapAndTypeCheckThis(cx, args, + "enqueue")); + if (!unwrappedController) { + return false; + } + + // Step 2: Perform ! ReadableStreamDefaultControllerError(this, e). + if (!ReadableStreamControllerError(cx, unwrappedController, args.get(0))) { + return false; + } + + args.rval().setUndefined(); + return true; +} + +static const JSPropertySpec ReadableStreamDefaultController_properties[] = { + JS_PSG("desiredSize", ReadableStreamDefaultController_desiredSize, 0), + JS_PS_END}; + +static const JSFunctionSpec ReadableStreamDefaultController_methods[] = { + JS_FN("close", ReadableStreamDefaultController_close, 0, 0), + JS_FN("enqueue", ReadableStreamDefaultController_enqueue, 1, 0), + JS_FN("error", ReadableStreamDefaultController_error, 1, 0), JS_FS_END}; + +JS_STREAMS_CLASS_SPEC(ReadableStreamDefaultController, 0, SlotCount, + ClassSpec::DontDefineConstructor, 0, JS_NULL_CLASS_OPS); + +/** + * Unified implementation of ReadableStream controllers' [[CancelSteps]] + * internal methods. + * Streams spec, 3.9.5.1. [[CancelSteps]] ( reason ) + * and + * Streams spec, 3.11.5.1. [[CancelSteps]] ( reason ) + */ +[[nodiscard]] JSObject* js::ReadableStreamControllerCancelSteps( + JSContext* cx, Handle unwrappedController, + Handle reason) { + AssertSameCompartment(cx, reason); + + // Step 1 of 3.11.5.1: If this.[[pendingPullIntos]] is not empty, + if (!unwrappedController->is()) { + Rooted unwrappedPendingPullIntos( + cx, unwrappedController->as() + .pendingPullIntos()); + + if (unwrappedPendingPullIntos->length() != 0) { + // Step a: Let firstDescriptor be the first element of + // this.[[pendingPullIntos]]. + PullIntoDescriptor* unwrappedDescriptor = + UnwrapAndDowncastObject( + cx, &unwrappedPendingPullIntos->get(0).toObject()); + if (!unwrappedDescriptor) { + return nullptr; + } + + // Step b: Set firstDescriptor.[[bytesFilled]] to 0. + unwrappedDescriptor->setBytesFilled(0); + } + } + + Rooted unwrappedUnderlyingSource( + cx, unwrappedController->underlyingSource()); + + // Step 1 of 3.9.5.1, step 2 of 3.11.5.1: Perform ! ResetQueue(this). + if (!ResetQueue(cx, unwrappedController)) { + return nullptr; + } + + // Step 2 of 3.9.5.1, step 3 of 3.11.5.1: Let result be the result of + // performing this.[[cancelAlgorithm]], passing reason. + // + // Our representation of cancel algorithms is a bit awkward, for + // performance, so we must figure out which algorithm is being invoked. + Rooted result(cx); + if (IsMaybeWrapped(unwrappedUnderlyingSource)) { + // The cancel algorithm given in ReadableStreamTee step 13 or 14. + MOZ_ASSERT(unwrappedUnderlyingSource.toObject().is(), + "tee streams and controllers are always same-compartment with " + "the TeeState object"); + Rooted unwrappedTeeState( + cx, &unwrappedUnderlyingSource.toObject().as()); + Rooted unwrappedDefaultController( + cx, &unwrappedController->as()); + result = ReadableStreamTee_Cancel(cx, unwrappedTeeState, + unwrappedDefaultController, reason); + } else if (unwrappedController->hasExternalSource()) { + // An embedding-provided cancel algorithm. + Rooted rval(cx); + { + AutoRealm ar(cx, unwrappedController); + JS::ReadableStreamUnderlyingSource* source = + unwrappedController->externalSource(); + Rooted stream(cx, unwrappedController->stream()); + Rooted wrappedReason(cx, reason); + if (!cx->compartment()->wrap(cx, &wrappedReason)) { + return nullptr; + } + + cx->check(stream, wrappedReason); + rval = source->cancel(cx, stream, wrappedReason); + } + + // Make sure the ReadableStreamControllerClearAlgorithms call below is + // reached, even on error. + if (!cx->compartment()->wrap(cx, &rval)) { + result = nullptr; + } else { + result = PromiseObject::unforgeableResolve(cx, rval); + } + } else { + // The algorithm created in + // SetUpReadableByteStreamControllerFromUnderlyingSource step 5. + Rooted unwrappedCancelMethod(cx, + unwrappedController->cancelMethod()); + if (unwrappedCancelMethod.isUndefined()) { + // CreateAlgorithmFromUnderlyingMethod step 7. + result = PromiseResolvedWithUndefined(cx); + } else { + // CreateAlgorithmFromUnderlyingMethod steps 6.c.i-ii. + { + AutoRealm ar(cx, unwrappedController); + + // |unwrappedCancelMethod| and |unwrappedUnderlyingSource| come directly + // from |unwrappedController| slots so must be same-compartment with it. + cx->check(unwrappedCancelMethod); + cx->check(unwrappedUnderlyingSource); + + Rooted wrappedReason(cx, reason); + if (!cx->compartment()->wrap(cx, &wrappedReason)) { + return nullptr; + } + + // If PromiseCall fails, don't bail out until after the + // ReadableStreamControllerClearAlgorithms call below. + result = PromiseCall(cx, unwrappedCancelMethod, + unwrappedUnderlyingSource, wrappedReason); + } + if (!cx->compartment()->wrap(cx, &result)) { + result = nullptr; + } + } + } + + // Step 3 (or 4): Perform + // ! ReadableStreamDefaultControllerClearAlgorithms(this) + // (or ReadableByteStreamControllerClearAlgorithms(this)). + ReadableStreamControllerClearAlgorithms(unwrappedController); + + // Step 4 (or 5): Return result. + return result; +} + +/** + * Streams spec, 3.9.5.2. + * ReadableStreamDefaultController [[PullSteps]]( forAuthorCode ) + */ +PromiseObject* js::ReadableStreamDefaultControllerPullSteps( + JSContext* cx, + Handle unwrappedController) { + // Step 1: Let stream be this.[[controlledReadableStream]]. + Rooted unwrappedStream(cx, unwrappedController->stream()); + + // Step 2: If this.[[queue]] is not empty, + Rooted unwrappedQueue(cx); + Rooted val( + cx, unwrappedController->getFixedSlot(StreamController::Slot_Queue)); + if (val.isObject()) { + unwrappedQueue = &val.toObject().as(); + } + + if (unwrappedQueue && unwrappedQueue->length() != 0) { + // Step a: Let chunk be ! DequeueValue(this). + Rooted chunk(cx); + if (!DequeueValue(cx, unwrappedController, &chunk)) { + return nullptr; + } + + // Step b: If this.[[closeRequested]] is true and this.[[queue]] is empty, + if (unwrappedController->closeRequested() && + unwrappedQueue->length() == 0) { + // Step i: Perform ! ReadableStreamDefaultControllerClearAlgorithms(this). + ReadableStreamControllerClearAlgorithms(unwrappedController); + + // Step ii: Perform ! ReadableStreamClose(stream). + if (!ReadableStreamCloseInternal(cx, unwrappedStream)) { + return nullptr; + } + } + + // Step c: Otherwise, perform + // ! ReadableStreamDefaultControllerCallPullIfNeeded(this). + else { + if (!ReadableStreamControllerCallPullIfNeeded(cx, unwrappedController)) { + return nullptr; + } + } + + // Step d: Return a promise resolved with + // ! ReadableStreamCreateReadResult(chunk, false, forAuthorCode). + cx->check(chunk); + ReadableStreamReader* unwrappedReader = + UnwrapReaderFromStream(cx, unwrappedStream); + if (!unwrappedReader) { + return nullptr; + } + + PlainObject* readResultObj = ReadableStreamCreateReadResult( + cx, chunk, false, unwrappedReader->forAuthorCode()); + if (!readResultObj) { + return nullptr; + } + + Rooted readResult(cx, ObjectValue(*readResultObj)); + return PromiseObject::unforgeableResolveWithNonPromise(cx, readResult); + } + + // Step 3: Let pendingPromise be + // ! ReadableStreamAddReadRequest(stream, forAuthorCode). + Rooted pendingPromise( + cx, ReadableStreamAddReadOrReadIntoRequest(cx, unwrappedStream)); + if (!pendingPromise) { + return nullptr; + } + + // Step 4: Perform ! ReadableStreamDefaultControllerCallPullIfNeeded(this). + if (!ReadableStreamControllerCallPullIfNeeded(cx, unwrappedController)) { + return nullptr; + } + + // Step 5: Return pendingPromise. + return pendingPromise; +} diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamDefaultControllerOperations.cpp b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamDefaultControllerOperations.cpp new file mode 100644 index 00000000000..cf7debed02f --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamDefaultControllerOperations.cpp @@ -0,0 +1,682 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Readable stream default controller abstract operations. */ + +#include "builtin/streams/ReadableStreamDefaultControllerOperations.h" + +#include "mozilla/Assertions.h" // MOZ_ASSERT{,_IF} + +#include "jsfriendapi.h" // js::AssertSameCompartment + +#include "builtin/Stream.h" // js::ReadableByteStreamControllerClearPendingPullIntos +#include "builtin/streams/MiscellaneousOperations.h" // js::CreateAlgorithmFromUnderlyingMethod, js::InvokeOrNoop, js::IsMaybeWrapped +#include "builtin/streams/QueueWithSizes.h" // js::EnqueueValueWithSize, js::ResetQueue +#include "builtin/streams/ReadableStreamController.h" // js::ReadableStream{,Default}Controller, js::ReadableByteStreamController, js::ReadableStreamControllerStart{,Failed}Handler +#include "builtin/streams/ReadableStreamInternals.h" // js::ReadableStream{CloseInternal,ErrorInternal,FulfillReadOrReadIntoRequest,GetNumReadRequests} +#include "builtin/streams/ReadableStreamOperations.h" // js::ReadableStreamTee_Pull, js::SetUpReadableStreamDefaultController +#include "builtin/streams/TeeState.h" // js::TeeState +#include "js/CallAndConstruct.h" // JS::IsCallable +#include "js/CallArgs.h" // JS::CallArgs{,FromVp} +#include "js/Promise.h" // JS::AddPromiseReactions +#include "js/RootingAPI.h" // JS::Handle, JS::Rooted +#include "js/Stream.h" // JS::ReadableStreamUnderlyingSource +#include "js/Value.h" // JS::{,Int32,Object}Value, JS::UndefinedHandleValue +#include "vm/Compartment.h" // JS::Compartment +#include "vm/Interpreter.h" // js::Call, js::GetAndClearExceptionAndStack +#include "vm/JSContext.h" // JSContext +#include "vm/JSObject.h" // JSObject +#include "vm/List.h" // js::ListObject +#include "vm/PromiseObject.h" // js::PromiseObject, js::PromiseResolvedWithUndefined +#include "vm/Runtime.h" // JSAtomState +#include "vm/SavedFrame.h" // js::SavedFrame + +#include "builtin/HandlerFunction-inl.h" // js::NewHandler +#include "builtin/streams/MiscellaneousOperations-inl.h" // js::PromiseCall +#include "vm/Compartment-inl.h" // JS::Compartment::wrap, js::UnwrapCalleeSlot +#include "vm/JSContext-inl.h" // JSContext::check +#include "vm/JSObject-inl.h" // js::IsCallable, js::NewBuiltinClassInstance +#include "vm/Realm-inl.h" // js::AutoRealm + +using js::ReadableByteStreamController; +using js::ReadableStream; +using js::ReadableStreamController; +using js::ReadableStreamControllerCallPullIfNeeded; +using js::ReadableStreamControllerError; +using js::ReadableStreamGetNumReadRequests; +using js::UnwrapCalleeSlot; + +using JS::CallArgs; +using JS::CallArgsFromVp; +using JS::Handle; +using JS::Rooted; +using JS::UndefinedHandleValue; +using JS::Value; + +/*** 3.10. Readable stream default controller abstract operations ***********/ + +// Streams spec, 3.10.1. IsReadableStreamDefaultController ( x ) +// Implemented via is() + +/** + * Streams spec, 3.10.2 and 3.13.3. step 7: + * Upon fulfillment of pullPromise, [...] + */ +static bool ControllerPullHandler(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + Rooted unwrappedController( + cx, UnwrapCalleeSlot(cx, args, 0)); + if (!unwrappedController) { + return false; + } + + bool pullAgain = unwrappedController->pullAgain(); + + // Step a: Set controller.[[pulling]] to false. + // Step b.i: Set controller.[[pullAgain]] to false. + unwrappedController->clearPullFlags(); + + // Step b: If controller.[[pullAgain]] is true, + if (pullAgain) { + // Step ii: Perform + // ! ReadableStreamDefaultControllerCallPullIfNeeded(controller) + // (or ReadableByteStreamControllerCallPullIfNeeded(controller)). + if (!ReadableStreamControllerCallPullIfNeeded(cx, unwrappedController)) { + return false; + } + } + + args.rval().setUndefined(); + return true; +} + +/** + * Streams spec, 3.10.2 and 3.13.3. step 8: + * Upon rejection of pullPromise with reason e, + */ +static bool ControllerPullFailedHandler(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + Handle e = args.get(0); + + Rooted controller( + cx, UnwrapCalleeSlot(cx, args, 0)); + if (!controller) { + return false; + } + + // Step a: Perform ! ReadableStreamDefaultControllerError(controller, e). + // (ReadableByteStreamControllerError in 3.12.3.) + if (!ReadableStreamControllerError(cx, controller, e)) { + return false; + } + + args.rval().setUndefined(); + return true; +} + +static bool ReadableStreamControllerShouldCallPull( + ReadableStreamController* unwrappedController); + +/** + * Streams spec, 3.10.2 + * ReadableStreamDefaultControllerCallPullIfNeeded ( controller ) + * Streams spec, 3.13.3. + * ReadableByteStreamControllerCallPullIfNeeded ( controller ) + */ +[[nodiscard]] bool js::ReadableStreamControllerCallPullIfNeeded( + JSContext* cx, Handle unwrappedController) { + // Step 1: Let shouldPull be + // ! ReadableStreamDefaultControllerShouldCallPull(controller). + // (ReadableByteStreamDefaultControllerShouldCallPull in 3.13.3.) + bool shouldPull = ReadableStreamControllerShouldCallPull(unwrappedController); + + // Step 2: If shouldPull is false, return. + if (!shouldPull) { + return true; + } + + // Step 3: If controller.[[pulling]] is true, + if (unwrappedController->pulling()) { + // Step a: Set controller.[[pullAgain]] to true. + unwrappedController->setPullAgain(); + + // Step b: Return. + return true; + } + + // Step 4: Assert: controller.[[pullAgain]] is false. + MOZ_ASSERT(!unwrappedController->pullAgain()); + + // Step 5: Set controller.[[pulling]] to true. + unwrappedController->setPulling(); + + // We use this variable in step 7. For ease of error-handling, we wrap it + // early. + Rooted wrappedController(cx, unwrappedController); + if (!cx->compartment()->wrap(cx, &wrappedController)) { + return false; + } + + // Step 6: Let pullPromise be the result of performing + // controller.[[pullAlgorithm]]. + // Our representation of pull algorithms is a bit awkward, for performance, + // so we must figure out which algorithm is being invoked. + Rooted pullPromise(cx); + Rooted unwrappedUnderlyingSource( + cx, unwrappedController->underlyingSource()); + + if (IsMaybeWrapped(unwrappedUnderlyingSource)) { + // The pull algorithm given in ReadableStreamTee step 12. + MOZ_ASSERT(unwrappedUnderlyingSource.toObject().is(), + "tee streams and controllers are always same-compartment with " + "the TeeState object"); + Rooted unwrappedTeeState( + cx, &unwrappedUnderlyingSource.toObject().as()); + pullPromise = ReadableStreamTee_Pull(cx, unwrappedTeeState); + } else if (unwrappedController->hasExternalSource()) { + // An embedding-provided pull algorithm. + { + AutoRealm ar(cx, unwrappedController); + JS::ReadableStreamUnderlyingSource* source = + unwrappedController->externalSource(); + Rooted stream(cx, unwrappedController->stream()); + double desiredSize = + ReadableStreamControllerGetDesiredSizeUnchecked(unwrappedController); + source->requestData(cx, stream, desiredSize); + } + pullPromise = PromiseResolvedWithUndefined(cx); + } else { + // The pull algorithm created in + // SetUpReadableStreamDefaultControllerFromUnderlyingSource step 4. + Rooted unwrappedPullMethod(cx, unwrappedController->pullMethod()); + if (unwrappedPullMethod.isUndefined()) { + // CreateAlgorithmFromUnderlyingMethod step 7. + pullPromise = PromiseResolvedWithUndefined(cx); + } else { + // CreateAlgorithmFromUnderlyingMethod step 6.b.i. + { + AutoRealm ar(cx, unwrappedController); + + // |unwrappedPullMethod| and |unwrappedUnderlyingSource| come directly + // from |unwrappedController| slots so must be same-compartment with it. + cx->check(unwrappedPullMethod); + cx->check(unwrappedUnderlyingSource); + + Rooted controller(cx, ObjectValue(*unwrappedController)); + cx->check(controller); + + pullPromise = PromiseCall(cx, unwrappedPullMethod, + unwrappedUnderlyingSource, controller); + if (!pullPromise) { + return false; + } + } + if (!cx->compartment()->wrap(cx, &pullPromise)) { + return false; + } + } + } + if (!pullPromise) { + return false; + } + + // Step 7: Upon fulfillment of pullPromise, [...] + // Step 8. Upon rejection of pullPromise with reason e, [...] + Rooted onPullFulfilled( + cx, NewHandler(cx, ControllerPullHandler, wrappedController)); + if (!onPullFulfilled) { + return false; + } + Rooted onPullRejected( + cx, NewHandler(cx, ControllerPullFailedHandler, wrappedController)); + if (!onPullRejected) { + return false; + } + return JS::AddPromiseReactions(cx, pullPromise, onPullFulfilled, + onPullRejected); +} + +/** + * Streams spec, 3.10.3. + * ReadableStreamDefaultControllerShouldCallPull ( controller ) + * Streams spec, 3.13.25. + * ReadableByteStreamControllerShouldCallPull ( controller ) + */ +static bool ReadableStreamControllerShouldCallPull( + ReadableStreamController* unwrappedController) { + // Step 1: Let stream be controller.[[controlledReadableStream]] + // (or [[controlledReadableByteStream]]). + ReadableStream* unwrappedStream = unwrappedController->stream(); + + // 3.10.3. Step 2: + // If ! ReadableStreamDefaultControllerCanCloseOrEnqueue(controller) + // is false, return false. + // This turns out to be the same as 3.13.25 steps 2-3. + + // 3.13.25 Step 2: If stream.[[state]] is not "readable", return false. + if (!unwrappedStream->readable()) { + return false; + } + + // 3.13.25 Step 3: If controller.[[closeRequested]] is true, return false. + if (unwrappedController->closeRequested()) { + return false; + } + + // Step 3 (or 4): + // If controller.[[started]] is false, return false. + if (!unwrappedController->started()) { + return false; + } + + // 3.10.3. + // Step 4: If ! IsReadableStreamLocked(stream) is true and + // ! ReadableStreamGetNumReadRequests(stream) > 0, return true. + // + // 3.13.25. + // Step 5: If ! ReadableStreamHasDefaultReader(stream) is true and + // ! ReadableStreamGetNumReadRequests(stream) > 0, return true. + // Step 6: If ! ReadableStreamHasBYOBReader(stream) is true and + // ! ReadableStreamGetNumReadIntoRequests(stream) > 0, return true. + // + // All of these amount to the same thing in this implementation: + if (unwrappedStream->locked() && + ReadableStreamGetNumReadRequests(unwrappedStream) > 0) { + return true; + } + + // Step 5 (or 7): + // Let desiredSize be + // ! ReadableStreamDefaultControllerGetDesiredSize(controller). + // (ReadableByteStreamControllerGetDesiredSize in 3.13.25.) + double desiredSize = + ReadableStreamControllerGetDesiredSizeUnchecked(unwrappedController); + + // Step 6 (or 8): Assert: desiredSize is not null (implicit). + // Step 7 (or 9): If desiredSize > 0, return true. + // Step 8 (or 10): Return false. + return desiredSize > 0; +} + +/** + * Streams spec, 3.10.4. + * ReadableStreamDefaultControllerClearAlgorithms ( controller ) + * and 3.13.4. + * ReadableByteStreamControllerClearAlgorithms ( controller ) + */ +void js::ReadableStreamControllerClearAlgorithms( + Handle controller) { + // Step 1: Set controller.[[pullAlgorithm]] to undefined. + // Step 2: Set controller.[[cancelAlgorithm]] to undefined. + // (In this implementation, the UnderlyingSource slot is part of the + // representation of these algorithms.) + controller->setPullMethod(UndefinedHandleValue); + controller->setCancelMethod(UndefinedHandleValue); + ReadableStreamController::clearUnderlyingSource(controller); + + // Step 3 (of 3.10.4 only) : Set controller.[[strategySizeAlgorithm]] to + // undefined. + if (controller->is()) { + controller->as().setStrategySize( + UndefinedHandleValue); + } +} + +/** + * Streams spec, 3.10.5. ReadableStreamDefaultControllerClose ( controller ) + */ +[[nodiscard]] bool js::ReadableStreamDefaultControllerClose( + JSContext* cx, + Handle unwrappedController) { + // Step 1: Let stream be controller.[[controlledReadableStream]]. + Rooted unwrappedStream(cx, unwrappedController->stream()); + + // Step 2: Assert: + // ! ReadableStreamDefaultControllerCanCloseOrEnqueue(controller) + // is true. + MOZ_ASSERT(!unwrappedController->closeRequested()); + MOZ_ASSERT(unwrappedStream->readable()); + + // Step 3: Set controller.[[closeRequested]] to true. + unwrappedController->setCloseRequested(); + + // Step 4: If controller.[[queue]] is empty, + Rooted unwrappedQueue(cx, unwrappedController->queue()); + if (unwrappedQueue->length() == 0) { + // Step a: Perform + // ! ReadableStreamDefaultControllerClearAlgorithms(controller). + ReadableStreamControllerClearAlgorithms(unwrappedController); + + // Step b: Perform ! ReadableStreamClose(stream). + return ReadableStreamCloseInternal(cx, unwrappedStream); + } + + return true; +} + +/** + * Streams spec, 3.10.6. + * ReadableStreamDefaultControllerEnqueue ( controller, chunk ) + */ +[[nodiscard]] bool js::ReadableStreamDefaultControllerEnqueue( + JSContext* cx, Handle unwrappedController, + Handle chunk) { + AssertSameCompartment(cx, chunk); + + // Step 1: Let stream be controller.[[controlledReadableStream]]. + Rooted unwrappedStream(cx, unwrappedController->stream()); + + // Step 2: Assert: + // ! ReadableStreamDefaultControllerCanCloseOrEnqueue(controller) is + // true. + MOZ_ASSERT(!unwrappedController->closeRequested()); + MOZ_ASSERT(unwrappedStream->readable()); + + // Step 3: If ! IsReadableStreamLocked(stream) is true and + // ! ReadableStreamGetNumReadRequests(stream) > 0, perform + // ! ReadableStreamFulfillReadRequest(stream, chunk, false). + if (unwrappedStream->locked() && + ReadableStreamGetNumReadRequests(unwrappedStream) > 0) { + if (!ReadableStreamFulfillReadOrReadIntoRequest(cx, unwrappedStream, chunk, + false)) { + return false; + } + } else { + // Step 4: Otherwise, + // Step a: Let result be the result of performing + // controller.[[strategySizeAlgorithm]], passing in chunk, and + // interpreting the result as an ECMAScript completion value. + // Step c: (on success) Let chunkSize be result.[[Value]]. + Rooted chunkSize(cx, Int32Value(1)); + bool success = true; + Rooted strategySize(cx, unwrappedController->strategySize()); + if (!strategySize.isUndefined()) { + if (!cx->compartment()->wrap(cx, &strategySize)) { + return false; + } + success = Call(cx, strategySize, UndefinedHandleValue, chunk, &chunkSize); + } + + // Step d: Let enqueueResult be + // EnqueueValueWithSize(controller, chunk, chunkSize). + if (success) { + success = EnqueueValueWithSize(cx, unwrappedController, chunk, chunkSize); + } + + // Step b: If result is an abrupt completion, + // and + // Step e: If enqueueResult is an abrupt completion, + if (!success) { + Rooted exn(cx); + Rooted stack(cx); + if (!cx->isExceptionPending() || + !GetAndClearExceptionAndStack(cx, &exn, &stack)) { + // Uncatchable error. Die immediately without erroring the + // stream. + return false; + } + + // Step b.i: Perform ! ReadableStreamDefaultControllerError( + // controller, result.[[Value]]). + // Step e.i: Perform ! ReadableStreamDefaultControllerError( + // controller, enqueueResult.[[Value]]). + if (!ReadableStreamControllerError(cx, unwrappedController, exn)) { + return false; + } + + // Step b.ii: Return result. + // Step e.ii: Return enqueueResult. + // (I.e., propagate the exception.) + cx->setPendingException(exn, stack); + return false; + } + } + + // Step 5: Perform + // ! ReadableStreamDefaultControllerCallPullIfNeeded(controller). + return ReadableStreamControllerCallPullIfNeeded(cx, unwrappedController); +} + +/** + * Streams spec, 3.10.7. ReadableStreamDefaultControllerError ( controller, e ) + * Streams spec, 3.13.11. ReadableByteStreamControllerError ( controller, e ) + */ +[[nodiscard]] bool js::ReadableStreamControllerError( + JSContext* cx, Handle unwrappedController, + Handle e) { + MOZ_ASSERT(!cx->isExceptionPending()); + AssertSameCompartment(cx, e); + + // Step 1: Let stream be controller.[[controlledReadableStream]] + // (or controller.[[controlledReadableByteStream]]). + Rooted unwrappedStream(cx, unwrappedController->stream()); + + // Step 2: If stream.[[state]] is not "readable", return. + if (!unwrappedStream->readable()) { + return true; + } + + // Step 3 of 3.13.10: + // Perform ! ReadableByteStreamControllerClearPendingPullIntos(controller). + if (unwrappedController->is()) { + Rooted unwrappedByteStreamController( + cx, &unwrappedController->as()); + if (!ReadableByteStreamControllerClearPendingPullIntos( + cx, unwrappedByteStreamController)) { + return false; + } + } + + // Step 3 (or 4): Perform ! ResetQueue(controller). + if (!ResetQueue(cx, unwrappedController)) { + return false; + } + + // Step 4 (or 5): + // Perform ! ReadableStreamDefaultControllerClearAlgorithms(controller) + // (or ReadableByteStreamControllerClearAlgorithms(controller)). + ReadableStreamControllerClearAlgorithms(unwrappedController); + + // Step 5 (or 6): Perform ! ReadableStreamError(stream, e). + return ReadableStreamErrorInternal(cx, unwrappedStream, e); +} + +/** + * Streams spec, 3.10.8. + * ReadableStreamDefaultControllerGetDesiredSize ( controller ) + * Streams spec 3.13.14. + * ReadableByteStreamControllerGetDesiredSize ( controller ) + */ +[[nodiscard]] double js::ReadableStreamControllerGetDesiredSizeUnchecked( + ReadableStreamController* controller) { + // Steps 1-4 done at callsites, so only assert that they have been done. +#if DEBUG + ReadableStream* stream = controller->stream(); + MOZ_ASSERT(!(stream->errored() || stream->closed())); +#endif // DEBUG + + // Step 5: Return controller.[[strategyHWM]] − controller.[[queueTotalSize]]. + return controller->strategyHWM() - controller->queueTotalSize(); +} + +/** + * Streams spec, 3.10.11. + * SetUpReadableStreamDefaultController(stream, controller, + * startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, + * sizeAlgorithm ) + * + * The standard algorithm takes a `controller` argument which must be a new, + * blank object. This implementation creates a new controller instead. + * + * In the spec, three algorithms (startAlgorithm, pullAlgorithm, + * cancelAlgorithm) are passed as arguments to this routine. This + * implementation passes these "algorithms" as data, using four arguments: + * sourceAlgorithms, underlyingSource, pullMethod, and cancelMethod. The + * sourceAlgorithms argument tells how to interpret the other three: + * + * - SourceAlgorithms::Script - We're creating a stream from a JS source. + * The caller is `new ReadableStream(underlyingSource)` or + * `JS::NewReadableDefaultStreamObject`. `underlyingSource` is the + * source; `pullMethod` and `cancelMethod` are its .pull and + * .cancel methods, which the caller has already extracted and + * type-checked: each one must be either a callable JS object or undefined. + * + * Script streams use the start/pull/cancel algorithms defined in + * 3.10.12. SetUpReadableStreamDefaultControllerFromUnderlyingSource, which + * call JS methods of the underlyingSource. + * + * - SourceAlgorithms::Tee - We're creating a tee stream. `underlyingSource` + * is a TeeState object. `pullMethod` and `cancelMethod` are undefined. + * + * Tee streams use the start/pull/cancel algorithms given in + * 3.4.10. ReadableStreamTee. + * + * Note: All arguments must be same-compartment with cx. ReadableStream + * controllers are always created in the same compartment as the stream. + */ +[[nodiscard]] bool js::SetUpReadableStreamDefaultController( + JSContext* cx, Handle stream, + SourceAlgorithms sourceAlgorithms, Handle underlyingSource, + Handle pullMethod, Handle cancelMethod, double highWaterMark, + Handle size) { + cx->check(stream, underlyingSource, size); + MOZ_ASSERT(pullMethod.isUndefined() || IsCallable(pullMethod)); + MOZ_ASSERT(cancelMethod.isUndefined() || IsCallable(cancelMethod)); + MOZ_ASSERT_IF(sourceAlgorithms != SourceAlgorithms::Script, + pullMethod.isUndefined()); + MOZ_ASSERT_IF(sourceAlgorithms != SourceAlgorithms::Script, + cancelMethod.isUndefined()); + MOZ_ASSERT(highWaterMark >= 0); + MOZ_ASSERT(size.isUndefined() || IsCallable(size)); + + // Done elsewhere in the standard: Create the new controller. + Rooted controller( + cx, NewBuiltinClassInstance(cx)); + if (!controller) { + return false; + } + + // Step 1: Assert: stream.[[readableStreamController]] is undefined. + MOZ_ASSERT(!stream->hasController()); + + // Step 2: Set controller.[[controlledReadableStream]] to stream. + controller->setStream(stream); + + // Step 3: Set controller.[[queue]] and controller.[[queueTotalSize]] to + // undefined (implicit), then perform ! ResetQueue(controller). + if (!ResetQueue(cx, controller)) { + return false; + } + + // Step 4: Set controller.[[started]], controller.[[closeRequested]], + // controller.[[pullAgain]], and controller.[[pulling]] to false. + controller->setFlags(0); + + // Step 5: Set controller.[[strategySizeAlgorithm]] to sizeAlgorithm + // and controller.[[strategyHWM]] to highWaterMark. + controller->setStrategySize(size); + controller->setStrategyHWM(highWaterMark); + + // Step 6: Set controller.[[pullAlgorithm]] to pullAlgorithm. + // (In this implementation, the pullAlgorithm is determined by the + // underlyingSource in combination with the pullMethod field.) + controller->setUnderlyingSource(underlyingSource); + controller->setPullMethod(pullMethod); + + // Step 7: Set controller.[[cancelAlgorithm]] to cancelAlgorithm. + controller->setCancelMethod(cancelMethod); + + // Step 8: Set stream.[[readableStreamController]] to controller. + stream->setController(controller); + + // Step 9: Let startResult be the result of performing startAlgorithm. + Rooted startResult(cx); + if (sourceAlgorithms == SourceAlgorithms::Script) { + Rooted controllerVal(cx, ObjectValue(*controller)); + if (!InvokeOrNoop(cx, underlyingSource, cx->names().start, controllerVal, + &startResult)) { + return false; + } + } + + // Step 10: Let startPromise be a promise resolved with startResult. + Rooted startPromise( + cx, PromiseObject::unforgeableResolve(cx, startResult)); + if (!startPromise) { + return false; + } + + // Step 11: Upon fulfillment of startPromise, [...] + // Step 12: Upon rejection of startPromise with reason r, [...] + Rooted onStartFulfilled( + cx, NewHandler(cx, ReadableStreamControllerStartHandler, controller)); + if (!onStartFulfilled) { + return false; + } + Rooted onStartRejected( + cx, + NewHandler(cx, ReadableStreamControllerStartFailedHandler, controller)); + if (!onStartRejected) { + return false; + } + if (!JS::AddPromiseReactions(cx, startPromise, onStartFulfilled, + onStartRejected)) { + return false; + } + + return true; +} + +/** + * Streams spec, 3.10.12. + * SetUpReadableStreamDefaultControllerFromUnderlyingSource( stream, + * underlyingSource, highWaterMark, sizeAlgorithm ) + */ +[[nodiscard]] bool js::SetUpReadableStreamDefaultControllerFromUnderlyingSource( + JSContext* cx, Handle stream, + Handle underlyingSource, double highWaterMark, + Handle sizeAlgorithm) { + // Step 1: Assert: underlyingSource is not undefined. + MOZ_ASSERT(!underlyingSource.isUndefined()); + + // Step 2: Let controller be ObjectCreate(the original value of + // ReadableStreamDefaultController's prototype property). + // (Deferred to SetUpReadableStreamDefaultController.) + + // Step 3: Let startAlgorithm be the following steps: + // a. Return ? InvokeOrNoop(underlyingSource, "start", + // « controller »). + SourceAlgorithms sourceAlgorithms = SourceAlgorithms::Script; + + // Step 4: Let pullAlgorithm be + // ? CreateAlgorithmFromUnderlyingMethod(underlyingSource, "pull", + // 0, « controller »). + Rooted pullMethod(cx); + if (!CreateAlgorithmFromUnderlyingMethod(cx, underlyingSource, + "ReadableStream source.pull method", + cx->names().pull, &pullMethod)) { + return false; + } + + // Step 5. Let cancelAlgorithm be + // ? CreateAlgorithmFromUnderlyingMethod(underlyingSource, + // "cancel", 1, « »). + Rooted cancelMethod(cx); + if (!CreateAlgorithmFromUnderlyingMethod( + cx, underlyingSource, "ReadableStream source.cancel method", + cx->names().cancel, &cancelMethod)) { + return false; + } + + // Step 6. Perform ? SetUpReadableStreamDefaultController(stream, + // controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, + // highWaterMark, sizeAlgorithm). + return SetUpReadableStreamDefaultController( + cx, stream, sourceAlgorithms, underlyingSource, pullMethod, cancelMethod, + highWaterMark, sizeAlgorithm); +} diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamDefaultControllerOperations.h b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamDefaultControllerOperations.h new file mode 100644 index 00000000000..905f56bad7a --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamDefaultControllerOperations.h @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Readable stream default controller abstract operations. */ + +#ifndef builtin_streams_ReadableStreamDefaultControllerOperations_h +#define builtin_streams_ReadableStreamDefaultControllerOperations_h + +#include "jstypes.h" // JS_PUBLIC_API +#include "js/RootingAPI.h" // JS::Handle +#include "js/Value.h" // JS::Value + +struct JS_PUBLIC_API JSContext; + +namespace js { + +class ReadableStream; +class ReadableStreamController; +class ReadableStreamDefaultController; + +[[nodiscard]] extern bool ReadableStreamDefaultControllerEnqueue( + JSContext* cx, + JS::Handle unwrappedController, + JS::Handle chunk); + +[[nodiscard]] extern bool ReadableStreamControllerError( + JSContext* cx, JS::Handle unwrappedController, + JS::Handle e); + +[[nodiscard]] extern bool ReadableStreamDefaultControllerClose( + JSContext* cx, + JS::Handle unwrappedController); + +[[nodiscard]] extern double ReadableStreamControllerGetDesiredSizeUnchecked( + ReadableStreamController* controller); + +[[nodiscard]] extern bool ReadableStreamControllerCallPullIfNeeded( + JSContext* cx, JS::Handle unwrappedController); + +extern void ReadableStreamControllerClearAlgorithms( + JS::Handle controller); + +/** + * Characterizes the family of algorithms, (startAlgorithm, pullAlgorithm, + * cancelAlgorithm), associated with a readable stream. + * + * See the comment on SetUpReadableStreamDefaultController(). + */ +enum class SourceAlgorithms { + Script, + Tee, +}; + +[[nodiscard]] extern bool SetUpReadableStreamDefaultController( + JSContext* cx, JS::Handle stream, + SourceAlgorithms sourceAlgorithms, JS::Handle underlyingSource, + JS::Handle pullMethod, JS::Handle cancelMethod, + double highWaterMark, JS::Handle size); + +[[nodiscard]] extern bool +SetUpReadableStreamDefaultControllerFromUnderlyingSource( + JSContext* cx, JS::Handle stream, + JS::Handle underlyingSource, double highWaterMark, + JS::Handle sizeAlgorithm); + +} // namespace js + +#endif // builtin_streams_ReadableStreamDefaultControllerOperations_h diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamDefaultReader.cpp b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamDefaultReader.cpp new file mode 100644 index 00000000000..c45281b9c46 --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamDefaultReader.cpp @@ -0,0 +1,264 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Class ReadableStreamDefaultReader. */ + +#include "builtin/streams/ClassSpecMacro.h" // JS_STREAMS_CLASS_SPEC +#include "builtin/streams/MiscellaneousOperations.h" // js::ReturnPromiseRejectedWithPendingError +#include "builtin/streams/ReadableStream.h" // js::ReadableStream +#include "builtin/streams/ReadableStreamReader.h" // js::ForAuthorCodeBool, js::ReadableStream{,Default}Reader +#include "js/CallArgs.h" // JS::CallArgs{,FromVp} +#include "js/Class.h" // JSClass, JS_NULL_CLASS_OPS +#include "js/ErrorReport.h" // JS_ReportErrorNumberASCII +#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* +#include "js/RootingAPI.h" // JS::Handle, JS::Rooted +#include "vm/PromiseObject.h" // js::PromiseObject + +#include "vm/Compartment-inl.h" // js::UnwrapAndTypeCheckThis +#include "vm/JSObject-inl.h" // js::NewObjectWithClassProto +#include "vm/NativeObject-inl.h" // js::ThrowIfNotConstructing + +using JS::CallArgs; +using JS::CallArgsFromVp; +using JS::Handle; +using JS::Rooted; +using JS::Value; + +using js::ForAuthorCodeBool; +using js::GetErrorMessage; +using js::ListObject; +using js::NewObjectWithClassProto; +using js::PromiseObject; +using js::ReadableStream; +using js::ReadableStreamDefaultReader; +using js::ReadableStreamReader; +using js::UnwrapAndTypeCheckThis; + +/*** 3.6. Class ReadableStreamDefaultReader *********************************/ + +/** + * Stream spec, 3.6.3. new ReadableStreamDefaultReader ( stream ) + * Steps 2-4. + */ +[[nodiscard]] ReadableStreamDefaultReader* +js::CreateReadableStreamDefaultReader(JSContext* cx, + Handle unwrappedStream, + ForAuthorCodeBool forAuthorCode, + Handle proto /* = nullptr */) { + Rooted reader( + cx, NewObjectWithClassProto(cx, proto)); + if (!reader) { + return nullptr; + } + + // Step 2: If ! IsReadableStreamLocked(stream) is true, throw a TypeError + // exception. + if (unwrappedStream->locked()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_READABLESTREAM_LOCKED); + return nullptr; + } + + // Step 3: Perform ! ReadableStreamReaderGenericInitialize(this, stream). + // Step 4: Set this.[[readRequests]] to a new empty List. + if (!ReadableStreamReaderGenericInitialize(cx, reader, unwrappedStream, + forAuthorCode)) { + return nullptr; + } + + return reader; +} + +/** + * Stream spec, 3.6.3. new ReadableStreamDefaultReader ( stream ) + */ +bool ReadableStreamDefaultReader::constructor(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + if (!ThrowIfNotConstructing(cx, args, "ReadableStreamDefaultReader")) { + return false; + } + + // Implicit in the spec: Find the prototype object to use. + Rooted proto(cx); + if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Null, &proto)) { + return false; + } + + // Step 1: If ! IsReadableStream(stream) is false, throw a TypeError + // exception. + Rooted unwrappedStream( + cx, UnwrapAndTypeCheckArgument( + cx, args, "ReadableStreamDefaultReader constructor", 0)); + if (!unwrappedStream) { + return false; + } + + Rooted reader( + cx, CreateReadableStreamDefaultReader(cx, unwrappedStream, + ForAuthorCodeBool::Yes, proto)); + if (!reader) { + return false; + } + + args.rval().setObject(*reader); + return true; +} + +/** + * Streams spec, 3.6.4.1 get closed + */ +[[nodiscard]] static bool ReadableStreamDefaultReader_closed(JSContext* cx, + unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + // Step 1: If ! IsReadableStreamDefaultReader(this) is false, return a promise + // rejected with a TypeError exception. + Rooted unwrappedReader( + cx, UnwrapAndTypeCheckThis(cx, args, + "get closed")); + if (!unwrappedReader) { + return ReturnPromiseRejectedWithPendingError(cx, args); + } + + // Step 2: Return this.[[closedPromise]]. + Rooted closedPromise(cx, unwrappedReader->closedPromise()); + if (!cx->compartment()->wrap(cx, &closedPromise)) { + return false; + } + + args.rval().setObject(*closedPromise); + return true; +} + +/** + * Streams spec, 3.6.4.2. cancel ( reason ) + */ +[[nodiscard]] static bool ReadableStreamDefaultReader_cancel(JSContext* cx, + unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + // Step 1: If ! IsReadableStreamDefaultReader(this) is false, return a promise + // rejected with a TypeError exception. + Rooted unwrappedReader( + cx, + UnwrapAndTypeCheckThis(cx, args, "cancel")); + if (!unwrappedReader) { + return ReturnPromiseRejectedWithPendingError(cx, args); + } + + // Step 2: If this.[[ownerReadableStream]] is undefined, return a promise + // rejected with a TypeError exception. + if (!unwrappedReader->hasStream()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_READABLESTREAMREADER_NOT_OWNED, "cancel"); + return ReturnPromiseRejectedWithPendingError(cx, args); + } + + // Step 3: Return ! ReadableStreamReaderGenericCancel(this, reason). + JSObject* cancelPromise = + ReadableStreamReaderGenericCancel(cx, unwrappedReader, args.get(0)); + if (!cancelPromise) { + return false; + } + args.rval().setObject(*cancelPromise); + return true; +} + +/** + * Streams spec, 3.6.4.3 read ( ) + */ +[[nodiscard]] static bool ReadableStreamDefaultReader_read(JSContext* cx, + unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + // Step 1: If ! IsReadableStreamDefaultReader(this) is false, return a promise + // rejected with a TypeError exception. + Rooted unwrappedReader( + cx, + UnwrapAndTypeCheckThis(cx, args, "read")); + if (!unwrappedReader) { + return ReturnPromiseRejectedWithPendingError(cx, args); + } + + // Step 2: If this.[[ownerReadableStream]] is undefined, return a promise + // rejected with a TypeError exception. + if (!unwrappedReader->hasStream()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_READABLESTREAMREADER_NOT_OWNED, "read"); + return ReturnPromiseRejectedWithPendingError(cx, args); + } + + // Step 3: Return ! ReadableStreamDefaultReaderRead(this, true). + PromiseObject* readPromise = + js::ReadableStreamDefaultReaderRead(cx, unwrappedReader); + if (!readPromise) { + return false; + } + args.rval().setObject(*readPromise); + return true; +} + +/** + * Streams spec, 3.6.4.4. releaseLock ( ) + */ +static bool ReadableStreamDefaultReader_releaseLock(JSContext* cx, + unsigned argc, Value* vp) { + // Step 1: If ! IsReadableStreamDefaultReader(this) is false, + // throw a TypeError exception. + CallArgs args = CallArgsFromVp(argc, vp); + Rooted reader( + cx, UnwrapAndTypeCheckThis(cx, args, + "releaseLock")); + if (!reader) { + return false; + } + + // Step 2: If this.[[ownerReadableStream]] is undefined, return. + if (!reader->hasStream()) { + args.rval().setUndefined(); + return true; + } + + // Step 3: If this.[[readRequests]] is not empty, throw a TypeError exception. + Value val = reader->getFixedSlot(ReadableStreamReader::Slot_Requests); + if (!val.isUndefined()) { + ListObject* readRequests = &val.toObject().as(); + if (readRequests->length() != 0) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_READABLESTREAMREADER_NOT_EMPTY, + "releaseLock"); + return false; + } + } + + // Step 4: Perform ! ReadableStreamReaderGenericRelease(this). + if (!js::ReadableStreamReaderGenericRelease(cx, reader)) { + return false; + } + + args.rval().setUndefined(); + return true; +} + +static const JSFunctionSpec ReadableStreamDefaultReader_methods[] = { + JS_FN("cancel", ReadableStreamDefaultReader_cancel, 1, 0), + JS_FN("read", ReadableStreamDefaultReader_read, 0, 0), + JS_FN("releaseLock", ReadableStreamDefaultReader_releaseLock, 0, 0), + JS_FS_END}; + +static const JSPropertySpec ReadableStreamDefaultReader_properties[] = { + JS_PSG("closed", ReadableStreamDefaultReader_closed, 0), JS_PS_END}; + +const JSClass ReadableStreamReader::class_ = {"ReadableStreamReader"}; + +JS_STREAMS_CLASS_SPEC(ReadableStreamDefaultReader, 1, SlotCount, + js::ClassSpec::DontDefineConstructor, 0, + JS_NULL_CLASS_OPS); diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamInternals.cpp b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamInternals.cpp new file mode 100644 index 00000000000..6b94da84160 --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamInternals.cpp @@ -0,0 +1,473 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* The interface between readable streams and controllers. */ + +#include "builtin/streams/ReadableStreamInternals.h" + +#include "mozilla/Assertions.h" // MOZ_ASSERT{,_IF} + +#include // uint32_t + +#include "jsfriendapi.h" // js::AssertSameCompartment + +#include "builtin/streams/ReadableStreamController.h" // js::ReadableStreamController{,CancelSteps} +#include "builtin/streams/ReadableStreamReader.h" // js::ReadableStream{,Default}Reader, js::ForAuthorCodeBool +#include "gc/AllocKind.h" // js::gc::AllocKind +#include "js/CallArgs.h" // JS::CallArgs{,FromVp} +#include "js/GCAPI.h" // JS::AutoSuppressGCAnalysis +#include "js/Promise.h" // JS::CallOriginalPromiseThen, JS::ResolvePromise +#include "js/Result.h" // JS_TRY_VAR_OR_RETURN_NULL +#include "js/RootingAPI.h" // JS::Handle, JS::Rooted +#include "js/Stream.h" // JS::ReadableStreamUnderlyingSource, JS::ReadableStreamMode +#include "js/Value.h" // JS::Value, JS::{Boolean,Object}Value, JS::UndefinedHandleValue +#include "vm/JSContext.h" // JSContext +#include "vm/JSFunction.h" // JSFunction, js::NewNativeFunction +#include "vm/JSObject.h" // js::GenericObject +#include "vm/NativeObject.h" // js::NativeObject, js::PlainObject +#include "vm/PromiseObject.h" // js::PromiseObject, js::PromiseResolvedWithUndefined +#include "vm/Realm.h" // JS::Realm +#include "vm/StringType.h" // js::PropertyName + +#include "builtin/Promise-inl.h" // js::SetSettledPromiseIsHandled +#include "builtin/streams/MiscellaneousOperations-inl.h" // js::{Reject,Resolve}UnwrappedPromiseWithUndefined +#include "builtin/streams/ReadableStreamReader-inl.h" // js::js::UnwrapReaderFromStream{,NoThrow} +#include "vm/Compartment-inl.h" // JS::Compartment::wrap +#include "vm/JSContext-inl.h" // JSContext::check +#include "vm/List-inl.h" // js::ListObject, js::AppendToListInFixedSlot, js::StoreNewListInFixedSlot +#include "vm/PlainObject-inl.h" // js::PlainObject::createWithTemplate +#include "vm/Realm-inl.h" // JS::Realm + +using JS::BooleanValue; +using JS::CallArgs; +using JS::CallArgsFromVp; +using JS::Handle; +using JS::ObjectValue; +using JS::ResolvePromise; +using JS::Rooted; +using JS::UndefinedHandleValue; +using JS::Value; + +using js::PlainObject; +using js::ReadableStream; + +/*** 3.5. The interface between readable streams and controllers ************/ + +/** + * Streams spec, 3.5.1. + * ReadableStreamAddReadIntoRequest ( stream, forAuthorCode ) + * Streams spec, 3.5.2. + * ReadableStreamAddReadRequest ( stream, forAuthorCode ) + * + * Our implementation does not pass around forAuthorCode parameters in the same + * places as the standard, but the effect is the same. See the comment on + * `ReadableStreamReader::forAuthorCode()`. + */ +[[nodiscard]] js::PromiseObject* js::ReadableStreamAddReadOrReadIntoRequest( + JSContext* cx, Handle unwrappedStream) { + // Step 1: Assert: ! IsReadableStream{BYOB,Default}Reader(stream.[[reader]]) + // is true. + // (Only default readers exist so far.) + Rooted unwrappedReader( + cx, UnwrapReaderFromStream(cx, unwrappedStream)); + if (!unwrappedReader) { + return nullptr; + } + MOZ_ASSERT(unwrappedReader->is()); + + // Step 2 of 3.5.1: Assert: stream.[[state]] is "readable" or "closed". + // Step 2 of 3.5.2: Assert: stream.[[state]] is "readable". + MOZ_ASSERT(unwrappedStream->readable() || unwrappedStream->closed()); + MOZ_ASSERT_IF(unwrappedReader->is(), + unwrappedStream->readable()); + + // Step 3: Let promise be a new promise. + Rooted promise(cx, PromiseObject::createSkippingExecutor(cx)); + if (!promise) { + return nullptr; + } + + // Step 4: Let read{Into}Request be + // Record {[[promise]]: promise, [[forAuthorCode]]: forAuthorCode}. + // Step 5: Append read{Into}Request as the last element of + // stream.[[reader]].[[read{Into}Requests]]. + // Since we don't need the [[forAuthorCode]] field (see the comment on + // `ReadableStreamReader::forAuthorCode()`), we elide the Record and store + // only the promise. + if (!AppendToListInFixedSlot(cx, unwrappedReader, + ReadableStreamReader::Slot_Requests, promise)) { + return nullptr; + } + + // Step 6: Return promise. + return promise; +} + +/** + * Used for transforming the result of promise fulfillment/rejection. + */ +static bool ReturnUndefined(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + args.rval().setUndefined(); + return true; +} + +/** + * Streams spec, 3.5.3. ReadableStreamCancel ( stream, reason ) + */ +[[nodiscard]] JSObject* js::ReadableStreamCancel( + JSContext* cx, Handle unwrappedStream, + Handle reason) { + AssertSameCompartment(cx, reason); + + // Step 1: Set stream.[[disturbed]] to true. + unwrappedStream->setDisturbed(); + + // Step 2: If stream.[[state]] is "closed", return a promise resolved with + // undefined. + if (unwrappedStream->closed()) { + return PromiseResolvedWithUndefined(cx); + } + + // Step 3: If stream.[[state]] is "errored", return a promise rejected with + // stream.[[storedError]]. + if (unwrappedStream->errored()) { + Rooted storedError(cx, unwrappedStream->storedError()); + if (!cx->compartment()->wrap(cx, &storedError)) { + return nullptr; + } + return PromiseObject::unforgeableReject(cx, storedError); + } + + // Step 4: Perform ! ReadableStreamClose(stream). + if (!ReadableStreamCloseInternal(cx, unwrappedStream)) { + return nullptr; + } + + // Step 5: Let sourceCancelPromise be + // ! stream.[[readableStreamController]].[[CancelSteps]](reason). + Rooted unwrappedController( + cx, unwrappedStream->controller()); + Rooted sourceCancelPromise( + cx, ReadableStreamControllerCancelSteps(cx, unwrappedController, reason)); + if (!sourceCancelPromise) { + return nullptr; + } + + // Step 6: Return the result of reacting to sourceCancelPromise with a + // fulfillment step that returns undefined. + Handle funName = cx->names().empty; + Rooted returnUndefined( + cx, NewNativeFunction(cx, ReturnUndefined, 0, funName, + gc::AllocKind::FUNCTION, GenericObject)); + if (!returnUndefined) { + return nullptr; + } + return JS::CallOriginalPromiseThen(cx, sourceCancelPromise, returnUndefined, + nullptr); +} + +/** + * Streams spec, 3.5.4. ReadableStreamClose ( stream ) + */ +[[nodiscard]] bool js::ReadableStreamCloseInternal( + JSContext* cx, Handle unwrappedStream) { + // Step 1: Assert: stream.[[state]] is "readable". + MOZ_ASSERT(unwrappedStream->readable()); + + // Step 2: Set stream.[[state]] to "closed". + unwrappedStream->setClosed(); + + // Step 4: If reader is undefined, return (reordered). + if (!unwrappedStream->hasReader()) { + return true; + } + + // Step 3: Let reader be stream.[[reader]]. + Rooted unwrappedReader( + cx, UnwrapReaderFromStream(cx, unwrappedStream)); + if (!unwrappedReader) { + return false; + } + + // Step 5: If ! IsReadableStreamDefaultReader(reader) is true, + if (unwrappedReader->is()) { + ForAuthorCodeBool forAuthorCode = unwrappedReader->forAuthorCode(); + + // Step a: Repeat for each readRequest that is an element of + // reader.[[readRequests]], + Rooted unwrappedReadRequests(cx, unwrappedReader->requests()); + uint32_t len = unwrappedReadRequests->length(); + Rooted readRequest(cx); + Rooted resultObj(cx); + Rooted resultVal(cx); + for (uint32_t i = 0; i < len; i++) { + // Step i: Resolve readRequest.[[promise]] with + // ! ReadableStreamCreateReadResult(undefined, true, + // readRequest.[[forAuthorCode]]). + readRequest = &unwrappedReadRequests->getAs(i); + if (!cx->compartment()->wrap(cx, &readRequest)) { + return false; + } + + resultObj = js::ReadableStreamCreateReadResult(cx, UndefinedHandleValue, + true, forAuthorCode); + if (!resultObj) { + return false; + } + resultVal = ObjectValue(*resultObj); + if (!ResolvePromise(cx, readRequest, resultVal)) { + return false; + } + } + + // Step b: Set reader.[[readRequests]] to an empty List. + unwrappedReader->clearRequests(); + } + + // Step 6: Resolve reader.[[closedPromise]] with undefined. + if (!ResolveUnwrappedPromiseWithUndefined(cx, + unwrappedReader->closedPromise())) { + return false; + } + + if (unwrappedStream->mode() == JS::ReadableStreamMode::ExternalSource) { + // Make sure we're in the stream's compartment. + AutoRealm ar(cx, unwrappedStream); + JS::ReadableStreamUnderlyingSource* source = + unwrappedStream->controller()->externalSource(); + source->onClosed(cx, unwrappedStream); + } + + return true; +} + +/** + * Streams spec, 3.5.5. ReadableStreamCreateReadResult ( value, done, + * forAuthorCode ) + */ +[[nodiscard]] PlainObject* js::ReadableStreamCreateReadResult( + JSContext* cx, Handle value, bool done, + ForAuthorCodeBool forAuthorCode) { + // Step 1: Let prototype be null. + // Step 2: If forAuthorCode is true, set prototype to %ObjectPrototype%. + Rooted templateObject( + cx, + forAuthorCode == ForAuthorCodeBool::Yes + ? GlobalObject::getOrCreateIterResultTemplateObject(cx) + : GlobalObject::getOrCreateIterResultWithoutPrototypeTemplateObject( + cx)); + if (!templateObject) { + return nullptr; + } + + // Step 3: Assert: Type(done) is Boolean (implicit). + + // Step 4: Let obj be ObjectCreate(prototype). + PlainObject* obj = PlainObject::createWithTemplate(cx, templateObject); + if (!obj) { + return nullptr; + } + + // Step 5: Perform CreateDataProperty(obj, "value", value). + obj->setSlot(GlobalObject::IterResultObjectValueSlot, value); + + // Step 6: Perform CreateDataProperty(obj, "done", done). + obj->setSlot(GlobalObject::IterResultObjectDoneSlot, BooleanValue(done)); + + // Step 7: Return obj. + return obj; +} + +/** + * Streams spec, 3.5.6. ReadableStreamError ( stream, e ) + */ +[[nodiscard]] bool js::ReadableStreamErrorInternal( + JSContext* cx, Handle unwrappedStream, Handle e) { + // Step 1: Assert: ! IsReadableStream(stream) is true (implicit). + + // Step 2: Assert: stream.[[state]] is "readable". + MOZ_ASSERT(unwrappedStream->readable()); + + // Step 3: Set stream.[[state]] to "errored". + unwrappedStream->setErrored(); + + // Step 4: Set stream.[[storedError]] to e. + { + AutoRealm ar(cx, unwrappedStream); + Rooted wrappedError(cx, e); + if (!cx->compartment()->wrap(cx, &wrappedError)) { + return false; + } + unwrappedStream->setStoredError(wrappedError); + } + + // Step 6: If reader is undefined, return (reordered). + if (!unwrappedStream->hasReader()) { + return true; + } + + // Step 5: Let reader be stream.[[reader]]. + Rooted unwrappedReader( + cx, UnwrapReaderFromStream(cx, unwrappedStream)); + if (!unwrappedReader) { + return false; + } + + // Steps 7-8: (Identical in our implementation.) + // Step 7.a/8.b: Repeat for each read{Into}Request that is an element of + // reader.[[read{Into}Requests]], + { + Rooted unwrappedReadRequests(cx, unwrappedReader->requests()); + Rooted readRequest(cx); + uint32_t len = unwrappedReadRequests->length(); + for (uint32_t i = 0; i < len; i++) { + // Step i: Reject read{Into}Request.[[promise]] with e. + // Responses have to be created in the compartment from which the error + // was triggered, which might not be the same as the one the request was + // created in, so we have to wrap requests here. + readRequest = &unwrappedReadRequests->get(i).toObject(); + if (!RejectUnwrappedPromiseWithError(cx, &readRequest, e)) { + return false; + } + } + } + + // Step 7.b/8.c: Set reader.[[read{Into}Requests]] to a new empty List. + if (!StoreNewListInFixedSlot(cx, unwrappedReader, + ReadableStreamReader::Slot_Requests)) { + return false; + } + + // Step 9: Reject reader.[[closedPromise]] with e. + if (!RejectUnwrappedPromiseWithError(cx, unwrappedReader->closedPromise(), + e)) { + return false; + } + + // Step 10: Set reader.[[closedPromise]].[[PromiseIsHandled]] to true. + // + // `closedPromise` can return a CCW, but that case is filtered out by step 6, + // given the only place that can set [[closedPromise]] to a CCW is + // 3.8.5 ReadableStreamReaderGenericRelease step 4, and + // 3.8.5 ReadableStreamReaderGenericRelease step 6 sets + // stream.[[reader]] to undefined. + Rooted closedPromise(cx, unwrappedReader->closedPromise()); + js::SetSettledPromiseIsHandled(cx, closedPromise.as()); + + if (unwrappedStream->mode() == JS::ReadableStreamMode::ExternalSource) { + // Make sure we're in the stream's compartment. + AutoRealm ar(cx, unwrappedStream); + JS::ReadableStreamUnderlyingSource* source = + unwrappedStream->controller()->externalSource(); + + // Ensure that the embedding doesn't have to deal with + // mixed-compartment arguments to the callback. + Rooted error(cx, e); + if (!cx->compartment()->wrap(cx, &error)) { + return false; + } + source->onErrored(cx, unwrappedStream, error); + } + + return true; +} + +/** + * Streams spec, 3.5.7. + * ReadableStreamFulfillReadIntoRequest( stream, chunk, done ) + * Streams spec, 3.5.8. + * ReadableStreamFulfillReadRequest ( stream, chunk, done ) + * These two spec functions are identical in our implementation. + */ +[[nodiscard]] bool js::ReadableStreamFulfillReadOrReadIntoRequest( + JSContext* cx, Handle unwrappedStream, Handle chunk, + bool done) { + cx->check(chunk); + + // Step 1: Let reader be stream.[[reader]]. + Rooted unwrappedReader( + cx, UnwrapReaderFromStream(cx, unwrappedStream)); + if (!unwrappedReader) { + return false; + } + + // Step 2: Let read{Into}Request be the first element of + // reader.[[read{Into}Requests]]. + // Step 3: Remove read{Into}Request from reader.[[read{Into}Requests]], + // shifting all other elements downward (so that the second becomes + // the first, and so on). + Rooted unwrappedReadIntoRequests(cx, + unwrappedReader->requests()); + Rooted readIntoRequest( + cx, &unwrappedReadIntoRequests->popFirstAs(cx)); + MOZ_ASSERT(readIntoRequest); + if (!cx->compartment()->wrap(cx, &readIntoRequest)) { + return false; + } + + // Step 4: Resolve read{Into}Request.[[promise]] with + // ! ReadableStreamCreateReadResult(chunk, done, + // readIntoRequest.[[forAuthorCode]]). + PlainObject* iterResult = ReadableStreamCreateReadResult( + cx, chunk, done, unwrappedReader->forAuthorCode()); + if (!iterResult) { + return false; + } + + Rooted val(cx, ObjectValue(*iterResult)); + return ResolvePromise(cx, readIntoRequest, val); +} + +/** + * Streams spec, 3.5.9. ReadableStreamGetNumReadIntoRequests ( stream ) + * Streams spec, 3.5.10. ReadableStreamGetNumReadRequests ( stream ) + * (Identical implementation.) + */ +uint32_t js::ReadableStreamGetNumReadRequests(ReadableStream* stream) { + // Step 1: Return the number of elements in + // stream.[[reader]].[[read{Into}Requests]]. + if (!stream->hasReader()) { + return 0; + } + + JS::AutoSuppressGCAnalysis nogc; + ReadableStreamReader* reader = UnwrapReaderFromStreamNoThrow(stream); + + // Reader is a dead wrapper, treat it as non-existent. + if (!reader) { + return 0; + } + + return reader->requests()->length(); +} + +// Streams spec, 3.5.11. ReadableStreamHasBYOBReader ( stream ) +// +// Not implemented. + +/** + * Streams spec 3.5.12. ReadableStreamHasDefaultReader ( stream ) + */ +[[nodiscard]] bool js::ReadableStreamHasDefaultReader( + JSContext* cx, Handle unwrappedStream, bool* result) { + // Step 1: Let reader be stream.[[reader]]. + // Step 2: If reader is undefined, return false. + if (!unwrappedStream->hasReader()) { + *result = false; + return true; + } + Rooted unwrappedReader( + cx, UnwrapReaderFromStream(cx, unwrappedStream)); + if (!unwrappedReader) { + return false; + } + + // Step 3: If ! ReadableStreamDefaultReader(reader) is false, return false. + // Step 4: Return true. + *result = unwrappedReader->is(); + return true; +} diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamInternals.h b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamInternals.h new file mode 100644 index 00000000000..a2b22f107a3 --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamInternals.h @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* The interface between readable streams and controllers. */ + +#ifndef builtin_streams_ReadableStreamInternals_h +#define builtin_streams_ReadableStreamInternals_h + +#include "jstypes.h" // JS_PUBLIC_API +#include "builtin/streams/ReadableStreamReader.h" // js::ForAuthorCodeBool +#include "js/RootingAPI.h" // JS::Handle +#include "js/Value.h" // JS::Value + +struct JS_PUBLIC_API JSContext; +class JS_PUBLIC_API JSObject; + +namespace js { + +class PlainObject; +class PromiseObject; +class ReadableStream; + +[[nodiscard]] extern PromiseObject* ReadableStreamAddReadOrReadIntoRequest( + JSContext* cx, JS::Handle unwrappedStream); + +[[nodiscard]] extern JSObject* ReadableStreamCancel( + JSContext* cx, JS::Handle unwrappedStream, + JS::Handle reason); + +[[nodiscard]] extern bool ReadableStreamCloseInternal( + JSContext* cx, JS::Handle unwrappedStream); + +[[nodiscard]] extern PlainObject* ReadableStreamCreateReadResult( + JSContext* cx, JS::Handle value, bool done, + ForAuthorCodeBool forAuthorCode); + +[[nodiscard]] extern bool ReadableStreamErrorInternal( + JSContext* cx, JS::Handle unwrappedStream, + JS::Handle e); + +[[nodiscard]] extern bool ReadableStreamFulfillReadOrReadIntoRequest( + JSContext* cx, JS::Handle unwrappedStream, + JS::Handle chunk, bool done); + +extern uint32_t ReadableStreamGetNumReadRequests(ReadableStream* stream); + +[[nodiscard]] extern bool ReadableStreamHasDefaultReader( + JSContext* cx, JS::Handle unwrappedStream, bool* result); + +} // namespace js + +#endif // builtin_streams_ReadableStreamInternals_h diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamOperations.cpp b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamOperations.cpp new file mode 100644 index 00000000000..1778fe9bb45 --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamOperations.cpp @@ -0,0 +1,633 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* General readable stream abstract operations. */ + +#include "builtin/streams/ReadableStreamOperations.h" + +#include "mozilla/Assertions.h" // MOZ_ASSERT{,_IF} + +#include "builtin/Array.h" // js::NewDenseFullyAllocatedArray +#include "builtin/Promise.h" // js::RejectPromiseWithPendingError +#include "builtin/streams/ReadableStream.h" // js::ReadableStream +#include "builtin/streams/ReadableStreamController.h" // js::ReadableStream{,Default}Controller +#include "builtin/streams/ReadableStreamDefaultControllerOperations.h" // js::ReadableStreamDefaultController{Close,Enqueue}, js::ReadableStreamControllerError, js::SourceAlgorithms +#include "builtin/streams/ReadableStreamInternals.h" // js::ReadableStreamCancel +#include "builtin/streams/ReadableStreamReader.h" // js::CreateReadableStreamDefaultReader, js::ForAuthorCodeBool, js::ReadableStream{,Default}Reader, js::ReadableStreamDefaultReaderRead +#include "builtin/streams/TeeState.h" // js::TeeState +#include "js/CallAndConstruct.h" // JS::IsCallable +#include "js/CallArgs.h" // JS::CallArgs{,FromVp} +#include "js/Promise.h" // JS::CallOriginalPromiseThen, JS::AddPromiseReactions +#include "js/RootingAPI.h" // JS::{,Mutable}Handle, JS::Rooted +#include "js/Value.h" // JS::Value, JS::UndefinedHandleValue +#include "vm/JSContext.h" // JSContext +#include "vm/NativeObject.h" // js::NativeObject +#include "vm/ObjectOperations.h" // js::GetProperty +#include "vm/PromiseObject.h" // js::PromiseObject, js::PromiseResolvedWithUndefined + +#include "builtin/HandlerFunction-inl.h" // js::NewHandler, js::TargetFromHandler +#include "builtin/streams/MiscellaneousOperations-inl.h" // js::ResolveUnwrappedPromiseWithValue +#include "builtin/streams/ReadableStreamReader-inl.h" // js::UnwrapReaderFromStream +#include "vm/Compartment-inl.h" // JS::Compartment::wrap, js::Unwrap{Callee,Internal}Slot +#include "vm/JSContext-inl.h" // JSContext::check +#include "vm/JSObject-inl.h" // js::IsCallable, js::NewObjectWithClassProto +#include "vm/Realm-inl.h" // js::AutoRealm + +using js::IsCallable; +using js::NewHandler; +using js::NewObjectWithClassProto; +using js::PromiseObject; +using js::ReadableStream; +using js::ReadableStreamDefaultController; +using js::ReadableStreamDefaultControllerEnqueue; +using js::ReadableStreamDefaultReader; +using js::ReadableStreamReader; +using js::SourceAlgorithms; +using js::TargetFromHandler; +using js::TeeState; +using js::UnwrapCalleeSlot; + +using JS::CallArgs; +using JS::CallArgsFromVp; +using JS::Handle; +using JS::MutableHandle; +using JS::ObjectValue; +using JS::Rooted; +using JS::UndefinedHandleValue; +using JS::Value; + +/*** 3.4. General readable stream abstract operations ***********************/ + +// Streams spec, 3.4.1. AcquireReadableStreamBYOBReader ( stream ) +// Always inlined. + +// Streams spec, 3.4.2. AcquireReadableStreamDefaultReader ( stream ) +// Always inlined. See CreateReadableStreamDefaultReader. + +/** + * Streams spec, 3.4.3. CreateReadableStream ( + * startAlgorithm, pullAlgorithm, cancelAlgorithm + * [, highWaterMark [, sizeAlgorithm ] ] ) + * + * The start/pull/cancelAlgorithm arguments are represented instead as four + * arguments: sourceAlgorithms, underlyingSource, pullMethod, cancelMethod. + * See the comment on SetUpReadableStreamDefaultController. + */ +[[nodiscard]] static ReadableStream* CreateReadableStream( + JSContext* cx, SourceAlgorithms sourceAlgorithms, + Handle underlyingSource, + Handle pullMethod = UndefinedHandleValue, + Handle cancelMethod = UndefinedHandleValue, double highWaterMark = 1, + Handle sizeAlgorithm = UndefinedHandleValue, + Handle proto = nullptr) { + cx->check(underlyingSource, sizeAlgorithm, proto); + MOZ_ASSERT(sizeAlgorithm.isUndefined() || IsCallable(sizeAlgorithm)); + + // Step 1: If highWaterMark was not passed, set it to 1 (implicit). + // Step 2: If sizeAlgorithm was not passed, set it to an algorithm that + // returns 1 (implicit). + // Step 3: Assert: ! IsNonNegativeNumber(highWaterMark) is true. + MOZ_ASSERT(highWaterMark >= 0); + + // Step 4: Let stream be ObjectCreate(the original value of ReadableStream's + // prototype property). + // Step 5: Perform ! InitializeReadableStream(stream). + Rooted stream(cx, + ReadableStream::create(cx, nullptr, proto)); + if (!stream) { + return nullptr; + } + + // Step 6: Let controller be ObjectCreate(the original value of + // ReadableStreamDefaultController's prototype property). + // Step 7: Perform ? SetUpReadableStreamDefaultController(stream, + // controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, + // highWaterMark, sizeAlgorithm). + if (!SetUpReadableStreamDefaultController( + cx, stream, sourceAlgorithms, underlyingSource, pullMethod, + cancelMethod, highWaterMark, sizeAlgorithm)) { + return nullptr; + } + + // Step 8: Return stream. + return stream; +} + +// Streams spec, 3.4.4. CreateReadableByteStream ( +// startAlgorithm, pullAlgorithm, cancelAlgorithm +// [, highWaterMark [, autoAllocateChunkSize ] ] ) +// Not implemented. + +/** + * Streams spec, 3.4.5. InitializeReadableStream ( stream ) + */ +/* static */ [[nodiscard]] ReadableStream* ReadableStream::create( + JSContext* cx, void* nsISupportsObject_alreadyAddreffed /* = nullptr */, + Handle proto /* = nullptr */) { + // In the spec, InitializeReadableStream is always passed a newly created + // ReadableStream object. We instead create it here and return it below. + Rooted stream( + cx, NewObjectWithClassProto(cx, proto)); + if (!stream) { + return nullptr; + } + + static_assert(Slot_ISupports == 0, + "Must use right slot for JSCLASS_SLOT0_IS_NSISUPPORTS"); + JS::SetObjectISupports(stream, nsISupportsObject_alreadyAddreffed); + + // Step 1: Set stream.[[state]] to "readable". + stream->initStateBits(Readable); + MOZ_ASSERT(stream->readable()); + + // Step 2: Set stream.[[reader]] and stream.[[storedError]] to + // undefined (implicit). + MOZ_ASSERT(!stream->hasReader()); + MOZ_ASSERT(stream->storedError().isUndefined()); + + // Step 3: Set stream.[[disturbed]] to false (done in step 1). + MOZ_ASSERT(!stream->disturbed()); + + return stream; +} + +// Streams spec, 3.4.6. IsReadableStream ( x ) +// Using UnwrapAndTypeCheck templates instead. + +// Streams spec, 3.4.7. IsReadableStreamDisturbed ( stream ) +// Using stream->disturbed() instead. + +/** + * Streams spec, 3.4.8. IsReadableStreamLocked ( stream ) + */ +bool ReadableStream::locked() const { + // Step 1: Assert: ! IsReadableStream(stream) is true (implicit). + // Step 2: If stream.[[reader]] is undefined, return false. + // Step 3: Return true. + // Special-casing for streams with external sources. Those can be locked + // explicitly via JSAPI, which is indicated by a controller flag. + // IsReadableStreamLocked is called from the controller's constructor, at + // which point we can't yet call stream->controller(), but the source also + // can't be locked yet. + if (hasController() && controller()->sourceLocked()) { + return true; + } + return hasReader(); +} + +// Streams spec, 3.4.9. IsReadableStreamAsyncIterator ( x ) +// +// Not implemented. + +/** + * Streams spec, 3.4.10. ReadableStreamTee steps 12.c.i-x. + */ +static bool TeeReaderReadHandler(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + Rooted unwrappedTeeState(cx, + UnwrapCalleeSlot(cx, args, 0)); + if (!unwrappedTeeState) { + return false; + } + + Handle resultVal = args.get(0); + + // Step 12.c.i: Set reading to false. + unwrappedTeeState->unsetReading(); + + // Step 12.c.ii: Assert: Type(result) is Object. + Rooted result(cx, &resultVal.toObject()); + + bool done; + { + // Step 12.c.iii: Let done be ? Get(result, "done"). + // (This can fail only if `result` was nuked.) + Rooted doneVal(cx); + if (!GetProperty(cx, result, result, cx->names().done, &doneVal)) { + return false; + } + + // Step 12.c.iv: Assert: Type(done) is Boolean. + done = doneVal.toBoolean(); + } + + if (done) { + // Step 12.3 close steps + + // Step 1: Set reading to false (done unconditionally above). + // Step 2: If canceled1 is false, perform + // ! ReadableStreamDefaultControllerClose(branch1.[[controller]]). + if (!unwrappedTeeState->canceled1()) { + Rooted unwrappedBranch1( + cx, unwrappedTeeState->branch1()); + if (!ReadableStreamDefaultControllerClose(cx, unwrappedBranch1)) { + return false; + } + } + + // Step 3: If canceled2 is false, perform + // ! ReadableStreamDefaultControllerClose(branch2.[[controller]]). + if (!unwrappedTeeState->canceled2()) { + Rooted unwrappedBranch2( + cx, unwrappedTeeState->branch2()); + if (!ReadableStreamDefaultControllerClose(cx, unwrappedBranch2)) { + return false; + } + } + + // Step 4: If canceled1 is false or canceled2 is false, + // resolve cancelPromise with undefined. + if (!unwrappedTeeState->canceled1() || !unwrappedTeeState->canceled2()) { + Rooted unwrappedCancelPromise( + cx, unwrappedTeeState->cancelPromise()); + MOZ_ASSERT(unwrappedCancelPromise != nullptr); + + if (!ResolveUnwrappedPromiseWithUndefined(cx, unwrappedCancelPromise)) { + return false; + } + } + + args.rval().setUndefined(); + return true; + } + + // Step 12.c.vi: Let value be ! Get(result, "value"). + // (This can fail only if `result` was nuked.) + Rooted value(cx); + if (!GetProperty(cx, result, result, cx->names().value, &value)) { + return false; + } + + // Step 12.c.vii: Let value1 and value2 be value. + // Step 12.c.viii: If canceled2 is false and cloneForBranch2 is true, set + // value2 to + // ? StructuredDeserialize(? StructuredSerialize(value2), + // the current Realm Record). + // We don't yet support any specifications that use cloneForBranch2, and + // the Streams spec doesn't offer any way for author code to enable it, + // so it's always false here. + auto& value1 = value; + MOZ_ASSERT(!unwrappedTeeState->cloneForBranch2(), + "support for cloneForBranch2=true is not yet implemented"); + auto& value2 = value; + + Rooted unwrappedController(cx); + + // Step 12.c.ix: If canceled1 is false, perform + // ? ReadableStreamDefaultControllerEnqueue( + // branch1.[[readableStreamController]], value1). + if (!unwrappedTeeState->canceled1()) { + unwrappedController = unwrappedTeeState->branch1(); + if (!ReadableStreamDefaultControllerEnqueue(cx, unwrappedController, + value1)) { + return false; + } + } + + // Step 12.c.x: If canceled2 is false, perform + // ? ReadableStreamDefaultControllerEnqueue( + // branch2.[[readableStreamController]], value2). + if (!unwrappedTeeState->canceled2()) { + unwrappedController = unwrappedTeeState->branch2(); + if (!ReadableStreamDefaultControllerEnqueue(cx, unwrappedController, + value2)) { + return false; + } + } + + args.rval().setUndefined(); + return true; +} + +/** + * Streams spec, 3.4.10. ReadableStreamTee step 12, "Let pullAlgorithm be the + * following steps:" + */ +[[nodiscard]] PromiseObject* js::ReadableStreamTee_Pull( + JSContext* cx, JS::Handle unwrappedTeeState) { + // Combine step 12.a/12.e far below, and handle steps 12.b-12.d after + // inverting step 12.a's "If reading is true" condition. + if (!unwrappedTeeState->reading()) { + // Step 12.b: Set reading to true. + unwrappedTeeState->setReading(); + + // Implicit in the spec: Unpack `reader` from the TeeState (by way of the + // stream stored in one of its slots). + Rooted unwrappedReader(cx); + { + Rooted unwrappedStream( + cx, UnwrapInternalSlot(cx, unwrappedTeeState, + TeeState::Slot_Stream)); + if (!unwrappedStream) { + return nullptr; + } + ReadableStreamReader* unwrappedReaderObj = + UnwrapReaderFromStream(cx, unwrappedStream); + if (!unwrappedReaderObj) { + return nullptr; + } + + unwrappedReader = &unwrappedReaderObj->as(); + } + + // Step 12.c: Let readPromise be the result of reacting to + // ! ReadableStreamDefaultReaderRead(reader) with the following + // fulfillment steps given the argument result: [...] + // Step 12.d: Set readPromise.[[PromiseIsHandled]] to true. + + // First, perform |ReadableStreamDefaultReaderRead(reader)|. + Rooted readerReadResultPromise( + cx, js::ReadableStreamDefaultReaderRead(cx, unwrappedReader)); + if (!readerReadResultPromise) { + return nullptr; + } + + // Next, create a function to perform the fulfillment steps under step 12.c + // (implemented in the |TeeReaderReadHandler| C++ function). + Rooted teeState(cx, unwrappedTeeState); + if (!cx->compartment()->wrap(cx, &teeState)) { + return nullptr; + } + + Rooted onFulfilled( + cx, NewHandler(cx, TeeReaderReadHandler, teeState)); + if (!onFulfilled) { + return nullptr; + } + + // Finally, perform those fulfillment steps when |readerReadResultPromise| + // fulfills. (Step 12.c doesn't provide rejection steps, so don't handle + // rejection.) + // + // The spec's |readPromise| promise is unobservable, so implement this using + // a JSAPI function that acts as if it created |readPromise| but doesn't + // actually do so. + // + // Step 12.d causes |readPromise| to be treated as handled, even if it + // rejects. Use |JS::AddPromiseReactionsIgnoringUnhandledRejection|, not + // |JS::AddPromiseReactions|, to avoid reporting a freshly-consed-up promise + // as rejected if |readerReadResultPromise| rejects. + if (!JS::AddPromiseReactionsIgnoringUnhandledRejection( + cx, readerReadResultPromise, onFulfilled, nullptr)) { + return nullptr; + } + } + + // Step 12.a: (If reading is true,) return a promise resolved with undefined. + // Step 12.e: Return a promise resolved with undefined. + return PromiseResolvedWithUndefined(cx); +} + +/** + * Cancel one branch of a tee'd stream with the given |reason_|. + * + * Streams spec, 3.4.10. ReadableStreamTee steps 13 and 14: "Let + * cancel1Algorithm/cancel2Algorithm be the following steps, taking a reason + * argument:" + */ +[[nodiscard]] JSObject* js::ReadableStreamTee_Cancel( + JSContext* cx, JS::Handle unwrappedTeeState, + JS::Handle unwrappedBranch, + JS::Handle reason) { + Rooted unwrappedStream( + cx, UnwrapInternalSlot(cx, unwrappedTeeState, + TeeState::Slot_Stream)); + if (!unwrappedStream) { + return nullptr; + } + + bool bothBranchesCanceled = false; + + // Step 13/14.a: Set canceled1/canceled2 to true. + // Step 13/14.b: Set reason1/reason2 to reason. + { + AutoRealm ar(cx, unwrappedTeeState); + + Rooted unwrappedReason(cx, reason); + if (!cx->compartment()->wrap(cx, &unwrappedReason)) { + return nullptr; + } + + if (unwrappedBranch->isTeeBranch1()) { + unwrappedTeeState->setCanceled1(unwrappedReason); + bothBranchesCanceled = unwrappedTeeState->canceled2(); + } else { + MOZ_ASSERT(unwrappedBranch->isTeeBranch2()); + unwrappedTeeState->setCanceled2(unwrappedReason); + bothBranchesCanceled = unwrappedTeeState->canceled1(); + } + } + + Rooted unwrappedCancelPromise( + cx, unwrappedTeeState->cancelPromise()); + MOZ_ASSERT(unwrappedCancelPromise != nullptr); + + // Step 13/14.c: If canceled2/canceled1 is true, + if (bothBranchesCanceled) { + // Step 13/14.c.i: Let compositeReason be + // ! CreateArrayFromList(« reason1, reason2 »). + Rooted compositeReason(cx); + { + Rooted reason1(cx, unwrappedTeeState->reason1()); + Rooted reason2(cx, unwrappedTeeState->reason2()); + if (!cx->compartment()->wrap(cx, &reason1) || + !cx->compartment()->wrap(cx, &reason2)) { + return nullptr; + } + + ArrayObject* reasonArray = NewDenseFullyAllocatedArray(cx, 2); + if (!reasonArray) { + return nullptr; + } + reasonArray->setDenseInitializedLength(2); + reasonArray->initDenseElement(0, reason1); + reasonArray->initDenseElement(1, reason2); + + compositeReason = ObjectValue(*reasonArray); + } + + // Step 13/14.c.ii: Let cancelResult be + // ! ReadableStreamCancel(stream, compositeReason). + // In our implementation, this can fail with OOM. The best course then + // is to reject cancelPromise with an OOM error. + Rooted cancelResult( + cx, js::ReadableStreamCancel(cx, unwrappedStream, compositeReason)); + if (!cancelResult) { + // Handle the OOM case mentioned above. + AutoRealm ar(cx, unwrappedCancelPromise); + if (!RejectPromiseWithPendingError(cx, unwrappedCancelPromise)) { + return nullptr; + } + } else { + // Step 13/14.c.iii: Resolve cancelPromise with cancelResult. + Rooted cancelResultVal(cx, ObjectValue(*cancelResult)); + if (!ResolveUnwrappedPromiseWithValue(cx, unwrappedCancelPromise, + cancelResultVal)) { + return nullptr; + } + } + } + + // Step 13/14.d: Return cancelPromise. + Rooted cancelPromise(cx, unwrappedCancelPromise); + if (!cx->compartment()->wrap(cx, &cancelPromise)) { + return nullptr; + } + + return cancelPromise; +} + +/* + * https://streams.spec.whatwg.org/#readable-stream-tee + * ReadableStreamTee(stream, cloneForBranch2) + * + * Step 18: Upon rejection of reader.[[closedPromise]] with reason r, + */ +static bool TeeReaderErroredHandler(JSContext* cx, unsigned argc, + JS::Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + Rooted teeState(cx, TargetFromHandler(args)); + Handle reason = args.get(0); + + Rooted unwrappedBranchController(cx); + + // Step 18.1: Perform + // ! ReadableStreamDefaultControllerError( + // branch1.[[controller]], r). + unwrappedBranchController = teeState->branch1(); + if (!ReadableStreamControllerError(cx, unwrappedBranchController, reason)) { + return false; + } + + // Step 18.2: Perform + // ! ReadableStreamDefaultControllerError( + // branch2.[[controller]], r). + unwrappedBranchController = teeState->branch2(); + if (!ReadableStreamControllerError(cx, unwrappedBranchController, reason)) { + return false; + } + + // Step 18.3: If canceled1 is false or canceled2 is false, + // resolve cancelPromise with undefined. + if (!teeState->canceled1() || !teeState->canceled2()) { + Rooted unwrappedCancelPromise(cx, + teeState->cancelPromise()); + MOZ_ASSERT(unwrappedCancelPromise != nullptr); + + if (!ResolveUnwrappedPromiseWithUndefined(cx, unwrappedCancelPromise)) { + return false; + } + } + + args.rval().setUndefined(); + return true; +} + +/** + * Streams spec, 3.4.10. ReadableStreamTee ( stream, cloneForBranch2 ) + */ +[[nodiscard]] bool js::ReadableStreamTee( + JSContext* cx, JS::Handle unwrappedStream, + bool cloneForBranch2, JS::MutableHandle branch1Stream, + JS::MutableHandle branch2Stream) { + // Step 1: Assert: ! IsReadableStream(stream) is true (implicit). + + // Step 2: Assert: Type(cloneForBranch2) is Boolean (implicit). + // + // The streams spec only ever passes |cloneForBranch2 = false|. It's expected + // that external specs that pass |cloneForBranch2 = true| will at some point + // come into existence, but we don't presently implement any such specs. + MOZ_ASSERT(!cloneForBranch2, + "support for cloneForBranch2=true is not yet implemented"); + + // Step 3: Let reader be ? AcquireReadableStreamDefaultReader(stream). + Rooted reader( + cx, CreateReadableStreamDefaultReader(cx, unwrappedStream, + ForAuthorCodeBool::No)); + if (!reader) { + return false; + } + + // Several algorithms close over the variables initialized in the next few + // steps, so we allocate them in an object, the TeeState. The algorithms + // also close over `stream` and `reader`, so TeeState gets a reference to + // the stream. + // + // Step 4: Let reading be false. + // Step 5: Let canceled1 be false. + // Step 6: Let canceled2 be false. + // Step 7: Let reason1 be undefined. + // Step 8: Let reason2 be undefined. + // Step 9: Let branch1 be undefined. + // Step 10: Let branch2 be undefined. + // Step 11: Let cancelPromise be a new promise. + Rooted teeState(cx, TeeState::create(cx, unwrappedStream)); + if (!teeState) { + return false; + } + + MOZ_ASSERT(!teeState->reading()); + MOZ_ASSERT(!teeState->canceled1()); + MOZ_ASSERT(!teeState->canceled2()); + + // Step 12: Let pullAlgorithm be the following steps: [...] + // Step 13: Let cancel1Algorithm be the following steps: [...] + // Step 14: Let cancel2Algorithm be the following steps: [...] + // Step 15: Let startAlgorithm be an algorithm that returns undefined. + // + // Implicit. Our implementation does not use objects to represent + // [[pullAlgorithm]], [[cancelAlgorithm]], and so on. Instead, we decide + // which one to perform based on class checks. For example, our + // implementation of ReadableStreamControllerCallPullIfNeeded checks + // whether the stream's underlyingSource is a TeeState object. + + // Step 16: Set branch1 to + // ! CreateReadableStream(startAlgorithm, pullAlgorithm, + // cancel1Algorithm). + Rooted underlyingSource(cx, ObjectValue(*teeState)); + branch1Stream.set( + CreateReadableStream(cx, SourceAlgorithms::Tee, underlyingSource)); + if (!branch1Stream) { + return false; + } + + Rooted branch1(cx); + branch1 = &branch1Stream->controller()->as(); + branch1->setTeeBranch1(); + teeState->setBranch1(branch1); + + // Step 17: Set branch2 to + // ! CreateReadableStream(startAlgorithm, pullAlgorithm, + // cancel2Algorithm). + branch2Stream.set( + CreateReadableStream(cx, SourceAlgorithms::Tee, underlyingSource)); + if (!branch2Stream) { + return false; + } + + Rooted branch2(cx); + branch2 = &branch2Stream->controller()->as(); + branch2->setTeeBranch2(); + teeState->setBranch2(branch2); + + // Step 18: Upon rejection of reader.[[closedPromise]] with reason r, [...] + Rooted closedPromise(cx, reader->closedPromise()); + + Rooted onRejected( + cx, NewHandler(cx, TeeReaderErroredHandler, teeState)); + if (!onRejected) { + return false; + } + + if (!JS::AddPromiseReactions(cx, closedPromise, nullptr, onRejected)) { + return false; + } + + // Step 19: Return « branch1, branch2 ». + return true; +} diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamOperations.h b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamOperations.h new file mode 100644 index 00000000000..0936c814d41 --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamOperations.h @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* General readable stream abstract operations. */ + +#ifndef builtin_streams_ReadableStreamOperations_h +#define builtin_streams_ReadableStreamOperations_h + +#include "js/RootingAPI.h" // JS::Handle +#include "js/Value.h" // JS::Value + +class JS_PUBLIC_API JSObject; + +namespace js { + +class PromiseObject; +class ReadableStream; +class ReadableStreamDefaultController; +class TeeState; + +[[nodiscard]] extern PromiseObject* ReadableStreamTee_Pull( + JSContext* cx, JS::Handle unwrappedTeeState); + +[[nodiscard]] extern JSObject* ReadableStreamTee_Cancel( + JSContext* cx, JS::Handle unwrappedTeeState, + JS::Handle unwrappedBranch, + JS::Handle reason); + +[[nodiscard]] extern bool ReadableStreamTee( + JSContext* cx, JS::Handle unwrappedStream, + bool cloneForBranch2, JS::MutableHandle branch1Stream, + JS::MutableHandle branch2Stream); + +} // namespace js + +#endif // builtin_streams_ReadableStreamOperations_h diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamReader-inl.h b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamReader-inl.h new file mode 100644 index 00000000000..e3b4eabbf8a --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamReader-inl.h @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef builtin_streams_ReadableStreamReader_inl_h +#define builtin_streams_ReadableStreamReader_inl_h + +#include "builtin/streams/ReadableStreamReader.h" + +#include "mozilla/Assertions.h" // MOZ_ASSERT + +#include "jsfriendapi.h" // JS_IsDeadWrapper + +#include "builtin/streams/ReadableStream.h" // js::ReadableStream +#include "js/Proxy.h" // js::IsProxy +#include "js/RootingAPI.h" // JS::Handle +#include "vm/NativeObject.h" // js::NativeObject::getFixedSlot + +#include "vm/Compartment-inl.h" // js::UnwrapInternalSlot + +namespace js { + +/** + * Returns the stream associated with the given reader. + */ +[[nodiscard]] inline ReadableStream* UnwrapStreamFromReader( + JSContext* cx, JS::Handle reader) { + MOZ_ASSERT(reader->hasStream()); + return UnwrapInternalSlot(cx, reader, + ReadableStreamReader::Slot_Stream); +} + +/** + * Returns the reader associated with the given stream. + * + * Must only be called on ReadableStreams that already have a reader + * associated with them. + * + * If the reader is a wrapper, it will be unwrapped, so the result might not be + * an object from the currently active compartment. + */ +[[nodiscard]] inline ReadableStreamReader* UnwrapReaderFromStream( + JSContext* cx, JS::Handle stream) { + return UnwrapInternalSlot(cx, stream, + ReadableStream::Slot_Reader); +} + +[[nodiscard]] inline ReadableStreamReader* UnwrapReaderFromStreamNoThrow( + ReadableStream* stream) { + JSObject* readerObj = + &stream->getFixedSlot(ReadableStream::Slot_Reader).toObject(); + if (IsProxy(readerObj)) { + if (JS_IsDeadWrapper(readerObj)) { + return nullptr; + } + + readerObj = readerObj->maybeUnwrapAs(); + if (!readerObj) { + return nullptr; + } + } + + return &readerObj->as(); +} + +} // namespace js + +#endif // builtin_streams_ReadableStreamReader_inl_h diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamReader.cpp b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamReader.cpp new file mode 100644 index 00000000000..959d694394b --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamReader.cpp @@ -0,0 +1,275 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* ReadableStream reader abstract operations. */ + +#include "builtin/streams/ReadableStreamReader-inl.h" + +#include "mozilla/Assertions.h" // MOZ_ASSERT{,_IF} + +#include "jsfriendapi.h" // JS_ReportErrorNumberASCII + +#include "builtin/Stream.h" // js::ReadableStreamController, js::ReadableStreamControllerPullSteps +#include "builtin/streams/ReadableStream.h" // js::ReadableStream +#include "builtin/streams/ReadableStreamController.h" // js::ReadableStreamController +#include "builtin/streams/ReadableStreamInternals.h" // js::ReadableStream{Cancel,CreateReadResult} +#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* +#include "js/RootingAPI.h" // JS::Handle, JS::Rooted +#include "js/Value.h" // JS::Value, JS::UndefinedHandleValue +#include "vm/Interpreter.h" // js::GetAndClearException +#include "vm/JSContext.h" // JSContext +#include "vm/PlainObject.h" // js::PlainObject +#include "vm/PromiseObject.h" // js::PromiseObject, js::PromiseResolvedWithUndefined +#include "vm/Runtime.h" // JSRuntime + +#include "builtin/Promise-inl.h" // js::SetSettledPromiseIsHandled +#include "vm/Compartment-inl.h" // JS::Compartment::wrap, js::UnwrapInternalSlot +#include "vm/List-inl.h" // js::StoreNewListInFixedSlot +#include "vm/Realm-inl.h" // js::AutoRealm + +using JS::Handle; +using JS::Rooted; +using JS::Value; + +using js::PromiseObject; +using js::ReadableStreamController; +using js::UnwrapStreamFromReader; + +/*** 3.8. Readable stream reader abstract operations ************************/ + +// Streams spec, 3.8.1. IsReadableStreamDefaultReader ( x ) +// Implemented via is() + +// Streams spec, 3.8.2. IsReadableStreamBYOBReader ( x ) +// Implemented via is() + +/** + * Streams spec, 3.8.3. ReadableStreamReaderGenericCancel ( reader, reason ) + */ +[[nodiscard]] JSObject* js::ReadableStreamReaderGenericCancel( + JSContext* cx, Handle unwrappedReader, + Handle reason) { + // Step 1: Let stream be reader.[[ownerReadableStream]]. + // Step 2: Assert: stream is not undefined (implicit). + Rooted unwrappedStream( + cx, UnwrapStreamFromReader(cx, unwrappedReader)); + if (!unwrappedStream) { + return nullptr; + } + + // Step 3: Return ! ReadableStreamCancel(stream, reason). + return js::ReadableStreamCancel(cx, unwrappedStream, reason); +} + +/** + * Streams spec, 3.8.4. + * ReadableStreamReaderGenericInitialize ( reader, stream ) + */ +[[nodiscard]] bool js::ReadableStreamReaderGenericInitialize( + JSContext* cx, Handle reader, + Handle unwrappedStream, ForAuthorCodeBool forAuthorCode) { + cx->check(reader); + + // Step 1: Set reader.[[forAuthorCode]] to true. + reader->setForAuthorCode(forAuthorCode); + + // Step 2: Set reader.[[ownerReadableStream]] to stream. + { + Rooted readerCompartmentStream(cx, unwrappedStream); + if (!cx->compartment()->wrap(cx, &readerCompartmentStream)) { + return false; + } + reader->setStream(readerCompartmentStream); + } + + // Step 3 is moved to the end. + + // Step 4: If stream.[[state]] is "readable", + Rooted promise(cx); + if (unwrappedStream->readable()) { + // Step a: Set reader.[[closedPromise]] to a new promise. + promise = PromiseObject::createSkippingExecutor(cx); + } else if (unwrappedStream->closed()) { + // Step 5: Otherwise, if stream.[[state]] is "closed", + // Step a: Set reader.[[closedPromise]] to a promise resolved with + // undefined. + promise = PromiseResolvedWithUndefined(cx); + } else { + // Step 6: Otherwise, + // Step a: Assert: stream.[[state]] is "errored". + MOZ_ASSERT(unwrappedStream->errored()); + + // Step b: Set reader.[[closedPromise]] to a promise rejected with + // stream.[[storedError]]. + Rooted storedError(cx, unwrappedStream->storedError()); + if (!cx->compartment()->wrap(cx, &storedError)) { + return false; + } + promise = PromiseObject::unforgeableReject(cx, storedError); + if (!promise) { + return false; + } + + // Step c. Set reader.[[closedPromise]].[[PromiseIsHandled]] to true. + js::SetSettledPromiseIsHandled(cx, promise); + } + + if (!promise) { + return false; + } + + reader->setClosedPromise(promise); + + // Step 4 of caller 3.6.3. new ReadableStreamDefaultReader(stream): + // Step 5 of caller 3.7.3. new ReadableStreamBYOBReader(stream): + // Set this.[[read{Into}Requests]] to a new empty List. + if (!StoreNewListInFixedSlot(cx, reader, + ReadableStreamReader::Slot_Requests)) { + return false; + } + + // Step 3: Set stream.[[reader]] to reader. + // Doing this last prevents a partially-initialized reader from being + // attached to the stream (and possibly left there on OOM). + { + AutoRealm ar(cx, unwrappedStream); + Rooted streamCompartmentReader(cx, reader); + if (!cx->compartment()->wrap(cx, &streamCompartmentReader)) { + return false; + } + unwrappedStream->setReader(streamCompartmentReader); + } + + return true; +} + +/** + * Streams spec, 3.8.5. ReadableStreamReaderGenericRelease ( reader ) + */ +[[nodiscard]] bool js::ReadableStreamReaderGenericRelease( + JSContext* cx, Handle unwrappedReader) { + // Step 1: Assert: reader.[[ownerReadableStream]] is not undefined. + Rooted unwrappedStream( + cx, UnwrapStreamFromReader(cx, unwrappedReader)); + if (!unwrappedStream) { + return false; + } + + // Step 2: Assert: reader.[[ownerReadableStream]].[[reader]] is reader. +#ifdef DEBUG + // The assertion is weakened a bit to allow for nuked wrappers. + ReadableStreamReader* unwrappedReader2 = + UnwrapReaderFromStreamNoThrow(unwrappedStream); + MOZ_ASSERT_IF(unwrappedReader2, unwrappedReader2 == unwrappedReader); +#endif + + // Create an exception to reject promises with below. We don't have a + // clean way to do this, unfortunately. + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_READABLESTREAMREADER_RELEASED); + Rooted exn(cx); + if (!cx->isExceptionPending() || !GetAndClearException(cx, &exn)) { + // Uncatchable error. Die immediately without resolving + // reader.[[closedPromise]]. + return false; + } + + // Step 3: If reader.[[ownerReadableStream]].[[state]] is "readable", reject + // reader.[[closedPromise]] with a TypeError exception. + Rooted unwrappedClosedPromise(cx); + if (unwrappedStream->readable()) { + unwrappedClosedPromise = UnwrapInternalSlot( + cx, unwrappedReader, ReadableStreamReader::Slot_ClosedPromise); + if (!unwrappedClosedPromise) { + return false; + } + + AutoRealm ar(cx, unwrappedClosedPromise); + if (!cx->compartment()->wrap(cx, &exn)) { + return false; + } + if (!PromiseObject::reject(cx, unwrappedClosedPromise, exn)) { + return false; + } + } else { + // Step 4: Otherwise, set reader.[[closedPromise]] to a new promise + // rejected with a TypeError exception. + Rooted closedPromise(cx, + PromiseObject::unforgeableReject(cx, exn)); + if (!closedPromise) { + return false; + } + unwrappedClosedPromise = &closedPromise->as(); + + AutoRealm ar(cx, unwrappedReader); + if (!cx->compartment()->wrap(cx, &closedPromise)) { + return false; + } + unwrappedReader->setClosedPromise(closedPromise); + } + + // Step 5: Set reader.[[closedPromise]].[[PromiseIsHandled]] to true. + js::SetSettledPromiseIsHandled(cx, unwrappedClosedPromise); + + // Step 6: Set reader.[[ownerReadableStream]].[[reader]] to undefined. + unwrappedStream->clearReader(); + + // Step 7: Set reader.[[ownerReadableStream]] to undefined. + unwrappedReader->clearStream(); + + return true; +} + +/** + * Streams spec, 3.8.7. + * ReadableStreamDefaultReaderRead ( reader [, forAuthorCode ] ) + */ +[[nodiscard]] PromiseObject* js::ReadableStreamDefaultReaderRead( + JSContext* cx, Handle unwrappedReader) { + // Step 1: If forAuthorCode was not passed, set it to false (implicit). + + // Step 2: Let stream be reader.[[ownerReadableStream]]. + // Step 3: Assert: stream is not undefined. + Rooted unwrappedStream( + cx, UnwrapStreamFromReader(cx, unwrappedReader)); + if (!unwrappedStream) { + return nullptr; + } + + // Step 4: Set stream.[[disturbed]] to true. + unwrappedStream->setDisturbed(); + + // Step 5: If stream.[[state]] is "closed", return a promise resolved with + // ! ReadableStreamCreateReadResult(undefined, true, forAuthorCode). + if (unwrappedStream->closed()) { + PlainObject* iterResult = ReadableStreamCreateReadResult( + cx, UndefinedHandleValue, true, unwrappedReader->forAuthorCode()); + if (!iterResult) { + return nullptr; + } + + Rooted iterResultVal(cx, JS::ObjectValue(*iterResult)); + return PromiseObject::unforgeableResolveWithNonPromise(cx, iterResultVal); + } + + // Step 6: If stream.[[state]] is "errored", return a promise rejected + // with stream.[[storedError]]. + if (unwrappedStream->errored()) { + Rooted storedError(cx, unwrappedStream->storedError()); + if (!cx->compartment()->wrap(cx, &storedError)) { + return nullptr; + } + return PromiseObject::unforgeableReject(cx, storedError); + } + + // Step 7: Assert: stream.[[state]] is "readable". + MOZ_ASSERT(unwrappedStream->readable()); + + // Step 8: Return ! stream.[[readableStreamController]].[[PullSteps]](). + Rooted unwrappedController( + cx, unwrappedStream->controller()); + return ReadableStreamControllerPullSteps(cx, unwrappedController); +} diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamReader.h b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamReader.h new file mode 100644 index 00000000000..055ce827feb --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/ReadableStreamReader.h @@ -0,0 +1,154 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* ReadableStream readers and generic reader operations. */ + +#ifndef builtin_streams_ReadableStreamReader_h +#define builtin_streams_ReadableStreamReader_h + +#include "jstypes.h" // JS_PUBLIC_API +#include "js/Class.h" // JSClass, js::ClassSpec +#include "js/RootingAPI.h" // JS::Handle +#include "js/Value.h" // JS::{,Boolean,Object,Undefined}Value +#include "vm/JSObject.h" // JSObject::is +#include "vm/List.h" // js::ListObject +#include "vm/NativeObject.h" // js::NativeObject + +struct JS_PUBLIC_API JSContext; + +namespace js { + +class PromiseObject; +class ReadableStream; + +/** + * Tells whether or not read() result objects inherit from Object.prototype. + * Generally, they should do so only if the reader was created by author code. + * See . + */ +enum class ForAuthorCodeBool { No, Yes }; + +class ReadableStreamReader : public NativeObject { + public: + /** + * Memory layout of Stream Reader instances. + * + * See https://streams.spec.whatwg.org/#default-reader-internal-slots and + * https://streams.spec.whatwg.org/#byob-reader-internal-slots for details. + * + * Note that [[readRequests]] and [[readIntoRequests]] are treated the same + * in our implementation. + * + * Of the stored values, Stream and ClosedPromise might be + * cross-compartment wrapper wrappers. + * + * For Stream, this can happen if the Reader was created by applying a + * different compartment's ReadableStream.prototype.getReader method. + * + * For ClosedPromise, it can be caused by applying a different + * compartment's ReadableStream*Reader.prototype.releaseLock method. + * + * Requests is guaranteed to be in the same compartment as the Reader, but + * can contain wrapped request objects from other globals. + */ + enum Slots { + Slot_Stream, + Slot_Requests, + Slot_ClosedPromise, + Slot_ForAuthorCode, + SlotCount, + }; + + bool hasStream() const { return !getFixedSlot(Slot_Stream).isUndefined(); } + void setStream(JSObject* stream) { + setFixedSlot(Slot_Stream, JS::ObjectValue(*stream)); + } + void clearStream() { setFixedSlot(Slot_Stream, JS::UndefinedValue()); } + bool isClosed() { return !hasStream(); } + + /** + * Tells whether this reader was created by author code. + * + * This returns Yes for readers created using `stream.getReader()`, and No + * for readers created for the internal use of algorithms like + * `stream.tee()` and `new Response(stream)`. + * + * The standard does not have this field. Instead, eight algorithms take a + * forAuthorCode parameter, and a [[forAuthorCode]] field is part of each + * read request. But the behavior is always equivalent to treating readers + * created by author code as having a bit set on them. We implement it that + * way for simplicity. + */ + ForAuthorCodeBool forAuthorCode() const { + return getFixedSlot(Slot_ForAuthorCode).toBoolean() ? ForAuthorCodeBool::Yes + : ForAuthorCodeBool::No; + } + void setForAuthorCode(ForAuthorCodeBool value) { + setFixedSlot(Slot_ForAuthorCode, + JS::BooleanValue(value == ForAuthorCodeBool::Yes)); + } + + ListObject* requests() const { + return &getFixedSlot(Slot_Requests).toObject().as(); + } + void clearRequests() { setFixedSlot(Slot_Requests, JS::UndefinedValue()); } + + JSObject* closedPromise() const { + return &getFixedSlot(Slot_ClosedPromise).toObject(); + } + void setClosedPromise(JSObject* wrappedPromise) { + setFixedSlot(Slot_ClosedPromise, JS::ObjectValue(*wrappedPromise)); + } + + static const JSClass class_; +}; + +class ReadableStreamDefaultReader : public ReadableStreamReader { + public: + static bool constructor(JSContext* cx, unsigned argc, JS::Value* vp); + static const ClassSpec classSpec_; + static const JSClass class_; + static const ClassSpec protoClassSpec_; + static const JSClass protoClass_; +}; + +[[nodiscard]] extern ReadableStreamDefaultReader* +CreateReadableStreamDefaultReader(JSContext* cx, + JS::Handle unwrappedStream, + ForAuthorCodeBool forAuthorCode, + JS::Handle proto = nullptr); + +[[nodiscard]] extern JSObject* ReadableStreamReaderGenericCancel( + JSContext* cx, JS::Handle unwrappedReader, + JS::Handle reason); + +[[nodiscard]] extern bool ReadableStreamReaderGenericInitialize( + JSContext* cx, JS::Handle reader, + JS::Handle unwrappedStream, + ForAuthorCodeBool forAuthorCode); + +[[nodiscard]] extern bool ReadableStreamReaderGenericRelease( + JSContext* cx, JS::Handle unwrappedReader); + +[[nodiscard]] extern PromiseObject* ReadableStreamDefaultReaderRead( + JSContext* cx, JS::Handle unwrappedReader); + +} // namespace js + +template <> +inline bool JSObject::is() const { + return is(); +} + +namespace js { + +[[nodiscard]] extern JSObject* CreateReadableStreamBYOBReader( + JSContext* cx, JS::Handle unwrappedStream, + ForAuthorCodeBool forAuthorCode, JS::Handle proto = nullptr); + +} // namespace js + +#endif // builtin_streams_ReadableStreamReader_h diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/StreamAPI.cpp b/mozjs-sys/mozjs/js/src/builtin/streams/StreamAPI.cpp new file mode 100644 index 00000000000..8d88412b6be --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/StreamAPI.cpp @@ -0,0 +1,603 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Public and friend stream APIs for external use. */ + +#include "mozilla/Assertions.h" // MOZ_ASSERT{,_IF} + +#include // uint32_t, uintptr_t + +#include "jsfriendapi.h" // js::IsObjectInContextCompartment +#include "jstypes.h" // JS_{FRIEND,PUBLIC}_API + +#include "builtin/Stream.h" // js::ReadableByteStreamController{,Close}, js::ReadableStreamDefaultController{,Close}, js::StreamController +#include "builtin/streams/ReadableStream.h" // js::ReadableStream +#include "builtin/streams/ReadableStreamController.h" // js::CheckReadableStreamControllerCanCloseOrEnqueue +#include "builtin/streams/ReadableStreamDefaultControllerOperations.h" // js::ReadableStreamController{Error,GetDesiredSizeUnchecked}, js::SetUpReadableStreamDefaultControllerFromUnderlyingSource +#include "builtin/streams/ReadableStreamInternals.h" // js::ReadableStream{Cancel,FulfillReadOrReadIntoRequest,GetNumReadRequests,HasDefaultReader} +#include "builtin/streams/ReadableStreamOperations.h" // js::ReadableStreamTee +#include "builtin/streams/ReadableStreamReader.h" // js::ReadableStream{,Default}Reader, js::ForAuthorCodeBool +#include "builtin/streams/StreamController.h" // js::StreamController +#include "gc/Zone.h" // JS::Zone +#include "js/Context.h" // js::AssertHeapIsIdle +#include "js/ErrorReport.h" // JS_ReportErrorNumberASCII +#include "js/experimental/TypedData.h" // JS_GetArrayBufferViewData, JS_NewUint8Array +#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* +#include "js/GCAPI.h" // JS::AutoCheckCannotGC, JS::AutoSuppressGCAnalysis +#include "js/Object.h" // JS::SetObjectISupports +#include "js/RootingAPI.h" // JS::{,Mutable}Handle, JS::Rooted +#include "js/Stream.h" // JS::ReadableStreamUnderlyingSource +#include "js/Value.h" // JS::{,Object,Undefined}Value +#include "vm/ArrayBufferViewObject.h" // js::ArrayBufferViewObject +#include "vm/JSContext.h" // JSContext, CHECK_THREAD +#include "vm/JSObject.h" // JSObject +#include "vm/PlainObject.h" // js::PlainObject +#include "vm/PromiseObject.h" // js::PromiseObject + +#include "builtin/streams/ReadableStreamReader-inl.h" // js::UnwrapStreamFromReader +#include "vm/Compartment-inl.h" // JS::Compartment::wrap, js::UnwrapAndDowncastObject +#include "vm/JSObject-inl.h" // js::NewBuiltinClassInstance +#include "vm/Realm-inl.h" // js::AutoRealm + +using js::ArrayBufferViewObject; +using js::AssertHeapIsIdle; +using js::AutoRealm; +using js::CheckReadableStreamControllerCanCloseOrEnqueue; +using js::ForAuthorCodeBool; +using js::GetErrorMessage; +using js::IsObjectInContextCompartment; +using js::NewBuiltinClassInstance; +using js::PlainObject; +using js::ReadableByteStreamController; +using js::ReadableByteStreamControllerClose; +using js::ReadableStream; +using js::ReadableStreamController; +using js::ReadableStreamControllerError; +using js::ReadableStreamControllerGetDesiredSizeUnchecked; +using js::ReadableStreamDefaultController; +using js::ReadableStreamDefaultControllerClose; +using js::ReadableStreamDefaultReader; +using js::ReadableStreamFulfillReadOrReadIntoRequest; +using js::ReadableStreamGetNumReadRequests; +using js::ReadableStreamHasDefaultReader; +using js::ReadableStreamReader; +using js::ReadableStreamTee; +using js::SetUpReadableStreamDefaultControllerFromUnderlyingSource; +using js::StreamController; +using js::UnwrapAndDowncastObject; +using js::UnwrapStreamFromReader; + +JS_PUBLIC_API JSObject* js::UnwrapReadableStream(JSObject* obj) { + return obj->maybeUnwrapIf(); +} + +JS_PUBLIC_API JSObject* JS::NewReadableDefaultStreamObject( + JSContext* cx, JS::Handle underlyingSource /* = nullptr */, + JS::Handle size /* = nullptr */, + double highWaterMark /* = 1 */, + JS::Handle proto /* = nullptr */) { + MOZ_ASSERT(!cx->zone()->isAtomsZone()); + AssertHeapIsIdle(); + CHECK_THREAD(cx); + cx->check(underlyingSource, size, proto); + MOZ_ASSERT(highWaterMark >= 0); + + // A copy of ReadableStream::constructor, with most of the + // argument-checking done implicitly by C++ type checking. + Rooted stream(cx, ReadableStream::create(cx)); + if (!stream) { + return nullptr; + } + Rooted sourceVal(cx); + if (underlyingSource) { + sourceVal.setObject(*underlyingSource); + } else { + JSObject* source = NewPlainObject(cx); + if (!source) { + return nullptr; + } + sourceVal.setObject(*source); + } + Rooted sizeVal(cx, size ? ObjectValue(*size) : UndefinedValue()); + + if (!SetUpReadableStreamDefaultControllerFromUnderlyingSource( + cx, stream, sourceVal, highWaterMark, sizeVal)) { + return nullptr; + } + + return stream; +} + +JS_PUBLIC_API JSObject* JS::NewReadableExternalSourceStreamObject( + JSContext* cx, JS::ReadableStreamUnderlyingSource* underlyingSource, + void* nsISupportsObject_alreadyAddreffed /* = nullptr */, + Handle proto /* = nullptr */) { + MOZ_ASSERT(!cx->zone()->isAtomsZone()); + AssertHeapIsIdle(); + CHECK_THREAD(cx); + MOZ_ASSERT(underlyingSource); + MOZ_ASSERT((uintptr_t(underlyingSource) & 1) == 0, + "external underlying source pointers must be aligned"); + cx->check(proto); + + return ReadableStream::createExternalSourceStream( + cx, underlyingSource, nsISupportsObject_alreadyAddreffed, proto); +} + +JS_PUBLIC_API bool JS::IsReadableStream(JSObject* obj) { + return obj->canUnwrapAs(); +} + +JS_PUBLIC_API bool JS::IsReadableStreamReader(JSObject* obj) { + return obj->canUnwrapAs(); +} + +JS_PUBLIC_API bool JS::IsReadableStreamDefaultReader(JSObject* obj) { + return obj->canUnwrapAs(); +} + +template +[[nodiscard]] static T* APIUnwrapAndDowncast(JSContext* cx, JSObject* obj) { + cx->check(obj); + return UnwrapAndDowncastObject(cx, obj); +} + +JS_PUBLIC_API bool JS::ReadableStreamIsReadable(JSContext* cx, + Handle streamObj, + bool* result) { + ReadableStream* unwrappedStream = + APIUnwrapAndDowncast(cx, streamObj); + if (!unwrappedStream) { + return false; + } + + *result = unwrappedStream->readable(); + return true; +} + +JS_PUBLIC_API bool JS::ReadableStreamIsLocked(JSContext* cx, + Handle streamObj, + bool* result) { + ReadableStream* unwrappedStream = + APIUnwrapAndDowncast(cx, streamObj); + if (!unwrappedStream) { + return false; + } + + *result = unwrappedStream->locked(); + return true; +} + +JS_PUBLIC_API bool JS::ReadableStreamIsDisturbed(JSContext* cx, + Handle streamObj, + bool* result) { + ReadableStream* unwrappedStream = + APIUnwrapAndDowncast(cx, streamObj); + if (!unwrappedStream) { + return false; + } + + *result = unwrappedStream->disturbed(); + return true; +} + +JS_PUBLIC_API JSObject* JS::ReadableStreamCancel(JSContext* cx, + Handle streamObj, + Handle reason) { + AssertHeapIsIdle(); + CHECK_THREAD(cx); + cx->check(reason); + + Rooted unwrappedStream( + cx, APIUnwrapAndDowncast(cx, streamObj)); + if (!unwrappedStream) { + return nullptr; + } + + return js::ReadableStreamCancel(cx, unwrappedStream, reason); +} + +JS_PUBLIC_API bool JS::ReadableStreamGetMode(JSContext* cx, + Handle streamObj, + JS::ReadableStreamMode* mode) { + ReadableStream* unwrappedStream = + APIUnwrapAndDowncast(cx, streamObj); + if (!unwrappedStream) { + return false; + } + + *mode = unwrappedStream->mode(); + return true; +} + +JS_PUBLIC_API JSObject* JS::ReadableStreamGetReader( + JSContext* cx, Handle streamObj, ReadableStreamReaderMode mode) { + AssertHeapIsIdle(); + CHECK_THREAD(cx); + + Rooted unwrappedStream( + cx, APIUnwrapAndDowncast(cx, streamObj)); + if (!unwrappedStream) { + return nullptr; + } + + JSObject* result = CreateReadableStreamDefaultReader(cx, unwrappedStream, + ForAuthorCodeBool::No); + MOZ_ASSERT_IF(result, IsObjectInContextCompartment(result, cx)); + return result; +} + +JS_PUBLIC_API bool JS::ReadableStreamGetExternalUnderlyingSource( + JSContext* cx, Handle streamObj, + JS::ReadableStreamUnderlyingSource** source) { + AssertHeapIsIdle(); + CHECK_THREAD(cx); + + Rooted unwrappedStream( + cx, APIUnwrapAndDowncast(cx, streamObj)); + if (!unwrappedStream) { + return false; + } + + MOZ_ASSERT(unwrappedStream->mode() == JS::ReadableStreamMode::ExternalSource); + if (unwrappedStream->locked()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_READABLESTREAM_LOCKED); + return false; + } + if (!unwrappedStream->readable()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE, + "ReadableStreamGetExternalUnderlyingSource"); + return false; + } + + auto unwrappedController = + &unwrappedStream->controller()->as(); + unwrappedController->setSourceLocked(); + *source = unwrappedController->externalSource(); + return true; +} + +JS_PUBLIC_API bool JS::ReadableStreamReleaseExternalUnderlyingSource( + JSContext* cx, Handle streamObj) { + ReadableStream* unwrappedStream = + APIUnwrapAndDowncast(cx, streamObj); + if (!unwrappedStream) { + return false; + } + + MOZ_ASSERT(unwrappedStream->mode() == JS::ReadableStreamMode::ExternalSource); + MOZ_ASSERT(unwrappedStream->locked()); + MOZ_ASSERT(unwrappedStream->controller()->sourceLocked()); + unwrappedStream->controller()->clearSourceLocked(); + return true; +} + +JS_PUBLIC_API bool JS::ReadableStreamUpdateDataAvailableFromSource( + JSContext* cx, JS::Handle streamObj, uint32_t availableData) { + AssertHeapIsIdle(); + CHECK_THREAD(cx); + + Rooted unwrappedStream( + cx, APIUnwrapAndDowncast(cx, streamObj)); + if (!unwrappedStream) { + return false; + } + + // This is based on Streams spec 3.11.4.4. enqueue(chunk) steps 1-3 and + // 3.13.9. ReadableByteStreamControllerEnqueue(controller, chunk) steps + // 8-9. + // + // Adapted to handling updates signaled by the embedding for streams with + // external underlying sources. + // + // The remaining steps of those two functions perform checks and asserts + // that don't apply to streams with external underlying sources. + + Rooted unwrappedController( + cx, &unwrappedStream->controller()->as()); + + // Step 2: If this.[[closeRequested]] is true, throw a TypeError exception. + if (unwrappedController->closeRequested()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_READABLESTREAMCONTROLLER_CLOSED, "enqueue"); + return false; + } + + // Step 3: If this.[[controlledReadableStream]].[[state]] is not "readable", + // throw a TypeError exception. + if (!unwrappedController->stream()->readable()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE, + "enqueue"); + return false; + } + + unwrappedController->clearPullFlags(); + +#if DEBUG + uint32_t oldAvailableData = + unwrappedController->getFixedSlot(StreamController::Slot_TotalSize) + .toInt32(); +#endif // DEBUG + unwrappedController->setQueueTotalSize(availableData); + + // 3.139. ReadableByteStreamControllerEnqueue + // Step 8.a: If ! ReadableStreamGetNumReadRequests(stream) is 0, + // Reordered because for externally-sourced streams it applies regardless + // of reader type. + if (ReadableStreamGetNumReadRequests(unwrappedStream) == 0) { + return true; + } + + // Step 8: If ! ReadableStreamHasDefaultReader(stream) is true + bool hasDefaultReader; + if (!ReadableStreamHasDefaultReader(cx, unwrappedStream, &hasDefaultReader)) { + return false; + } + if (hasDefaultReader) { + // Step b: Otherwise, + // Step i: Assert: controller.[[queue]] is empty. + MOZ_ASSERT(oldAvailableData == 0); + + // Step ii: Let transferredView be + // ! Construct(%Uint8Array%, transferredBuffer, + // byteOffset, byteLength). + JSObject* viewObj = JS_NewUint8Array(cx, availableData); + if (!viewObj) { + return false; + } + Rooted transferredView( + cx, &viewObj->as()); + if (!transferredView) { + return false; + } + + JS::ReadableStreamUnderlyingSource* source = + unwrappedController->externalSource(); + + size_t bytesWritten; + { + AutoRealm ar(cx, unwrappedStream); + source->writeIntoReadRequestBuffer(cx, unwrappedStream, transferredView, + availableData, &bytesWritten); + } + + // Step iii: Perform ! ReadableStreamFulfillReadRequest(stream, + // transferredView, + // false). + Rooted chunk(cx, ObjectValue(*transferredView)); + if (!ReadableStreamFulfillReadOrReadIntoRequest(cx, unwrappedStream, chunk, + false)) { + return false; + } + + unwrappedController->setQueueTotalSize(availableData - bytesWritten); + } else { + // Step 9: Otherwise, if ! ReadableStreamHasBYOBReader(stream) is true, + // [...] + // (Omitted. BYOB readers are not implemented.) + + // Step 10: Otherwise, + // Step a: Assert: ! IsReadableStreamLocked(stream) is false. + MOZ_ASSERT(!unwrappedStream->locked()); + + // Step b: Perform ! ReadableByteStreamControllerEnqueueChunkToQueue( + // controller, transferredBuffer, byteOffset, byteLength). + // (Not needed for external underlying sources.) + } + + return true; +} + +JS_PUBLIC_API void JS::ReadableStreamReleaseCCObject(JSObject* streamObj) { + MOZ_ASSERT(JS::IsReadableStream(streamObj)); + JS::SetObjectISupports(streamObj, nullptr); +} + +JS_PUBLIC_API bool JS::ReadableStreamTee(JSContext* cx, + Handle streamObj, + MutableHandle branch1Obj, + MutableHandle branch2Obj) { + AssertHeapIsIdle(); + CHECK_THREAD(cx); + + Rooted unwrappedStream( + cx, APIUnwrapAndDowncast(cx, streamObj)); + if (!unwrappedStream) { + return false; + } + + Rooted branch1Stream(cx); + Rooted branch2Stream(cx); + if (!ReadableStreamTee(cx, unwrappedStream, false, &branch1Stream, + &branch2Stream)) { + return false; + } + + branch1Obj.set(branch1Stream); + branch2Obj.set(branch2Stream); + return true; +} + +JS_PUBLIC_API bool JS::ReadableStreamGetDesiredSize(JSContext* cx, + JSObject* streamObj, + bool* hasValue, + double* value) { + ReadableStream* unwrappedStream = + APIUnwrapAndDowncast(cx, streamObj); + if (!unwrappedStream) { + return false; + } + + if (unwrappedStream->errored()) { + *hasValue = false; + return true; + } + + *hasValue = true; + + if (unwrappedStream->closed()) { + *value = 0; + return true; + } + + *value = ReadableStreamControllerGetDesiredSizeUnchecked( + unwrappedStream->controller()); + return true; +} + +JS_PUBLIC_API bool JS::ReadableStreamClose(JSContext* cx, + Handle streamObj) { + AssertHeapIsIdle(); + CHECK_THREAD(cx); + + Rooted unwrappedStream( + cx, APIUnwrapAndDowncast(cx, streamObj)); + if (!unwrappedStream) { + return false; + } + + Rooted unwrappedControllerObj( + cx, unwrappedStream->controller()); + if (!CheckReadableStreamControllerCanCloseOrEnqueue( + cx, unwrappedControllerObj, "close")) { + return false; + } + + if (unwrappedControllerObj->is()) { + Rooted unwrappedController(cx); + unwrappedController = + &unwrappedControllerObj->as(); + return ReadableStreamDefaultControllerClose(cx, unwrappedController); + } + + Rooted unwrappedController(cx); + unwrappedController = + &unwrappedControllerObj->as(); + return ReadableByteStreamControllerClose(cx, unwrappedController); +} + +JS_PUBLIC_API bool JS::ReadableStreamEnqueue(JSContext* cx, + Handle streamObj, + Handle chunk) { + AssertHeapIsIdle(); + CHECK_THREAD(cx); + cx->check(chunk); + + Rooted unwrappedStream( + cx, APIUnwrapAndDowncast(cx, streamObj)); + if (!unwrappedStream) { + return false; + } + + if (unwrappedStream->mode() != JS::ReadableStreamMode::Default) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_READABLESTREAM_NOT_DEFAULT_CONTROLLER, + "JS::ReadableStreamEnqueue"); + return false; + } + + Rooted unwrappedController(cx); + unwrappedController = + &unwrappedStream->controller()->as(); + + MOZ_ASSERT(!unwrappedController->closeRequested()); + MOZ_ASSERT(unwrappedStream->readable()); + + return ReadableStreamDefaultControllerEnqueue(cx, unwrappedController, chunk); +} + +JS_PUBLIC_API bool JS::ReadableStreamError(JSContext* cx, + Handle streamObj, + Handle error) { + AssertHeapIsIdle(); + CHECK_THREAD(cx); + cx->check(error); + + Rooted unwrappedStream( + cx, APIUnwrapAndDowncast(cx, streamObj)); + if (!unwrappedStream) { + return false; + } + + Rooted unwrappedController( + cx, unwrappedStream->controller()); + return ReadableStreamControllerError(cx, unwrappedController, error); +} + +JS_PUBLIC_API bool JS::ReadableStreamReaderIsClosed(JSContext* cx, + Handle readerObj, + bool* result) { + Rooted unwrappedReader( + cx, APIUnwrapAndDowncast(cx, readerObj)); + if (!unwrappedReader) { + return false; + } + + *result = unwrappedReader->isClosed(); + return true; +} + +JS_PUBLIC_API bool JS::ReadableStreamReaderCancel(JSContext* cx, + Handle readerObj, + Handle reason) { + AssertHeapIsIdle(); + CHECK_THREAD(cx); + cx->check(reason); + + Rooted unwrappedReader( + cx, APIUnwrapAndDowncast(cx, readerObj)); + if (!unwrappedReader) { + return false; + } + MOZ_ASSERT(unwrappedReader->forAuthorCode() == ForAuthorCodeBool::No, + "C++ code should not touch readers created by scripts"); + + return ReadableStreamReaderGenericCancel(cx, unwrappedReader, reason); +} + +JS_PUBLIC_API bool JS::ReadableStreamReaderReleaseLock( + JSContext* cx, Handle readerObj) { + AssertHeapIsIdle(); + CHECK_THREAD(cx); + + Rooted unwrappedReader( + cx, APIUnwrapAndDowncast(cx, readerObj)); + if (!unwrappedReader) { + return false; + } + MOZ_ASSERT(unwrappedReader->forAuthorCode() == ForAuthorCodeBool::No, + "C++ code should not touch readers created by scripts"); + +#ifdef DEBUG + Rooted unwrappedStream( + cx, UnwrapStreamFromReader(cx, unwrappedReader)); + if (!unwrappedStream) { + return false; + } + MOZ_ASSERT(ReadableStreamGetNumReadRequests(unwrappedStream) == 0); +#endif // DEBUG + + return ReadableStreamReaderGenericRelease(cx, unwrappedReader); +} + +JS_PUBLIC_API JSObject* JS::ReadableStreamDefaultReaderRead( + JSContext* cx, Handle readerObj) { + AssertHeapIsIdle(); + CHECK_THREAD(cx); + + Rooted unwrappedReader( + cx, APIUnwrapAndDowncast(cx, readerObj)); + if (!unwrappedReader) { + return nullptr; + } + MOZ_ASSERT(unwrappedReader->forAuthorCode() == ForAuthorCodeBool::No, + "C++ code should not touch readers created by scripts"); + + return js::ReadableStreamDefaultReaderRead(cx, unwrappedReader); +} diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/StreamController-inl.h b/mozjs-sys/mozjs/js/src/builtin/streams/StreamController-inl.h new file mode 100644 index 00000000000..c19bb38c8fb --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/StreamController-inl.h @@ -0,0 +1,22 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Base stream controller inlines. */ + +#ifndef builtin_streams_StreamController_inl_h +#define builtin_streams_StreamController_inl_h + +#include "builtin/streams/StreamController.h" // js::StreamController +#include "builtin/streams/ReadableStreamController.h" // js::Readable{ByteStream,StreamDefault}Controller +#include "vm/JSObject.h" // JSObject + +template <> +inline bool JSObject::is() const { + return is() || + is(); +} + +#endif // builtin_streams_ReadableStreamController_inl_h diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/StreamController.h b/mozjs-sys/mozjs/js/src/builtin/streams/StreamController.h new file mode 100644 index 00000000000..4986421ab0d --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/StreamController.h @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Base class for readable and writable stream controllers. */ + +#ifndef builtin_streams_StreamController_h +#define builtin_streams_StreamController_h + +#include "js/Value.h" // JS::Value, JS::NumberValue +#include "vm/JSObject.h" // JSObject +#include "vm/List.h" // js::ListObject +#include "vm/NativeObject.h" // js::NativeObject + +namespace js { + +/** + * Common base class of both readable and writable stream controllers. + */ +class StreamController : public NativeObject { + public: + /** + * Memory layout for stream controllers. + * + * Both ReadableStreamDefaultController and ReadableByteStreamController + * are queue containers and must have these slots at identical offsets. + * + * The queue is guaranteed to be in the same compartment as the container, + * but might contain wrappers for objects from other compartments. + */ + enum Slots { Slot_Queue, Slot_TotalSize, SlotCount }; + + ListObject* queue() const { + return &getFixedSlot(Slot_Queue).toObject().as(); + } + double queueTotalSize() const { + return getFixedSlot(Slot_TotalSize).toNumber(); + } + void setQueueTotalSize(double size) { + setFixedSlot(Slot_TotalSize, JS::NumberValue(size)); + } +}; + +} // namespace js + +template <> +inline bool JSObject::is() const; + +#endif // builtin_streams_StreamController_h diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/TeeState.cpp b/mozjs-sys/mozjs/js/src/builtin/streams/TeeState.cpp new file mode 100644 index 00000000000..b5faa96f8c8 --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/TeeState.cpp @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Stream teeing state. */ + +#include "builtin/streams/TeeState.h" + +#include "builtin/streams/ReadableStream.h" // js::ReadableStream +#include "js/Class.h" // JSClass, JSCLASS_HAS_RESERVED_SLOTS +#include "js/RootingAPI.h" // JS::Handle, JS::Rooted +#include "vm/Compartment.h" // JS::Compartment +#include "vm/JSContext.h" // JSContext +#include "vm/PromiseObject.h" // js::PromiseObject + +#include "vm/JSObject-inl.h" // js::NewBuiltinClassInstance + +using js::ReadableStream; +using js::TeeState; + +using JS::Handle; +using JS::Int32Value; +using JS::ObjectValue; +using JS::Rooted; + +/* static */ TeeState* TeeState::create( + JSContext* cx, Handle unwrappedStream) { + Rooted state(cx, NewBuiltinClassInstance(cx)); + if (!state) { + return nullptr; + } + + Rooted cancelPromise( + cx, PromiseObject::createSkippingExecutor(cx)); + if (!cancelPromise) { + return nullptr; + } + + state->setFixedSlot(Slot_Flags, Int32Value(0)); + state->setFixedSlot(Slot_CancelPromise, ObjectValue(*cancelPromise)); + Rooted wrappedStream(cx, unwrappedStream); + if (!cx->compartment()->wrap(cx, &wrappedStream)) { + return nullptr; + } + state->setFixedSlot(Slot_Stream, JS::ObjectValue(*wrappedStream)); + + return state; +} + +const JSClass TeeState::class_ = {"TeeState", + JSCLASS_HAS_RESERVED_SLOTS(SlotCount)}; diff --git a/mozjs-sys/mozjs/js/src/builtin/streams/TeeState.h b/mozjs-sys/mozjs/js/src/builtin/streams/TeeState.h new file mode 100644 index 00000000000..beb42ff8e67 --- /dev/null +++ b/mozjs-sys/mozjs/js/src/builtin/streams/TeeState.h @@ -0,0 +1,155 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Stream teeing state. */ + +#ifndef builtin_streams_TeeState_h +#define builtin_streams_TeeState_h + +#include "mozilla/Assertions.h" // MOZ_ASSERT + +#include // uint32_t + +#include "builtin/streams/ReadableStreamController.h" // js::ReadableStreamDefaultController +#include "js/Class.h" // JSClass +#include "js/Value.h" // JS::{Int32,Object}Value +#include "vm/NativeObject.h" // js::NativeObject +#include "vm/PromiseObject.h" // js::PromiseObject + +namespace js { + +/** + * TeeState objects implement the local variables in Streams spec 3.3.9 + * ReadableStreamTee, which are accessed by several algorithms. + */ +class TeeState : public NativeObject { + public: + /** + * Memory layout for TeeState instances. + * + * The Reason1 and Reason2 slots store opaque values, which might be + * wrapped objects from other compartments. Since we don't treat them as + * objects in Streams-specific code, we don't have to worry about that + * apart from ensuring that the values are properly wrapped before storing + * them. + * + * CancelPromise is always created in TeeState::create below, so is + * guaranteed to be in the same compartment as the TeeState instance + * itself. + * + * Stream can be from another compartment. It is automatically wrapped + * before storing it and unwrapped upon retrieval. That means that + * TeeState consumers need to be able to deal with unwrapped + * ReadableStream instances from non-current compartments. + * + * Branch1 and Branch2 are always created in the same compartment as the + * TeeState instance, so cannot be from another compartment. + */ + enum Slots { + Slot_Flags = 0, + Slot_Reason1, + Slot_Reason2, + Slot_CancelPromise, + Slot_Stream, + Slot_Branch1, + Slot_Branch2, + SlotCount + }; + + private: + enum Flags { + Flag_Reading = 1 << 0, + Flag_Canceled1 = 1 << 1, + Flag_Canceled2 = 1 << 2, + + // No internal user ever sets the cloneForBranch2 flag to true, and the + // streams spec doesn't expose a way to set the flag to true. So for the + // moment, don't even reserve flag-space to store it. + // Flag_CloneForBranch2 = 1 << 3, + }; + uint32_t flags() const { return getFixedSlot(Slot_Flags).toInt32(); } + void setFlags(uint32_t flags) { + setFixedSlot(Slot_Flags, JS::Int32Value(flags)); + } + + public: + static const JSClass class_; + + // Consistent with not even storing this always-false flag, expose it as + // compile-time constant false. + static constexpr bool cloneForBranch2() { return false; } + + bool reading() const { return flags() & Flag_Reading; } + void setReading() { + MOZ_ASSERT(!(flags() & Flag_Reading)); + setFlags(flags() | Flag_Reading); + } + void unsetReading() { + MOZ_ASSERT(flags() & Flag_Reading); + setFlags(flags() & ~Flag_Reading); + } + + bool canceled1() const { return flags() & Flag_Canceled1; } + void setCanceled1(HandleValue reason) { + MOZ_ASSERT(!(flags() & Flag_Canceled1)); + setFlags(flags() | Flag_Canceled1); + setFixedSlot(Slot_Reason1, reason); + } + + bool canceled2() const { return flags() & Flag_Canceled2; } + void setCanceled2(HandleValue reason) { + MOZ_ASSERT(!(flags() & Flag_Canceled2)); + setFlags(flags() | Flag_Canceled2); + setFixedSlot(Slot_Reason2, reason); + } + + JS::Value reason1() const { + MOZ_ASSERT(canceled1()); + return getFixedSlot(Slot_Reason1); + } + + JS::Value reason2() const { + MOZ_ASSERT(canceled2()); + return getFixedSlot(Slot_Reason2); + } + + PromiseObject* cancelPromise() { + return &getFixedSlot(Slot_CancelPromise).toObject().as(); + } + + ReadableStreamDefaultController* branch1() { + ReadableStreamDefaultController* controller = + &getFixedSlot(Slot_Branch1) + .toObject() + .as(); + MOZ_ASSERT(controller->isTeeBranch1()); + return controller; + } + void setBranch1(ReadableStreamDefaultController* controller) { + MOZ_ASSERT(controller->isTeeBranch1()); + setFixedSlot(Slot_Branch1, JS::ObjectValue(*controller)); + } + + ReadableStreamDefaultController* branch2() { + ReadableStreamDefaultController* controller = + &getFixedSlot(Slot_Branch2) + .toObject() + .as(); + MOZ_ASSERT(controller->isTeeBranch2()); + return controller; + } + void setBranch2(ReadableStreamDefaultController* controller) { + MOZ_ASSERT(controller->isTeeBranch2()); + setFixedSlot(Slot_Branch2, JS::ObjectValue(*controller)); + } + + static TeeState* create(JSContext* cx, + Handle unwrappedStream); +}; + +} // namespace js + +#endif // builtin_streams_TeeState_h diff --git a/mozjs-sys/mozjs/js/src/jsapi-tests/moz.build b/mozjs-sys/mozjs/js/src/jsapi-tests/moz.build index e24b9bf43ad..d31caa78b7f 100644 --- a/mozjs-sys/mozjs/js/src/jsapi-tests/moz.build +++ b/mozjs-sys/mozjs/js/src/jsapi-tests/moz.build @@ -173,6 +173,11 @@ if not CONFIG["JS_CODEGEN_NONE"]: "testsJit.cpp", ] +if CONFIG["MOZ_JS_STREAMS"]: + UNIFIED_SOURCES += [ + "testReadableStream.cpp", + ] + if CONFIG["NIGHTLY_BUILD"]: # The Error interceptor only exists on Nightly. UNIFIED_SOURCES += [ diff --git a/mozjs-sys/mozjs/js/src/jsapi-tests/testReadableStream.cpp b/mozjs-sys/mozjs/js/src/jsapi-tests/testReadableStream.cpp new file mode 100644 index 00000000000..edae0826589 --- /dev/null +++ b/mozjs-sys/mozjs/js/src/jsapi-tests/testReadableStream.cpp @@ -0,0 +1,1195 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jsapi.h" +#include "jsfriendapi.h" +#include "js/experimental/TypedData.h" // JS_GetArrayBufferViewData, JS_IsUint8Array +#include "js/PropertyAndElement.h" // JS_GetProperty, JS_HasProperty, JS_SetProperty +#include "js/Stream.h" +#include "jsapi-tests/tests.h" + +using namespace JS; + +char testBufferData[] = + "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +struct StubExternalUnderlyingSource + : public JS::ReadableStreamUnderlyingSource { + void* buffer = testBufferData; + bool dataRequestCBCalled = false; + bool writeIntoRequestBufferCBCalled = false; + bool cancelStreamCBCalled = false; + Value cancelStreamReason; + bool streamClosedCBCalled = false; + Value streamClosedReason; + bool streamErroredCBCalled = false; + Value streamErroredReason; + bool finalizeStreamCBCalled = false; + void* finalizedStreamUnderlyingSource; + + static StubExternalUnderlyingSource instance; + + void requestData(JSContext* cx, HandleObject stream, + size_t desiredSize) override { + js::AssertSameCompartment(cx, stream); + MOZ_RELEASE_ASSERT(!dataRequestCBCalled, "Invalid test setup"); + dataRequestCBCalled = true; + } + + void writeIntoReadRequestBuffer(JSContext* cx, HandleObject stream, + JS::HandleObject chunk, size_t length, + size_t* bytesWritten) override { + js::AssertSameCompartment(cx, stream); + MOZ_RELEASE_ASSERT(!writeIntoRequestBufferCBCalled, "Invalid test setup"); + writeIntoRequestBufferCBCalled = true; + + MOZ_RELEASE_ASSERT(this == &StubExternalUnderlyingSource::instance); + MOZ_RELEASE_ASSERT(StubExternalUnderlyingSource::instance.buffer == + testBufferData); + MOZ_RELEASE_ASSERT(length <= sizeof(testBufferData)); + + JS::AutoCheckCannotGC noGC; + bool isSharedMemory; + + void* buffer = JS_GetArrayBufferViewData(chunk, &isSharedMemory, noGC); + MOZ_ASSERT(!isSharedMemory); + + memcpy(buffer, testBufferData, length); + *bytesWritten = length; + } + + Value cancel(JSContext* cx, HandleObject stream, + HandleValue reason) override { + js::AssertSameCompartment(cx, stream); + js::AssertSameCompartment(cx, reason); + MOZ_RELEASE_ASSERT(!cancelStreamCBCalled, "Invalid test setup"); + cancelStreamCBCalled = true; + cancelStreamReason = reason; + return reason; + } + + void onClosed(JSContext* cx, HandleObject stream) override { + js::AssertSameCompartment(cx, stream); + MOZ_RELEASE_ASSERT(!streamClosedCBCalled, "Invalid test setup"); + streamClosedCBCalled = true; + } + + void onErrored(JSContext* cx, HandleObject stream, + HandleValue reason) override { + js::AssertSameCompartment(cx, stream); + js::AssertSameCompartment(cx, reason); + MOZ_RELEASE_ASSERT(!streamErroredCBCalled, "Invalid test setup"); + streamErroredCBCalled = true; + streamErroredReason = reason; + } + + void finalize() override { + MOZ_RELEASE_ASSERT(!finalizeStreamCBCalled, "Invalid test setup"); + finalizeStreamCBCalled = true; + finalizedStreamUnderlyingSource = this; + } + + void reset() { + dataRequestCBCalled = false; + writeIntoRequestBufferCBCalled = false; + cancelStreamReason = UndefinedValue(); + cancelStreamCBCalled = false; + streamClosedCBCalled = false; + streamErroredCBCalled = false; + finalizeStreamCBCalled = false; + } +}; + +StubExternalUnderlyingSource StubExternalUnderlyingSource::instance; + +static_assert(MOZ_ALIGNOF(StubExternalUnderlyingSource) > 1, + "UnderlyingSource pointers must not have the low bit set"); + +static JSObject* NewDefaultStream(JSContext* cx, HandleObject source = nullptr, + HandleFunction size = nullptr, + double highWaterMark = 1, + HandleObject proto = nullptr) { + RootedObject stream(cx, NewReadableDefaultStreamObject(cx, source, size, + highWaterMark, proto)); + if (stream) { + MOZ_RELEASE_ASSERT(IsReadableStream(stream)); + } + return stream; +} + +static bool GetIterResult(JSContext* cx, HandleObject promise, + MutableHandleValue value, bool* done) { + RootedObject iterResult(cx, &GetPromiseResult(promise).toObject()); + + bool found; + if (!JS_HasProperty(cx, iterResult, "value", &found)) { + return false; + } + MOZ_RELEASE_ASSERT(found); + if (!JS_HasProperty(cx, iterResult, "done", &found)) { + return false; + } + MOZ_RELEASE_ASSERT(found); + + RootedValue doneVal(cx); + if (!JS_GetProperty(cx, iterResult, "value", value)) { + return false; + } + if (!JS_GetProperty(cx, iterResult, "done", &doneVal)) { + return false; + } + + *done = doneVal.toBoolean(); + if (*done) { + MOZ_RELEASE_ASSERT(value.isUndefined()); + } + + return true; +} + +static JSObject* GetReadChunk(JSContext* cx, HandleObject readRequest) { + MOZ_RELEASE_ASSERT(GetPromiseState(readRequest) == PromiseState::Fulfilled); + RootedValue resultVal(cx, GetPromiseResult(readRequest)); + MOZ_RELEASE_ASSERT(resultVal.isObject()); + RootedObject result(cx, &resultVal.toObject()); + RootedValue chunkVal(cx); + JS_GetProperty(cx, result, "value", &chunkVal); + return &chunkVal.toObject(); +} + +struct StreamTestFixture : public JSAPITest { + virtual ~StreamTestFixture() {} +}; + +BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_NewReadableStream) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + ReadableStreamMode mode; + CHECK(ReadableStreamGetMode(cx, stream, &mode)); + CHECK(mode == ReadableStreamMode::Default); + return true; +} +END_FIXTURE_TEST(StreamTestFixture, testReadableStream_NewReadableStream) + +BEGIN_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamGetReaderDefault) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + + RootedObject reader(cx, ReadableStreamGetReader( + cx, stream, ReadableStreamReaderMode::Default)); + CHECK(reader); + CHECK(IsReadableStreamDefaultReader(reader)); + bool locked; + CHECK(ReadableStreamIsLocked(cx, stream, &locked)); + CHECK(locked); + bool closed; + CHECK(ReadableStreamReaderIsClosed(cx, reader, &closed)); + CHECK(!closed); + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamGetReaderDefault) + +BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamTee) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + + RootedObject leftStream(cx); + RootedObject rightStream(cx); + CHECK(ReadableStreamTee(cx, stream, &leftStream, &rightStream)); + bool locked; + CHECK(ReadableStreamIsLocked(cx, stream, &locked)); + CHECK(locked); + CHECK(leftStream); + CHECK(IsReadableStream(leftStream)); + CHECK(rightStream); + CHECK(IsReadableStream(rightStream)); + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamTee) + +BEGIN_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamEnqueue) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + + RootedObject chunk(cx, JS_NewPlainObject(cx)); + CHECK(chunk); + RootedValue chunkVal(cx, ObjectValue(*chunk)); + CHECK(ReadableStreamEnqueue(cx, stream, chunkVal)); + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamEnqueue) + +BEGIN_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamDefaultReaderRead) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + RootedObject reader(cx, ReadableStreamGetReader( + cx, stream, ReadableStreamReaderMode::Default)); + CHECK(reader); + + RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader)); + CHECK(request); + CHECK(IsPromiseObject(request)); + CHECK(GetPromiseState(request) == PromiseState::Pending); + + RootedObject chunk(cx, JS_NewPlainObject(cx)); + CHECK(chunk); + RootedValue chunkVal(cx, ObjectValue(*chunk)); + CHECK(ReadableStreamEnqueue(cx, stream, chunkVal)); + + CHECK(GetReadChunk(cx, request) == chunk); + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamDefaultReaderRead) + +BEGIN_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamDefaultReaderClose) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + RootedObject reader(cx, ReadableStreamGetReader( + cx, stream, ReadableStreamReaderMode::Default)); + CHECK(reader); + + RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader)); + CHECK(request); + CHECK(IsPromiseObject(request)); + CHECK(GetPromiseState(request) == PromiseState::Pending); + + CHECK(ReadableStreamClose(cx, stream)); + + bool done; + RootedValue value(cx); + CHECK(GetPromiseState(request) == PromiseState::Fulfilled); + CHECK(GetIterResult(cx, request, &value, &done)); + CHECK(value.isUndefined()); + CHECK(done); + + // The callbacks are only invoked for external streams. + CHECK(!StubExternalUnderlyingSource::instance.streamClosedCBCalled); + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamDefaultReaderClose) + +BEGIN_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamDefaultReaderError) { + StubExternalUnderlyingSource::instance.reset(); + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + RootedObject reader(cx, ReadableStreamGetReader( + cx, stream, ReadableStreamReaderMode::Default)); + CHECK(reader); + + RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader)); + CHECK(request); + CHECK(IsPromiseObject(request)); + CHECK(GetPromiseState(request) == PromiseState::Pending); + + bool locked; + CHECK(ReadableStreamIsLocked(cx, stream, &locked)); + CHECK(locked); + bool readable; + CHECK(ReadableStreamIsReadable(cx, stream, &readable)); + CHECK(readable); + RootedValue error(cx, Int32Value(42)); + CHECK(ReadableStreamError(cx, stream, error)); + + CHECK(GetPromiseState(request) == PromiseState::Rejected); + RootedValue reason(cx, GetPromiseResult(request)); + CHECK(reason.isInt32()); + CHECK(reason.toInt32() == 42); + + // The callbacks are only invoked for external streams. + CHECK(!StubExternalUnderlyingSource::instance.streamErroredCBCalled); + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamDefaultReaderError) + +static JSObject* NewExternalSourceStream( + JSContext* cx, ReadableStreamUnderlyingSource* source) { + RootedObject stream(cx, NewReadableExternalSourceStreamObject(cx, source)); + if (stream) { + MOZ_RELEASE_ASSERT(IsReadableStream(stream)); + } + return stream; +} + +static JSObject* NewExternalSourceStream(JSContext* cx) { + return NewExternalSourceStream(cx, &StubExternalUnderlyingSource::instance); +} + +BEGIN_FIXTURE_TEST( + StreamTestFixture, + testReadableStream_CreateReadableByteStreamWithExternalSource) { + StubExternalUnderlyingSource::instance.reset(); + + RootedObject stream(cx, NewExternalSourceStream(cx)); + CHECK(stream); + ReadableStreamMode mode; + CHECK(ReadableStreamGetMode(cx, stream, &mode)); + CHECK(mode == ReadableStreamMode::ExternalSource); + ReadableStreamUnderlyingSource* underlyingSource; + CHECK( + ReadableStreamGetExternalUnderlyingSource(cx, stream, &underlyingSource)); + CHECK(underlyingSource == &StubExternalUnderlyingSource::instance); + bool locked; + CHECK(ReadableStreamIsLocked(cx, stream, &locked)); + CHECK(locked); + CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream)); + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, + testReadableStream_CreateReadableByteStreamWithExternalSource) + +BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_ExternalSourceCancel) { + StubExternalUnderlyingSource::instance.reset(); + + RootedObject stream(cx, NewExternalSourceStream(cx)); + CHECK(stream); + RootedValue reason(cx, Int32Value(42)); + CHECK(ReadableStreamCancel(cx, stream, reason)); + CHECK(StubExternalUnderlyingSource::instance.cancelStreamCBCalled); + CHECK(StubExternalUnderlyingSource::instance.cancelStreamReason == reason); + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ExternalSourceCancel) + +BEGIN_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ExternalSourceGetReader) { + StubExternalUnderlyingSource::instance.reset(); + + RootedObject stream(cx, NewExternalSourceStream(cx)); + CHECK(stream); + + RootedValue streamVal(cx, ObjectValue(*stream)); + CHECK(JS_SetProperty(cx, global, "stream", streamVal)); + RootedValue rval(cx); + EVAL("stream.getReader()", &rval); + CHECK(rval.isObject()); + RootedObject reader(cx, &rval.toObject()); + CHECK(IsReadableStreamDefaultReader(reader)); + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ExternalSourceGetReader) + +enum class CompartmentMode { + Same, + Cross, +}; + +struct ReadFromExternalSourceFixture : public StreamTestFixture { + virtual ~ReadFromExternalSourceFixture() {} + + // On success, streamGlobal is a global object (not a wrapper) + // and stream is in the same compartment as cx (it may be a CCW). + bool createExternalSourceStream(CompartmentMode compartmentMode, + MutableHandleObject streamGlobal, + MutableHandleObject stream) { + if (compartmentMode == CompartmentMode::Same) { + streamGlobal.set(global); + stream.set(NewExternalSourceStream(cx)); + if (!stream) { + return false; + } + } else { + RootedObject savedGlobal(cx, global); + streamGlobal.set(createGlobal()); + if (!streamGlobal) { + return false; + } + global = savedGlobal; + + { + JSAutoRealm ar(cx, streamGlobal); + stream.set(NewExternalSourceStream(cx)); + if (!stream) { + return false; + } + } + if (!JS_WrapObject(cx, stream)) { + return false; + } + } + return true; + } + + bool readWithoutDataAvailable(CompartmentMode compartmentMode, + const char* evalSrc, const char* evalSrc2, + uint32_t writtenLength) { + StubExternalUnderlyingSource::instance.reset(); + definePrint(); + + // Create the stream. + RootedObject streamGlobal(cx); + RootedObject stream(cx); // can be a wrapper + CHECK(createExternalSourceStream(compartmentMode, &streamGlobal, &stream)); + js::RunJobs(cx); + + // GetExternalUnderlyingSource locks the stream. + ReadableStreamUnderlyingSource* underlyingSource; + CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, + &underlyingSource)); + CHECK(underlyingSource == &StubExternalUnderlyingSource::instance); + bool locked; + CHECK(ReadableStreamIsLocked(cx, stream, &locked)); + CHECK(locked); + CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream)); + + // Run caller-supplied JS code to read from the stream. + RootedValue streamVal(cx, ObjectValue(*stream)); + CHECK(JS_SetProperty(cx, global, "stream", streamVal)); + RootedValue rval(cx); + EVAL(evalSrc, &rval); + CHECK(StubExternalUnderlyingSource::instance.dataRequestCBCalled); + CHECK( + !StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled); + CHECK(rval.isObject()); + RootedObject unwrappedPromise(cx, + js::CheckedUnwrapStatic(&rval.toObject())); + CHECK(unwrappedPromise); + CHECK(IsPromiseObject(unwrappedPromise)); + CHECK(GetPromiseState(unwrappedPromise) == PromiseState::Pending); + + // Stream in some data; this resolves the read() result promise. + size_t length = sizeof(testBufferData); + CHECK(ReadableStreamUpdateDataAvailableFromSource(cx, stream, length)); + CHECK( + StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled); + CHECK(GetPromiseState(unwrappedPromise) == PromiseState::Fulfilled); + RootedObject chunk(cx); + { + JSAutoRealm ar(cx, unwrappedPromise); + RootedValue iterVal(cx); + bool done; + if (!GetIterResult(cx, unwrappedPromise, &iterVal, &done)) { + return false; + } + CHECK(!done); + chunk = &iterVal.toObject(); + } + CHECK(JS_WrapObject(cx, &chunk)); + CHECK(JS_IsUint8Array(chunk)); + + { + JS::AutoCheckCannotGC noGC(cx); + bool dummy; + void* buffer = JS_GetArrayBufferViewData(chunk, &dummy, noGC); + CHECK(!memcmp(buffer, testBufferData, writtenLength)); + } + + // Check the callbacks fired by calling read() again. + StubExternalUnderlyingSource::instance.dataRequestCBCalled = false; + StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled = + false; + EVAL(evalSrc2, &rval); + CHECK(StubExternalUnderlyingSource::instance.dataRequestCBCalled); + CHECK( + !StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled); + + return true; + } + + bool readWithDataAvailable(CompartmentMode compartmentMode, + const char* evalSrc, uint32_t writtenLength) { + StubExternalUnderlyingSource::instance.reset(); + definePrint(); + + // Create a stream. + RootedObject streamGlobal(cx); + RootedObject stream(cx); + CHECK(createExternalSourceStream(compartmentMode, &streamGlobal, &stream)); + + // Getting the underlying source locks the stream. + ReadableStreamUnderlyingSource* underlyingSource; + CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, + &underlyingSource)); + CHECK(underlyingSource == &StubExternalUnderlyingSource::instance); + bool locked; + CHECK(ReadableStreamIsLocked(cx, stream, &locked)); + CHECK(locked); + CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream)); + + // Make some data available. + size_t length = sizeof(testBufferData); + CHECK(ReadableStreamUpdateDataAvailableFromSource(cx, stream, length)); + + // Read from the stream. + RootedValue streamVal(cx, ObjectValue(*stream)); + CHECK(JS_SetProperty(cx, global, "stream", streamVal)); + RootedValue rval(cx); + EVAL(evalSrc, &rval); + CHECK( + StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled); + CHECK(rval.isObject()); + RootedObject unwrappedPromise(cx, + js::CheckedUnwrapStatic(&rval.toObject())); + CHECK(unwrappedPromise); + CHECK(IsPromiseObject(unwrappedPromise)); + CHECK(GetPromiseState(unwrappedPromise) == PromiseState::Fulfilled); + RootedObject chunk(cx); + { + JSAutoRealm ar(cx, unwrappedPromise); + RootedValue iterVal(cx); + bool done; + if (!GetIterResult(cx, unwrappedPromise, &iterVal, &done)) { + return false; + } + CHECK(!done); + chunk = &iterVal.toObject(); + } + CHECK(JS_WrapObject(cx, &chunk)); + CHECK(JS_IsUint8Array(chunk)); + + { + JS::AutoCheckCannotGC noGC(cx); + bool dummy; + void* buffer = JS_GetArrayBufferViewData(chunk, &dummy, noGC); + CHECK(!memcmp(buffer, testBufferData, writtenLength)); + } + + return true; + } +}; + +BEGIN_FIXTURE_TEST( + ReadFromExternalSourceFixture, + testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable) { + return readWithoutDataAvailable(CompartmentMode::Same, + "r = stream.getReader(); r.read()", + "r.read()", sizeof(testBufferData)); +} +END_FIXTURE_TEST( + ReadFromExternalSourceFixture, + testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable) + +BEGIN_FIXTURE_TEST( + ReadFromExternalSourceFixture, + testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment1) { + // Scenario 1: The stream and reader are both in the same compartment, but + // ReadableStreamUpdateDataAvailableFromSource is applied to a wrapper. + return readWithoutDataAvailable(CompartmentMode::Cross, + "r = stream.getReader(); r.read()", + "r.read()", sizeof(testBufferData)); +} +END_FIXTURE_TEST( + ReadFromExternalSourceFixture, + testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment1) + +BEGIN_FIXTURE_TEST( + ReadFromExternalSourceFixture, + testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment2) { + // Scenario 2: The stream and reader are in the same compartment, but a + // `read` method from another compartment is used on the reader. + return readWithoutDataAvailable( + CompartmentMode::Cross, + "r = stream.getReader(); read = new " + "ReadableStream({start(){}}).getReader().read; read.call(r)", + "read.call(r)", sizeof(testBufferData)); +} +END_FIXTURE_TEST( + ReadFromExternalSourceFixture, + testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment2) + +BEGIN_FIXTURE_TEST( + ReadFromExternalSourceFixture, + testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment3) { + // Scenario 3: The stream and reader are in different compartments. + return readWithoutDataAvailable( + CompartmentMode::Cross, + "r = ReadableStream.prototype.getReader.call(stream); r.read()", + "r.read()", sizeof(testBufferData)); +} +END_FIXTURE_TEST( + ReadFromExternalSourceFixture, + testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment3) + +BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture, + testReadableStream_ExternalSourceCloseWithPendingRead) { + CHECK(readWithoutDataAvailable(CompartmentMode::Same, + "r = stream.getReader(); request0 = r.read(); " + "request1 = r.read(); request0", + "r.read()", sizeof(testBufferData))); + + RootedValue val(cx); + CHECK(JS_GetProperty(cx, global, "request1", &val)); + CHECK(val.isObject()); + RootedObject request(cx, &val.toObject()); + CHECK(IsPromiseObject(request)); + CHECK(GetPromiseState(request) == PromiseState::Pending); + + CHECK(JS_GetProperty(cx, global, "stream", &val)); + RootedObject stream(cx, &val.toObject()); + ReadableStreamClose(cx, stream); + + val = GetPromiseResult(request); + CHECK(val.isObject()); + RootedObject result(cx, &val.toObject()); + + JS_GetProperty(cx, result, "done", &val); + CHECK(val.isBoolean()); + CHECK(val.toBoolean() == true); + + JS_GetProperty(cx, result, "value", &val); + CHECK(val.isUndefined()); + return true; +} +END_FIXTURE_TEST(ReadFromExternalSourceFixture, + testReadableStream_ExternalSourceCloseWithPendingRead) + +BEGIN_FIXTURE_TEST( + ReadFromExternalSourceFixture, + testReadableStream_ExternalSourceReadDefaultWithDataAvailable) { + return readWithDataAvailable(CompartmentMode::Same, + "r = stream.getReader(); r.read()", + sizeof(testBufferData)); +} +END_FIXTURE_TEST(ReadFromExternalSourceFixture, + testReadableStream_ExternalSourceReadDefaultWithDataAvailable) + +BEGIN_FIXTURE_TEST( + ReadFromExternalSourceFixture, + testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment1) { + // Scenario 1: The stream and reader are both in the same compartment, but + // ReadableStreamUpdateDataAvailableFromSource is applied to a wrapper. + return readWithDataAvailable(CompartmentMode::Cross, + "r = stream.getReader(); r.read()", + sizeof(testBufferData)); +} +END_FIXTURE_TEST( + ReadFromExternalSourceFixture, + testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment1) + +BEGIN_FIXTURE_TEST( + ReadFromExternalSourceFixture, + testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment2) { + // Scenario 2: The stream and reader are in the same compartment, but a + // `read` method from another compartment is used on the reader. + return readWithDataAvailable( + CompartmentMode::Cross, + "r = stream.getReader(); read = new " + "ReadableStream({start(){}}).getReader().read; read.call(r)", + sizeof(testBufferData)); +} +END_FIXTURE_TEST( + ReadFromExternalSourceFixture, + testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment2) + +BEGIN_FIXTURE_TEST( + ReadFromExternalSourceFixture, + testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment3) { + // Scenario 3: The stream and reader are in different compartments. + return readWithDataAvailable( + CompartmentMode::Cross, + "r = ReadableStream.prototype.getReader.call(stream); r.read()", + sizeof(testBufferData)); +} +END_FIXTURE_TEST( + ReadFromExternalSourceFixture, + testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment3) + +// Cross-global tests: +BEGIN_FIXTURE_TEST( + StreamTestFixture, + testReadableStream_ReadableStreamOtherGlobalDefaultReaderRead) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + RootedObject otherGlobal(cx, createGlobal()); + CHECK(otherGlobal); + + { + JSAutoRealm ar(cx, otherGlobal); + CHECK(JS_WrapObject(cx, &stream)); + RootedObject reader(cx, ReadableStreamGetReader( + cx, stream, ReadableStreamReaderMode::Default)); + CHECK(reader); + + RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader)); + CHECK(request); + CHECK(IsPromiseObject(request)); + CHECK(!js::IsWrapper(request)); + CHECK(GetPromiseState(request) == PromiseState::Pending); + + RootedObject chunk(cx, JS_NewPlainObject(cx)); + CHECK(chunk); + RootedValue chunkVal(cx, ObjectValue(*chunk)); + CHECK(ReadableStreamEnqueue(cx, stream, chunkVal)); + + CHECK(GetReadChunk(cx, request) == chunk); + } + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamOtherGlobalDefaultReaderRead) + +BEGIN_FIXTURE_TEST( + StreamTestFixture, + testReadableStream_ReadableStreamGetExternalUnderlyingSource) { + StubExternalUnderlyingSource::instance.reset(); + + RootedObject stream(cx, NewExternalSourceStream(cx)); + CHECK(stream); + ReadableStreamUnderlyingSource* source; + CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &source)); + CHECK(source == &StubExternalUnderlyingSource::instance); + CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream)); + + RootedObject otherGlobal(cx, createGlobal()); + CHECK(otherGlobal); + { + JSAutoRealm ar(cx, otherGlobal); + CHECK(JS_WrapObject(cx, &stream)); + ReadableStreamUnderlyingSource* source; + CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &source)); + CHECK(source == &StubExternalUnderlyingSource::instance); + CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream)); + } + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamGetExternalUnderlyingSource) + +BEGIN_FIXTURE_TEST( + StreamTestFixture, + testReadableStream_ReadableStreamUpdateDataAvailableFromSource) { + RootedObject stream(cx, NewExternalSourceStream(cx)); + CHECK(stream); + CHECK(ReadableStreamUpdateDataAvailableFromSource(cx, stream, 0)); + + RootedObject otherGlobal(cx, createGlobal()); + CHECK(otherGlobal); + { + JSAutoRealm ar(cx, otherGlobal); + CHECK(JS_WrapObject(cx, &stream)); + CHECK(ReadableStreamUpdateDataAvailableFromSource(cx, stream, 1)); + } + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamUpdateDataAvailableFromSource) + +BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_IsReadableStream) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + CHECK(IsReadableStream(stream)); + + RootedObject otherGlobal(cx, createGlobal()); + CHECK(otherGlobal); + { + JSAutoRealm ar(cx, otherGlobal); + CHECK(JS_WrapObject(cx, &stream)); + CHECK(IsReadableStream(stream)); + } + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, testReadableStream_IsReadableStream) + +BEGIN_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamGetMode) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + ReadableStreamMode mode; + CHECK(ReadableStreamGetMode(cx, stream, &mode)); + CHECK(mode == ReadableStreamMode::Default); + + RootedObject otherGlobal(cx, createGlobal()); + CHECK(otherGlobal); + { + JSAutoRealm ar(cx, otherGlobal); + CHECK(JS_WrapObject(cx, &stream)); + CHECK(ReadableStreamGetMode(cx, stream, &mode)); + CHECK(mode == ReadableStreamMode::Default); + } + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamGetMode) + +BEGIN_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamIsReadable) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + bool result; + CHECK(ReadableStreamIsReadable(cx, stream, &result)); + CHECK(result); + + RootedObject otherGlobal(cx, createGlobal()); + CHECK(otherGlobal); + { + JSAutoRealm ar(cx, otherGlobal); + CHECK(JS_WrapObject(cx, &stream)); + CHECK(ReadableStreamIsReadable(cx, stream, &result)); + CHECK(result); + } + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamIsReadable) + +BEGIN_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamIsLocked) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + bool result; + CHECK(ReadableStreamIsLocked(cx, stream, &result)); + CHECK_EQUAL(result, false); + + RootedObject otherGlobal(cx, createGlobal()); + CHECK(otherGlobal); + { + JSAutoRealm ar(cx, otherGlobal); + CHECK(JS_WrapObject(cx, &stream)); + CHECK(ReadableStreamIsLocked(cx, stream, &result)); + CHECK_EQUAL(result, false); + } + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamIsLocked) + +BEGIN_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamIsDisturbed) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + bool result; + CHECK(ReadableStreamIsDisturbed(cx, stream, &result)); + CHECK_EQUAL(result, false); + + RootedObject otherGlobal(cx, createGlobal()); + CHECK(otherGlobal); + { + JSAutoRealm ar(cx, otherGlobal); + CHECK(JS_WrapObject(cx, &stream)); + CHECK(ReadableStreamIsDisturbed(cx, stream, &result)); + CHECK_EQUAL(result, false); + } + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamIsDisturbed) + +BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamCancel) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + + RootedValue reason(cx); + JSObject* callResult = ReadableStreamCancel(cx, stream, reason); + CHECK(callResult); + + RootedObject otherGlobal(cx, createGlobal()); + CHECK(otherGlobal); + { + JSAutoRealm ar(cx, otherGlobal); + CHECK(JS_WrapObject(cx, &stream)); + RootedValue reason(cx); + JSObject* callResult = ReadableStreamCancel(cx, stream, reason); + CHECK(callResult); + } + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamCancel) + +BEGIN_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamGetReader) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + + RootedObject reader(cx); + reader = + ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default); + CHECK(reader); + CHECK(IsReadableStreamDefaultReader(reader)); + CHECK(ReadableStreamReaderReleaseLock(cx, reader)); + + RootedObject otherGlobal(cx, createGlobal()); + CHECK(otherGlobal); + { + JSAutoRealm ar(cx, otherGlobal); + CHECK(JS_WrapObject(cx, &stream)); + JSObject* callResult = + ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default); + CHECK(callResult); + } + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamGetReader) + +BEGIN_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamTee_CrossCompartment) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + + RootedObject branch1Stream(cx); + RootedObject branch2Stream(cx); + CHECK(ReadableStreamTee(cx, stream, &branch1Stream, &branch2Stream)); + CHECK(IsReadableStream(branch1Stream)); + CHECK(IsReadableStream(branch2Stream)); + stream = branch1Stream; + + RootedObject otherGlobal(cx, createGlobal()); + CHECK(otherGlobal); + { + JSAutoRealm ar(cx, otherGlobal); + CHECK(JS_WrapObject(cx, &stream)); + CHECK(ReadableStreamTee(cx, stream, &branch1Stream, &branch2Stream)); + CHECK(IsReadableStream(branch1Stream)); + CHECK(IsReadableStream(branch2Stream)); + } + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamTee_CrossCompartment) + +BEGIN_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamGetDesiredSize) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + bool hasValue; + double value; + CHECK(ReadableStreamGetDesiredSize(cx, stream, &hasValue, &value)); + CHECK_EQUAL(hasValue, true); + CHECK_EQUAL(value, 1.0); + + RootedObject otherGlobal(cx, createGlobal()); + CHECK(otherGlobal); + { + JSAutoRealm ar(cx, otherGlobal); + CHECK(JS_WrapObject(cx, &stream)); + hasValue = false; + value = 0; + CHECK(ReadableStreamGetDesiredSize(cx, stream, &hasValue, &value)); + CHECK_EQUAL(hasValue, true); + CHECK_EQUAL(value, 1.0); + } + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamGetDesiredSize) + +BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamClose) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + CHECK(ReadableStreamClose(cx, stream)); + + stream = NewDefaultStream(cx); + CHECK(stream); + RootedObject otherGlobal(cx, createGlobal()); + CHECK(otherGlobal); + { + JSAutoRealm ar(cx, otherGlobal); + CHECK(JS_WrapObject(cx, &stream)); + CHECK(ReadableStreamClose(cx, stream)); + } + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamClose) + +BEGIN_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamEnqueue_CrossCompartment) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + RootedValue chunk(cx); + CHECK(ReadableStreamEnqueue(cx, stream, chunk)); + + RootedObject otherGlobal(cx, createGlobal()); + CHECK(otherGlobal); + { + JSAutoRealm ar(cx, otherGlobal); + CHECK(JS_WrapObject(cx, &stream)); + RootedValue chunk(cx); + CHECK(ReadableStreamEnqueue(cx, stream, chunk)); + } + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamEnqueue_CrossCompartment) + +BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamError) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + RootedValue error(cx); + CHECK(ReadableStreamError(cx, stream, error)); + + stream = NewDefaultStream(cx); + RootedObject otherGlobal(cx, createGlobal()); + CHECK(otherGlobal); + { + JSAutoRealm ar(cx, otherGlobal); + CHECK(JS_WrapObject(cx, &stream)); + RootedValue error(cx); + CHECK(ReadableStreamError(cx, stream, error)); + } + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamError) + +BEGIN_FIXTURE_TEST(StreamTestFixture, + testReadableStream_IsReadableStreamReader) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + RootedObject reader(cx, ReadableStreamGetReader( + cx, stream, ReadableStreamReaderMode::Default)); + CHECK(reader); + CHECK(IsReadableStreamReader(reader)); + + RootedObject otherGlobal(cx, createGlobal()); + CHECK(otherGlobal); + { + JSAutoRealm ar(cx, otherGlobal); + CHECK(JS_WrapObject(cx, &reader)); + CHECK(IsReadableStreamReader(reader)); + } + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, testReadableStream_IsReadableStreamReader) + +BEGIN_FIXTURE_TEST(StreamTestFixture, + testReadableStream_IsReadableStreamDefaultReader) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + RootedObject reader(cx, ReadableStreamGetReader( + cx, stream, ReadableStreamReaderMode::Default)); + CHECK(IsReadableStreamDefaultReader(reader)); + + RootedObject otherGlobal(cx, createGlobal()); + CHECK(otherGlobal); + { + JSAutoRealm ar(cx, otherGlobal); + CHECK(JS_WrapObject(cx, &reader)); + CHECK(IsReadableStreamDefaultReader(reader)); + } + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, + testReadableStream_IsReadableStreamDefaultReader) + +BEGIN_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamReaderIsClosed) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + + RootedObject reader(cx, ReadableStreamGetReader( + cx, stream, ReadableStreamReaderMode::Default)); + bool result; + CHECK(ReadableStreamReaderIsClosed(cx, reader, &result)); + CHECK_EQUAL(result, false); + + RootedObject otherGlobal(cx, createGlobal()); + CHECK(otherGlobal); + { + JSAutoRealm ar(cx, otherGlobal); + CHECK(JS_WrapObject(cx, &reader)); + bool result; + CHECK(ReadableStreamReaderIsClosed(cx, reader, &result)); + } + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamReaderIsClosed) + +BEGIN_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamReaderCancel) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + RootedObject reader(cx, ReadableStreamGetReader( + cx, stream, ReadableStreamReaderMode::Default)); + RootedValue reason(cx); + CHECK(ReadableStreamReaderCancel(cx, reader, reason)); + + RootedObject otherGlobal(cx, createGlobal()); + CHECK(otherGlobal); + { + JSAutoRealm ar(cx, otherGlobal); + CHECK(JS_WrapObject(cx, &reader)); + RootedValue reason(cx); + CHECK(ReadableStreamReaderCancel(cx, reader, reason)); + } + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamReaderCancel) + +BEGIN_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamReaderReleaseLock) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + RootedObject reader(cx, ReadableStreamGetReader( + cx, stream, ReadableStreamReaderMode::Default)); + CHECK(reader); + CHECK(ReadableStreamReaderReleaseLock(cx, reader)); + + // Repeat the test cross-compartment. This creates a new reader, since + // releasing the lock above deactivated the first reader. + reader = + ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default); + CHECK(reader); + RootedObject otherGlobal(cx, createGlobal()); + CHECK(otherGlobal); + { + JSAutoRealm ar(cx, otherGlobal); + CHECK(JS_WrapObject(cx, &reader)); + CHECK(ReadableStreamReaderReleaseLock(cx, reader)); + } + + return true; +} +END_FIXTURE_TEST(StreamTestFixture, + testReadableStream_ReadableStreamReaderReleaseLock) + +BEGIN_FIXTURE_TEST( + StreamTestFixture, + testReadableStream_ReadableStreamDefaultReaderRead_CrossCompartment) { + RootedObject stream(cx, NewDefaultStream(cx)); + CHECK(stream); + RootedObject reader(cx, ReadableStreamGetReader( + cx, stream, ReadableStreamReaderMode::Default)); + JSObject* callResult = ReadableStreamDefaultReaderRead(cx, reader); + CHECK(callResult); + + RootedObject otherGlobal(cx, createGlobal()); + CHECK(otherGlobal); + { + JSAutoRealm ar(cx, otherGlobal); + CHECK(JS_WrapObject(cx, &reader)); + JSObject* callResult = ReadableStreamDefaultReaderRead(cx, reader); + CHECK(callResult); + } + + return true; +} +END_FIXTURE_TEST( + StreamTestFixture, + testReadableStream_ReadableStreamDefaultReaderRead_CrossCompartment) diff --git a/mozjs-sys/mozjs/js/src/moz.build b/mozjs-sys/mozjs/js/src/moz.build index 90dca3e7f7a..184cdfe1066 100755 --- a/mozjs-sys/mozjs/js/src/moz.build +++ b/mozjs-sys/mozjs/js/src/moz.build @@ -201,6 +201,7 @@ EXPORTS.js += [ "../public/SourceText.h", "../public/StableStringChars.h", "../public/Stack.h", + "../public/Stream.h", "../public/StreamConsumer.h", "../public/String.h", "../public/StructuredClone.h", @@ -643,7 +644,6 @@ if CONFIG["JS_HAS_CTYPES"]: if CONFIG["JS_HAS_INTL_API"]: if CONFIG["JS_STANDALONE"]: DIRS += [ - "../../intl/bidi", "../../intl/components", ] diff --git a/mozjs-sys/mozjs/js/src/shell/js.cpp b/mozjs-sys/mozjs/js/src/shell/js.cpp index 9b849a9b70d..907374858d4 100644 --- a/mozjs-sys/mozjs/js/src/shell/js.cpp +++ b/mozjs-sys/mozjs/js/src/shell/js.cpp @@ -12147,6 +12147,9 @@ bool InitOptionParser(OptionParser& op) { !op.addBoolOption('\0', "less-debug-code", "Emit less machine code for " "checking assertions under DEBUG.") || + !op.addBoolOption('\0', "enable-streams", + "Enable WHATWG Streams (default)") || + !op.addBoolOption('\0', "no-streams", "Disable WHATWG Streams") || !op.addBoolOption('\0', "disable-weak-refs", "Disable weak references") || !op.addBoolOption('\0', "disable-tosource", "Disable toSource/uneval") || !op.addBoolOption('\0', "disable-property-error-message-fix", diff --git a/mozjs-sys/mozjs/js/src/shell/jsshell.h b/mozjs-sys/mozjs/js/src/shell/jsshell.h index 67536a8f608..d3dead8286c 100644 --- a/mozjs-sys/mozjs/js/src/shell/jsshell.h +++ b/mozjs-sys/mozjs/js/src/shell/jsshell.h @@ -117,6 +117,11 @@ extern bool enableTestWasmAwaitTier2; extern bool enableSourcePragmas; extern bool enableAsyncStacks; extern bool enableAsyncStackCaptureDebuggeeOnly; +extern bool enableStreams; +extern bool enableReadableByteStreams; +extern bool enableBYOBStreamReaders; + +extern bool enableReadableStreamPipeTo; extern bool enableWeakRefs; extern bool enableToSource; extern bool enablePropertyErrorMessageFix; diff --git a/mozjs-sys/mozjs/js/src/vm/GlobalObject.cpp b/mozjs-sys/mozjs/js/src/vm/GlobalObject.cpp index cd50c9ddc6a..a4260133149 100644 --- a/mozjs-sys/mozjs/js/src/vm/GlobalObject.cpp +++ b/mozjs-sys/mozjs/js/src/vm/GlobalObject.cpp @@ -26,6 +26,11 @@ #include "builtin/FinalizationRegistryObject.h" #include "builtin/MapObject.h" #include "builtin/ShadowRealm.h" +#include "builtin/Stream.h" +#include "builtin/streams/QueueingStrategies.h" // js::{ByteLength,Count}QueueingStrategy +#include "builtin/streams/ReadableStream.h" // js::ReadableStream +#include "builtin/streams/ReadableStreamController.h" // js::Readable{StreamDefault,ByteStream}Controller +#include "builtin/streams/ReadableStreamReader.h" // js::ReadableStreamDefaultReader #include "builtin/Symbol.h" #ifdef JS_HAS_TEMPORAL_API # include "builtin/temporal/Calendar.h" @@ -211,6 +216,15 @@ bool GlobalObject::skipDeselectedConstructor(JSContext* cx, JSProtoKey key) { case JSProto_PluralRules: case JSProto_RelativeTimeFormat: return false; +#ifdef MOZ_JS_STREAMS + case JSProto_ReadableStream: + case JSProto_ReadableStreamDefaultReader: + case JSProto_ReadableStreamDefaultController: + case JSProto_ReadableByteStreamController: + case JSProto_ByteLengthQueuingStrategy: + case JSProto_CountQueuingStrategy: + return !cx->realm()->creationOptions().getStreamsEnabled(); +#endif case JSProto_Segmenter: # if defined(MOZ_ICU4X) diff --git a/mozjs-sys/mozjs/mfbt/moz.build b/mozjs-sys/mozjs/mfbt/moz.build index e3c036c20e2..73fc1c4575d 100644 --- a/mozjs-sys/mozjs/mfbt/moz.build +++ b/mozjs-sys/mozjs/mfbt/moz.build @@ -9,10 +9,6 @@ with Files("**"): Library("mfbt") -EXPORTS += [ - "../third_party/rust/encoding_c_mem/include/encoding_rs_mem.h", -] - EXPORTS.mozilla = [ "Algorithm.h", "Alignment.h", diff --git a/mozjs-sys/mozjs/moz.build b/mozjs-sys/mozjs/moz.build index 9db27fd056f..a330fd1aa47 100644 --- a/mozjs-sys/mozjs/moz.build +++ b/mozjs-sys/mozjs/moz.build @@ -183,10 +183,6 @@ if CONFIG["MOZ_BUILD_APP"]: else: include("/toolkit/toolkit.mozbuild") -OBJDIR_PP_FILES[".cargo"] += [ - CONFIG["MOZ_OVERRIDE_CARGO_CONFIG"] or ".cargo/config.toml.in" -] - DEFINES["top_srcdir"] = TOPSRCDIR SPHINX_TREES["contributing"] = "docs/contributing" diff --git a/mozjs-sys/src/jsapi.cpp b/mozjs-sys/src/jsapi.cpp index ed1f93e2007..8dd498772f3 100644 --- a/mozjs-sys/src/jsapi.cpp +++ b/mozjs-sys/src/jsapi.cpp @@ -224,7 +224,9 @@ bool CreateError(JSContext* cx, JSExnType type, JS::HandleObject stack, JS::HandleString message, JS::HandleValue cause, JS::MutableHandleValue rval) { return JS::CreateError( - cx, type, stack, fileName, lineNumber, columnNumber, report, message, + cx, type, stack, fileName, lineNumber, + JS::ColumnNumberOneOrigin::fromZeroOrigin(columnNumber), + report, message, JS::Rooted>(cx, mozilla::ToMaybe(&cause)), rval); } diff --git a/mozjs-sys/src/jsapi.hpp b/mozjs-sys/src/jsapi.hpp index d9751f476ab..07d0c3dd5f9 100644 --- a/mozjs-sys/src/jsapi.hpp +++ b/mozjs-sys/src/jsapi.hpp @@ -49,6 +49,7 @@ #include "js/shadow/Shape.h" #include "jsapi.h" #include "jsfriendapi.h" +#include "js/ColumnNumber.h" namespace glue { diff --git a/mozjs-sys/src/jsglue.cpp b/mozjs-sys/src/jsglue.cpp index 98b5c6c8321..0484537329d 100644 --- a/mozjs-sys/src/jsglue.cpp +++ b/mozjs-sys/src/jsglue.cpp @@ -35,6 +35,7 @@ #include "jsapi.h" #include "jsfriendapi.h" #include "mozilla/Unused.h" +#include "js/ColumnNumber.h" typedef bool (*WantToMeasure)(JSObject* obj); typedef size_t (*GetSize)(JSObject* obj); @@ -76,6 +77,8 @@ class RustJobQueue : public JS::JobQueue { MOZ_ASSERT(false, "runJobs should not be invoked"); } + bool isDrainingStopped() const override { return false; } + private: virtual js::UniquePtr saveJobQueue(JSContext* cx) { MOZ_ASSERT(false, "saveJobQueue should not be invoked"); @@ -161,6 +164,15 @@ class RustJSExternalStringCallbacks final : public JSExternalStringCallbacks { mozilla::MallocSizeOf mallocSizeOf) const override { return mTraps.sizeOfBuffer(privateData, chars, mallocSizeOf); } + + void finalize(JS::Latin1Char* chars) const override { + MOZ_ASSERT(false, "Latin1Char is not implemented for RustJSExternalStringCallbacks"); + } + + size_t sizeOfBuffer(const JS::Latin1Char* chars, + mozilla::MallocSizeOf mallocSizeOf) const override { + MOZ_ASSERT(false, "Latin1Char is not implemented for RustJSExternalStringCallbacks"); + } }; struct ProxyTraps { @@ -1091,9 +1103,11 @@ void StreamConsumerNoteResponseURLs(JS::StreamConsumer* sc, bool DescribeScriptedCaller(JSContext* cx, char* buffer, size_t buflen, uint32_t* line, uint32_t* col) { JS::AutoFilename filename; - if (!JS::DescribeScriptedCaller(cx, &filename, line, col)) { + JS::ColumnNumberOneOrigin column; + if (!JS::DescribeScriptedCaller(cx, &filename, line, &column)) { return false; } + *col = column.oneOriginValue() - 1; strncpy(buffer, filename.get(), buflen); return true; } @@ -1115,14 +1129,4 @@ void SetAccessorPropertyDescriptor( desc.set(JS::PropertyDescriptor::Accessor(getter, setter, attrs)); } -void FinishOffThreadStencil( - JSContext* cx, - JS::OffThreadToken* token, - JS::InstantiationStorage* storage, - already_AddRefed* stencil -) { - already_AddRefed retval = JS::FinishOffThreadStencil(cx, token, storage); - *stencil = std::move(retval); -} - } // extern "C" diff --git a/mozjs-sys/src/lib.rs b/mozjs-sys/src/lib.rs index aba3bcb5357..06d73f312fe 100644 --- a/mozjs-sys/src/lib.rs +++ b/mozjs-sys/src/lib.rs @@ -7,6 +7,7 @@ // These extern crates are needed for linking extern crate encoding_c; extern crate encoding_c_mem; +extern crate icu_capi; extern crate libz_sys; // The jsimpls module just implements traits so can be private diff --git a/mozjs/src/jsapi_wrappers.in b/mozjs/src/jsapi_wrappers.in index cc478c8a6aa..fc43a834a37 100644 --- a/mozjs/src/jsapi_wrappers.in +++ b/mozjs/src/jsapi_wrappers.in @@ -20,6 +20,7 @@ wrap!(jsapi: pub fn SetWindowProxy(cx: *mut JSContext, global: HandleObject, win wrap!(jsapi: pub fn IsArgumentsObject(obj: HandleObject) -> bool); wrap!(jsapi: pub fn EnqueueJob(cx: *mut JSContext, job: HandleObject) -> bool); wrap!(jsapi: pub fn AssertSameCompartment1(cx: *mut JSContext, v: HandleValue)); +wrap!(jsapi: pub fn NewFunctionByIdWithReservedAndProto(cx: *mut JSContext, native: JSNative, proto: HandleObject, nargs: ::std::os::raw::c_uint, flags: ::std::os::raw::c_uint, id: jsid) -> *mut JSFunction); wrap!(jsapi: pub fn GetObjectProto(cx: *mut JSContext, obj: HandleObject, proto: MutableHandleObject) -> bool); wrap!(jsapi: pub fn GetRealmOriginalEval(cx: *mut JSContext, eval: MutableHandleObject) -> bool); wrap!(jsapi: pub fn GetPropertyKeys(cx: *mut JSContext, obj: HandleObject, flags: ::std::os::raw::c_uint, props: MutableHandleIdVector) -> bool); @@ -53,14 +54,16 @@ wrap!(jsapi: pub fn CompileFunctionUtf8(cx: *mut JSContext, envChain: HandleObje wrap!(jsapi: pub fn ExposeScriptToDebugger(cx: *mut JSContext, script: Handle<*mut JSScript>)); wrap!(jsapi: pub fn UpdateDebugMetadata(cx: *mut JSContext, script: Handle<*mut JSScript>, options: *const InstantiateOptions, privateValue: HandleValue, elementAttributeName: HandleString, introScript: HandleScript, scriptOrModule: HandleScript) -> bool); wrap!(jsapi: pub fn OrdinaryToPrimitive(cx: *mut JSContext, obj: HandleObject, type_: JSType, vp: MutableHandleValue) -> bool); +wrap!(jsapi: pub fn ToGetterId(cx: *mut JSContext, id: Handle, getterId: MutableHandle) -> bool); +wrap!(jsapi: pub fn ToSetterId(cx: *mut JSContext, id: Handle, setterId: MutableHandle) -> bool); wrap!(jsapi: pub fn ObjectIsDate(cx: *mut JSContext, obj: HandleObject, isDate: *mut bool) -> bool); wrap!(jsapi: pub fn StrictlyEqual(cx: *mut JSContext, v1: Handle, v2: Handle, equal: *mut bool) -> bool); wrap!(jsapi: pub fn LooselyEqual(cx: *mut JSContext, v1: Handle, v2: Handle, equal: *mut bool) -> bool); wrap!(jsapi: pub fn SameValue(cx: *mut JSContext, v1: Handle, v2: Handle, same: *mut bool) -> bool); -wrap!(jsapi: pub fn ToGetterId(cx: *mut JSContext, id: Handle, getterId: MutableHandle) -> bool); -wrap!(jsapi: pub fn ToSetterId(cx: *mut JSContext, id: Handle, setterId: MutableHandle) -> bool); wrap!(jsapi: pub fn ToJSONMaybeSafely(cx: *mut JSContext, input: HandleObject, callback: JSONWriteCallback, data: *mut ::std::os::raw::c_void) -> bool); wrap!(jsapi: pub fn ToJSON(cx: *mut JSContext, value: Handle, replacer: HandleObject, space: Handle, callback: JSONWriteCallback, data: *mut ::std::os::raw::c_void) -> bool); +wrap!(jsapi: pub fn ParseJSONWithHandler(chars: *const Latin1Char, len: u32, handler: *mut JSONParseHandler) -> bool); +wrap!(jsapi: pub fn ParseJSONWithHandler1(chars: *const u16, len: u32, handler: *mut JSONParseHandler) -> bool); wrap!(jsapi: pub fn AddSizeOfTab(cx: *mut JSContext, obj: HandleObject, mallocSizeOf: MallocSizeOf, opv: *mut ObjectPrivateVisitor, sizes: *mut TabSizes) -> bool); wrap!(jsapi: pub fn FinishDynamicModuleImport(cx: *mut JSContext, evaluationPromise: HandleObject, referencingPrivate: Handle, moduleRequest: HandleObject, promise: HandleObject) -> bool); wrap!(jsapi: pub fn ModuleLink(cx: *mut JSContext, moduleRecord: HandleObject) -> bool); @@ -68,7 +71,7 @@ wrap!(jsapi: pub fn ModuleEvaluate(cx: *mut JSContext, moduleRecord: HandleObjec wrap!(jsapi: pub fn ThrowOnModuleEvaluationFailure(cx: *mut JSContext, evaluationPromise: HandleObject, errorBehaviour: ModuleErrorBehaviour) -> bool); wrap!(jsapi: pub fn GetRequestedModulesCount(cx: *mut JSContext, moduleRecord: HandleObject) -> u32); wrap!(jsapi: pub fn GetRequestedModuleSpecifier(cx: *mut JSContext, moduleRecord: HandleObject, index: u32) -> *mut JSString); -wrap!(jsapi: pub fn GetRequestedModuleSourcePos(cx: *mut JSContext, moduleRecord: HandleObject, index: u32, lineNumber: *mut u32, columnNumber: *mut u32)); +wrap!(jsapi: pub fn GetRequestedModuleSourcePos(cx: *mut JSContext, moduleRecord: HandleObject, index: u32, lineNumber: *mut u32, columnNumber: *mut ColumnNumberOneOrigin)); wrap!(jsapi: pub fn GetModuleScript(moduleRecord: HandleObject) -> *mut JSScript); wrap!(jsapi: pub fn CreateModuleRequest(cx: *mut JSContext, specifierArg: Handle<*mut JSString>) -> *mut JSObject); wrap!(jsapi: pub fn GetModuleRequestSpecifier(cx: *mut JSContext, moduleRequestArg: HandleObject) -> *mut JSString); @@ -96,7 +99,7 @@ wrap!(jsapi: pub fn AddPromiseReactionsIgnoringUnhandledRejection(cx: *mut JSCon wrap!(jsapi: pub fn GetPromiseUserInputEventHandlingState(promise: HandleObject) -> PromiseUserInputEventHandlingState); wrap!(jsapi: pub fn SetPromiseUserInputEventHandlingState(promise: HandleObject, state: PromiseUserInputEventHandlingState) -> bool); wrap!(jsapi: pub fn GetWaitForAllPromise(cx: *mut JSContext, promises: HandleObjectVector) -> *mut JSObject); -wrap!(jsapi: pub fn ObjectToCompletePropertyDescriptor(cx: *mut JSContext, obj: HandleObject, descriptor: Handle, desc: MutableHandle) -> bool); +wrap!(jsapi: pub fn ToCompletePropertyDescriptor(cx: *mut JSContext, descriptor: Handle, desc: MutableHandle) -> bool); wrap!(jsapi: pub fn NewSymbol(cx: *mut JSContext, description: Handle<*mut JSString>) -> *mut Symbol); wrap!(jsapi: pub fn GetSymbolFor(cx: *mut JSContext, key: Handle<*mut JSString>) -> *mut Symbol); wrap!(jsapi: pub fn GetSymbolDescription(symbol: Handle<*mut Symbol>) -> *mut JSString); @@ -118,7 +121,7 @@ wrap!(jsapi: pub fn CheckRegExpSyntax(cx: *mut JSContext, chars: *const u16, len wrap!(jsapi: pub fn GetSavedFrameSource(cx: *mut JSContext, principals: *mut JSPrincipals, savedFrame: HandleObject, sourcep: MutableHandle<*mut JSString>, selfHosted: SavedFrameSelfHosted) -> SavedFrameResult); wrap!(jsapi: pub fn GetSavedFrameSourceId(cx: *mut JSContext, principals: *mut JSPrincipals, savedFrame: HandleObject, sourceIdp: *mut u32, selfHosted: SavedFrameSelfHosted) -> SavedFrameResult); wrap!(jsapi: pub fn GetSavedFrameLine(cx: *mut JSContext, principals: *mut JSPrincipals, savedFrame: HandleObject, linep: *mut u32, selfHosted: SavedFrameSelfHosted) -> SavedFrameResult); -wrap!(jsapi: pub fn GetSavedFrameColumn(cx: *mut JSContext, principals: *mut JSPrincipals, savedFrame: HandleObject, columnp: *mut u32, selfHosted: SavedFrameSelfHosted) -> SavedFrameResult); +wrap!(jsapi: pub fn GetSavedFrameColumn(cx: *mut JSContext, principals: *mut JSPrincipals, savedFrame: HandleObject, columnp: *mut TaggedColumnNumberOneOrigin, selfHosted: SavedFrameSelfHosted) -> SavedFrameResult); wrap!(jsapi: pub fn GetSavedFrameFunctionDisplayName(cx: *mut JSContext, principals: *mut JSPrincipals, savedFrame: HandleObject, namep: MutableHandle<*mut JSString>, selfHosted: SavedFrameSelfHosted) -> SavedFrameResult); wrap!(jsapi: pub fn GetSavedFrameAsyncCause(cx: *mut JSContext, principals: *mut JSPrincipals, savedFrame: HandleObject, asyncCausep: MutableHandle<*mut JSString>, selfHosted: SavedFrameSelfHosted) -> SavedFrameResult); wrap!(jsapi: pub fn GetSavedFrameAsyncParent(cx: *mut JSContext, principals: *mut JSPrincipals, savedFrame: HandleObject, asyncParentp: MutableHandleObject, selfHosted: SavedFrameSelfHosted) -> SavedFrameResult); @@ -170,8 +173,8 @@ wrap!(jsapi: pub fn SetKeys(cx: *mut JSContext, obj: HandleObject, rval: Mutable wrap!(jsapi: pub fn SetValues(cx: *mut JSContext, obj: HandleObject, rval: MutableHandleValue) -> bool); wrap!(jsapi: pub fn SetEntries(cx: *mut JSContext, obj: HandleObject, rval: MutableHandleValue) -> bool); wrap!(jsapi: pub fn SetForEach(cx: *mut JSContext, obj: HandleObject, callbackFn: HandleValue, thisVal: HandleValue) -> bool); -wrap!(jsapi: pub fn GetWeakMapEntry(cx: *mut JSContext, mapObj: HandleObject, key: HandleObject, val: MutableHandleValue) -> bool); -wrap!(jsapi: pub fn SetWeakMapEntry(cx: *mut JSContext, mapObj: HandleObject, key: HandleObject, val: HandleValue) -> bool); +wrap!(jsapi: pub fn GetWeakMapEntry(cx: *mut JSContext, mapObj: HandleObject, key: HandleValue, val: MutableHandleValue) -> bool); +wrap!(jsapi: pub fn SetWeakMapEntry(cx: *mut JSContext, mapObj: HandleObject, key: HandleValue, val: HandleValue) -> bool); wrap!(jsapi: pub fn ProtoKeyToId(cx: *mut JSContext, key: JSProtoKey, idp: MutableHandleId)); wrap!(jsapi: pub fn ToPrimitive(cx: *mut JSContext, obj: HandleObject, hint: JSType, vp: MutableHandleValue) -> bool); wrap!(jsapi: pub fn OrdinaryHasInstance(cx: *mut JSContext, objArg: HandleObject, v: HandleValue, bp: *mut bool) -> bool); @@ -186,13 +189,13 @@ wrap!(jsapi: pub fn JS_ExecuteScript(cx: *mut JSContext, script: Handle<*mut JSS wrap!(jsapi: pub fn JS_ExecuteScript1(cx: *mut JSContext, script: Handle<*mut JSScript>) -> bool); wrap!(jsapi: pub fn JS_ExecuteScript2(cx: *mut JSContext, envChain: HandleObjectVector, script: Handle<*mut JSScript>, rval: MutableHandle) -> bool); wrap!(jsapi: pub fn JS_ExecuteScript3(cx: *mut JSContext, envChain: HandleObjectVector, script: Handle<*mut JSScript>) -> bool); +wrap!(jsapi: pub fn JS_EncodeStringToUTF8(cx: *mut JSContext, str_: Handle<*mut JSString>) -> UniqueChars); wrap!(jsapi: pub fn JS_Stringify(cx: *mut JSContext, value: MutableHandle, replacer: HandleObject, space: Handle, callback: JSONWriteCallback, data: *mut ::std::os::raw::c_void) -> bool); wrap!(jsapi: pub fn JS_ParseJSON(cx: *mut JSContext, chars: *const u16, len: u32, vp: MutableHandle) -> bool); wrap!(jsapi: pub fn JS_ParseJSON1(cx: *mut JSContext, str_: Handle<*mut JSString>, vp: MutableHandle) -> bool); wrap!(jsapi: pub fn JS_ParseJSON2(cx: *mut JSContext, chars: *const Latin1Char, len: u32, vp: MutableHandle) -> bool); wrap!(jsapi: pub fn JS_ParseJSONWithReviver(cx: *mut JSContext, chars: *const u16, len: u32, reviver: Handle, vp: MutableHandle) -> bool); wrap!(jsapi: pub fn JS_ParseJSONWithReviver1(cx: *mut JSContext, str_: Handle<*mut JSString>, reviver: Handle, vp: MutableHandle) -> bool); -wrap!(jsapi: pub fn JS_EncodeStringToUTF8(cx: *mut JSContext, str_: Handle<*mut JSString>) -> UniqueChars); wrap!(jsapi: pub fn JS_NewDependentString(cx: *mut JSContext, str_: Handle<*mut JSString>, start: usize, length: usize) -> *mut JSString); wrap!(jsapi: pub fn JS_ConcatStrings(cx: *mut JSContext, left: Handle<*mut JSString>, right: Handle<*mut JSString>) -> *mut JSString); wrap!(jsapi: pub fn JS_ReadStructuredClone(cx: *mut JSContext, data: *const JSStructuredCloneData, version: u32, scope: StructuredCloneScope, vp: MutableHandleValue, cloneDataPolicy: *const CloneDataPolicy, optionalCallbacks: *const JSStructuredCloneCallbacks, closure: *mut ::std::os::raw::c_void) -> bool); @@ -225,6 +228,8 @@ wrap!(jsapi: pub fn JS_NewBigInt64ArrayFromArray(cx: *mut JSContext, array: Hand wrap!(jsapi: pub fn JS_NewBigInt64ArrayWithBuffer(cx: *mut JSContext, arrayBuffer: HandleObject, byteOffset: usize, length: i64) -> *mut JSObject); wrap!(jsapi: pub fn JS_NewBigUint64ArrayFromArray(cx: *mut JSContext, array: HandleObject) -> *mut JSObject); wrap!(jsapi: pub fn JS_NewBigUint64ArrayWithBuffer(cx: *mut JSContext, arrayBuffer: HandleObject, byteOffset: usize, length: i64) -> *mut JSObject); +wrap!(jsapi: pub fn JS_NewFloat16ArrayFromArray(cx: *mut JSContext, array: HandleObject) -> *mut JSObject); +wrap!(jsapi: pub fn JS_NewFloat16ArrayWithBuffer(cx: *mut JSContext, arrayBuffer: HandleObject, byteOffset: usize, length: i64) -> *mut JSObject); wrap!(jsapi: pub fn JS_GetArrayBufferViewBuffer(cx: *mut JSContext, obj: HandleObject, isSharedMemory: *mut bool) -> *mut JSObject); wrap!(jsapi: pub fn JS_NewDataView(cx: *mut JSContext, buffer: HandleObject, byteOffset: usize, byteLength: usize) -> *mut JSObject); wrap!(jsapi: pub fn JS_CallFunctionValue(cx: *mut JSContext, obj: HandleObject, fval: Handle, args: *const HandleValueArray, rval: MutableHandle) -> bool); @@ -295,7 +300,7 @@ wrap!(jsapi: pub fn JS_DeletePropertyById(cx: *mut JSContext, obj: HandleObject, wrap!(jsapi: pub fn JS_DeleteProperty(cx: *mut JSContext, obj: HandleObject, name: *const ::std::os::raw::c_char, result: *mut ObjectOpResult) -> bool); wrap!(jsapi: pub fn JS_DeleteUCProperty(cx: *mut JSContext, obj: HandleObject, name: *const u16, namelen: usize, result: *mut ObjectOpResult) -> bool); wrap!(jsapi: pub fn JS_DeleteElement(cx: *mut JSContext, obj: HandleObject, index: u32, result: *mut ObjectOpResult) -> bool); -wrap!(jsapi: pub fn JS_DeletePropertyById1(cx: *mut JSContext, obj: HandleObject, id: jsid) -> bool); +wrap!(jsapi: pub fn JS_DeletePropertyById1(cx: *mut JSContext, obj: HandleObject, id: Handle) -> bool); wrap!(jsapi: pub fn JS_DeleteProperty1(cx: *mut JSContext, obj: HandleObject, name: *const ::std::os::raw::c_char) -> bool); wrap!(jsapi: pub fn JS_DeleteElement1(cx: *mut JSContext, obj: HandleObject, index: u32) -> bool); wrap!(jsapi: pub fn JS_DefineObject(cx: *mut JSContext, obj: HandleObject, name: *const ::std::os::raw::c_char, clasp: *const JSClass, attrs: ::std::os::raw::c_uint) -> *mut JSObject); @@ -345,6 +350,8 @@ wrap!(jsapi: pub fn JS_PreventExtensions(cx: *mut JSContext, obj: HandleObject, wrap!(jsapi: pub fn JS_SetImmutablePrototype(cx: *mut JSContext, obj: HandleObject, succeeded: *mut bool) -> bool); wrap!(jsapi: pub fn JS_AssignObject(cx: *mut JSContext, target: HandleObject, src: HandleObject) -> bool); wrap!(jsapi: pub fn JS_SetAllNonReservedSlotsToUndefined(obj: HandleObject)); +wrap!(jsapi: pub fn JS_GetFunctionId(cx: *mut JSContext, fun: Handle<*mut JSFunction>, name: MutableHandle<*mut JSString>) -> bool); +wrap!(jsapi: pub fn JS_GetFunctionDisplayId(cx: *mut JSContext, fun: Handle<*mut JSFunction>, name: MutableHandle<*mut JSString>) -> bool); wrap!(jsapi: pub fn JS_GetFunctionLength(cx: *mut JSContext, fun: HandleFunction, length: *mut u16) -> bool); wrap!(jsapi: pub fn JS_GetFunctionScript(cx: *mut JSContext, fun: HandleFunction) -> *mut JSScript); wrap!(jsapi: pub fn JS_DecompileScript(cx: *mut JSContext, script: Handle<*mut JSScript>) -> *mut JSString); diff --git a/mozjs/src/rust.rs b/mozjs/src/rust.rs index 92ff809a8bb..61c35d94cf5 100644 --- a/mozjs/src/rust.rs +++ b/mozjs/src/rust.rs @@ -44,7 +44,7 @@ use crate::jsapi::MutableHandleIdVector as RawMutableHandleIdVector; use crate::jsapi::{already_AddRefed, jsid}; use crate::jsapi::{BuildStackString, CaptureCurrentStack, StackFormat}; use crate::jsapi::{Evaluate2, HandleValueArray, StencilRelease}; -use crate::jsapi::{InitSelfHostedCode, InstantiationStorage, IsWindowSlow, OffThreadToken}; +use crate::jsapi::{InitSelfHostedCode, InstantiationStorage, IsWindowSlow}; use crate::jsapi::{ JSAutoRealm, JS_SetGCParameter, JS_SetNativeStackQuota, JS_WrapObject, JS_WrapValue, }; @@ -518,19 +518,6 @@ impl Stencil { } } -pub unsafe fn FinishOffThreadStencil( - cx: *mut JSContext, - token: *mut OffThreadToken, - storage: *mut InstantiationStorage, -) -> Stencil { - let mut stencil = already_AddRefed { - mRawPtr: std::ptr::null_mut(), - _phantom_0: PhantomData, - }; - crate::glue::FinishOffThreadStencil(cx, token, storage, &mut stencil); - return Stencil { inner: stencil }; -} - // ___________________________________________________________________________ // Fast inline converters @@ -650,7 +637,7 @@ pub unsafe extern "C" fn report_warning(_cx: *mut JSContext, report: *mut JSErro .collect() } - let fnptr = (*report)._base.filename; + let fnptr = (*report)._base.filename.data_; let fname = if !fnptr.is_null() { let c_str = CStr::from_ptr(fnptr); latin1_to_string(c_str.to_bytes()) @@ -659,7 +646,7 @@ pub unsafe extern "C" fn report_warning(_cx: *mut JSContext, report: *mut JSErro }; let lineno = (*report)._base.lineno; - let column = (*report)._base.column; + let column = (*report)._base.column._base - 1; let msg_ptr = (*report)._base.message_.data_ as *const u8; let msg_len = (0usize..) @@ -1097,9 +1084,12 @@ pub mod wrappers { use crate::jsapi::BigInt; use crate::jsapi::CallArgs; use crate::jsapi::CloneDataPolicy; + use crate::jsapi::ColumnNumberOneOrigin; use crate::jsapi::CompartmentTransplantCallback; + use crate::jsapi::JSONParseHandler; use crate::jsapi::Latin1Char; use crate::jsapi::PropertyKey; + use crate::jsapi::TaggedColumnNumberOneOrigin; //use jsapi::DynamicImportStatus; use crate::jsapi::ESClass; use crate::jsapi::ExceptionStackBehavior; @@ -1245,6 +1235,7 @@ pub mod jsapi_wrapped { use crate::jsapi::BigInt; use crate::jsapi::CallArgs; use crate::jsapi::CloneDataPolicy; + use crate::jsapi::ColumnNumberOneOrigin; use crate::jsapi::CompartmentTransplantCallback; use crate::jsapi::ESClass; use crate::jsapi::ExceptionStackBehavior; @@ -1258,6 +1249,7 @@ pub mod jsapi_wrapped { use crate::jsapi::JSFunctionSpec; use crate::jsapi::JSFunctionSpecWithHelp; use crate::jsapi::JSJitInfo; + use crate::jsapi::JSONParseHandler; use crate::jsapi::JSONWriteCallback; use crate::jsapi::JSPrincipals; use crate::jsapi::JSPropertySpec; @@ -1285,6 +1277,7 @@ pub mod jsapi_wrapped { use crate::jsapi::StructuredCloneScope; use crate::jsapi::Symbol; use crate::jsapi::SymbolCode; + use crate::jsapi::TaggedColumnNumberOneOrigin; use crate::jsapi::TwoByteChars; use crate::jsapi::UniqueChars; use crate::jsapi::Value; diff --git a/mozjs/tests/offthread.rs b/mozjs/tests/offthread.rs deleted file mode 100644 index 40b72e3c904..00000000000 --- a/mozjs/tests/offthread.rs +++ /dev/null @@ -1,94 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use std::os::raw::c_void; -use std::ptr; -use std::sync::mpsc::{channel, Sender}; - -use mozjs::jsapi::{ - CanCompileOffThread, CompileToStencilOffThread1, InstantiateGlobalStencil, InstantiateOptions, - JSAutoRealm, JS_NewGlobalObject, OffThreadToken, OnNewGlobalHookOption, -}; -use mozjs::jsval::UndefinedValue; -use mozjs::rooted; -use mozjs::rust::{ - transform_str_to_source_text, wrappers::JS_ExecuteScript, CompileOptionsWrapper, - FinishOffThreadStencil, JSEngine, RealmOptions, Runtime, SIMPLE_GLOBAL_CLASS, -}; - -struct Token(*mut OffThreadToken); - -unsafe impl Send for Token {} - -struct Context { - text: String, - sender: Sender, -} - -unsafe extern "C" fn callback(token: *mut OffThreadToken, callback_data: *mut c_void) { - let context = Box::from_raw(callback_data as *mut Context); - let token = Token(token); - context.sender.send(token).unwrap(); -} - -#[test] -fn evaluate() { - let engine = JSEngine::init().unwrap(); - let runtime = Runtime::new(engine.handle()); - let context = runtime.cx(); - let h_option = OnNewGlobalHookOption::FireOnNewGlobalHook; - let c_option = RealmOptions::default(); - - unsafe { - rooted!(in(context) let global = JS_NewGlobalObject( - context, - &SIMPLE_GLOBAL_CLASS, - ptr::null_mut(), - h_option, - &*c_option, - )); - - let _ac = JSAutoRealm::new(context, global.get()); - - let src = "1 + 1".to_string(); - let options = CompileOptionsWrapper::new(context, "", 1); - (*options.ptr)._base.forceAsync = true; - let options_ptr = options.ptr as *const _; - assert!(CanCompileOffThread(context, options_ptr, src.len())); - let (sender, receiver) = channel(); - let script_context = Box::new(Context { text: src, sender }); - assert!(!CompileToStencilOffThread1( - context, - options_ptr, - &mut transform_str_to_source_text(&script_context.text) as *mut _, - Some(callback), - Box::into_raw(script_context) as *mut c_void, - ) - .is_null()); - - let token = receiver.recv().unwrap(); - let compiled_script = FinishOffThreadStencil(context, token.0, ptr::null_mut()); - assert!(!compiled_script.is_null()); - - let options = InstantiateOptions { - skipFilenameValidation: false, - hideScriptFromDebugger: false, - deferDebugMetadata: false, - }; - rooted!(in(context) let script = InstantiateGlobalStencil( - context, - &options, - *compiled_script, - ptr::null_mut(), - )); - - rooted!(in(context) let mut rval = UndefinedValue()); - let result = JS_ExecuteScript(context, script.handle(), rval.handle_mut()); - assert!(result); - /*assert!(runtime - .evaluate_script(global.handle(), "1 + 1", "test", 1, rval.handle_mut()) - .is_ok());*/ - assert_eq!(rval.get().to_int32(), 2); - } -} diff --git a/mozjs/tests/rooting.rs b/mozjs/tests/rooting.rs index e850335ff54..c79461d1e7c 100644 --- a/mozjs/tests/rooting.rs +++ b/mozjs/tests/rooting.rs @@ -8,7 +8,7 @@ use std::ptr; use mozjs::jsapi::JSPROP_ENUMERATE; use mozjs::jsapi::{ - GetRealmObjectPrototype, JS_NewGlobalObject, JS_NewObjectWithGivenProto, JS_SetGCZeal, + GetRealmObjectPrototype, JS_NewGlobalObject, JS_NewObjectWithGivenProto, SetGCZeal, }; use mozjs::jsapi::{ JSAutoRealm, JSClass, JSContext, JSFunction, JSFunctionSpec, JSNativeWrapper, JSObject, @@ -27,7 +27,7 @@ fn rooting() { let c_option = RealmOptions::default(); unsafe { - JS_SetGCZeal(context, 2, 1); + SetGCZeal(context, 2, 1); rooted!(in(context) let global = JS_NewGlobalObject( context, &SIMPLE_GLOBAL_CLASS,