@ 261027.7

261018.1 · 4caa87cda978f25211499266fcc99e637c582ebc

chore: update swc to v1.16.18

reading declarations

Changes to deps/swc/tools/swc-native-addon-pack/. All files

deps/swc/tools/swc-native-addon-pack/Cargo.toml

new file mode 100644@@ -0,0 +1,23 @@+[package]+name = "swc_native_addon_pack"+version = "0.1.0"+edition = "2021"+rust-version = "1.85"+license = "Apache-2.0"+publish = false++[[bin]]+name = "swc-native-addon-pack"+path = "src/main.rs"++[[bin]]+name = "swc-native-addon-verify"+path = "src/verify.rs"++[dependencies]+swc_native_addon = { path = "../../crates/swc_native_addon" }+tempfile = "=3.12.0"+memchr = "=2.7.6"+object = { version = "=0.36.7", default-features = false, features = ["read", "std"] }+serde_json = "=1.0.140"+sha2 = "=0.10.9"

deps/swc/tools/swc-native-addon-pack/src/carrier.rs

new file mode 100644@@ -0,0 +1,101 @@+//! Inspect the final linked image, including its mapped payload and exports.+//! A magic string in debug information or an appended overlay is not a carrier.++use std::io::Cursor;++use object::{Object, ObjectSection, SectionFlags};+use swc_native_addon::{+    format::{native_target, NativeTarget, Payload, HEADER_LEN, MAX_SIZE},+    integrity::{RuntimeIntegrity, INTEGRITY_LEN},+};++pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;++/// Find the retained payload section in the completed native image. The build+/// script keeps one complete allocation, even when LTO could otherwise fold+/// header reads and discard parts of the original include_bytes allocation.+/// Full decoding and raw-image verification remain mandatory for callers.+pub fn payload(bytes: &[u8], target: NativeTarget) -> Result<Payload<'_>> {+    native_target(&mut Cursor::new(bytes), bytes.len() as u64, target)?;+    let image = object::File::parse(bytes)?;+    let exports = image.exports()?;+    for name in [+        b"napi_register_module_v1".as_slice(),+        b"node_api_module_get_api_version_v1".as_slice(),+    ] {+        if !exports.iter().any(|export| {+            let exported = export.name();+            exported == name+                || (image.format() == object::BinaryFormat::MachO+                    && exported.strip_prefix(b"_") == Some(name))+        }) {+            return Err(format!(+                "carrier is missing export {}",+                String::from_utf8_lossy(name)+            )+            .into());+        }+    }+    let section_name = match image.format() {+        object::BinaryFormat::Elf => ".swc_native",+        object::BinaryFormat::MachO => "__swc_native",+        object::BinaryFormat::Pe => ".swcn",+        _ => return Err("unsupported carrier image format".into()),+    };+    let data = retained_section(&image, section_name)?;+    if data.len() < HEADER_LEN {+        return Err("truncated carrier payload section".into());+    }+    let compressed = u64::from_le_bytes(data[16..24].try_into()?);+    if compressed > MAX_SIZE {+        return Err("carrier payload exceeds 2 GiB".into());+    }+    let end = usize::try_from(HEADER_LEN as u64 + compressed)?;+    let candidate = data.get(..end).ok_or("truncated carrier payload")?;+    // PE sections may have file-alignment padding, but no second payload or+    // trailing data may be hidden in the private section.+    if data[end..].iter().any(|byte| *byte != 0) {+        return Err("unexpected data after carrier payload".into());+    }+    let payload = Payload::parse(candidate)?;+    if payload.header.target != target {+        return Err("carrier payload target mismatch".into());+    }+    let integrity_name = match image.format() {+        object::BinaryFormat::Elf => ".swc_integrity",+        object::BinaryFormat::MachO => "__swc_integrity",+        object::BinaryFormat::Pe => ".swci",+        _ => unreachable!(),+    };+    let metadata = retained_section(&image, integrity_name)?;+    let encoded = metadata+        .get(..INTEGRITY_LEN)+        .ok_or("truncated carrier integrity metadata")?;+    if metadata[INTEGRITY_LEN..].iter().any(|byte| *byte != 0) {+        return Err("unexpected data after carrier integrity metadata".into());+    }+    Ok(payload.with_integrity(RuntimeIntegrity::parse(encoded)?)?)+}++fn retained_section<'data>(image: &object::File<'data>, section_name: &str) -> Result<&'data [u8]> {+    let mut sections = image+        .sections()+        .filter(|section| section.name().ok() == Some(section_name));+    let section = sections+        .next()+        .ok_or("carrier is missing its retained section")?;+    if sections.next().is_some() {+        return Err("carrier contains multiple retained sections".into());+    }+    match section.flags() {+        SectionFlags::Elf { sh_flags }+            if sh_flags & u64::from(object::elf::SHF_ALLOC) != 0+                && sh_flags & u64::from(object::elf::SHF_WRITE) == 0 => {}+        SectionFlags::Coff { characteristics }+            if characteristics & object::pe::IMAGE_SCN_MEM_READ != 0+                && characteristics & object::pe::IMAGE_SCN_MEM_WRITE == 0 => {}+        SectionFlags::MachO { .. } if section.segment_name()? == Some("__TEXT") => {}+        _ => return Err("carrier section must be in mapped read-only data".into()),+    }+    Ok(section.data()?)+}

deps/swc/tools/swc-native-addon-pack/src/main.rs

new file mode 100644@@ -0,0 +1,137 @@+//! Host-only packer. The input is read-only, and its sibling CLI is never+//! opened.++use std::{+    env, fs,+    io::{self, Cursor, Read, Seek, SeekFrom, Write},+    path::PathBuf,+};++/// Verifies a decoded payload without retaining a second raw addon image.+/// `Payload::decode_into` also seeks to validate the native file kind, so the+/// original raw bytes serve as the readable backing store after each compare.+struct RawVerifier<'a> {+    raw: &'a [u8],+    cursor: Cursor<&'a [u8]>,+    written: usize,+}++impl<'a> RawVerifier<'a> {+    fn new(raw: &'a [u8]) -> Self {+        Self {+            raw,+            cursor: Cursor::new(raw),+            written: 0,+        }+    }+}++impl Write for RawVerifier<'_> {+    fn write(&mut self, buffer: &[u8]) -> io::Result<usize> {+        let end = self.written.checked_add(buffer.len()).ok_or_else(|| {+            io::Error::new(io::ErrorKind::InvalidData, "decoded addon length overflow")+        })?;+        if self.raw.get(self.written..end) != Some(buffer) {+            return Err(io::Error::new(+                io::ErrorKind::InvalidData,+                "decoded addon differs from packer input",+            ));+        }+        self.written = end;+        Ok(buffer.len())+    }++    fn flush(&mut self) -> io::Result<()> {+        Ok(())+    }+}++impl Read for RawVerifier<'_> {+    fn read(&mut self, buffer: &mut [u8]) -> io::Result<usize> {+        self.cursor.read(buffer)+    }+}++impl Seek for RawVerifier<'_> {+    fn seek(&mut self, position: SeekFrom) -> io::Result<u64> {+        self.cursor.seek(position)+    }+}++use swc_native_addon::{+    format::{pack, NativeTarget, Payload, MAX_SIZE},+    platform,+};++fn run() -> Result<(), Box<dyn std::error::Error>> {+    let mut args = env::args_os().skip(1);+    let mut input = None;+    let mut output = None;+    let mut target = None;+    while let Some(arg) = args.next() {+        if arg == "--input" && input.is_none() {+            input = args.next().map(PathBuf::from);+        } else if arg == "--output" && output.is_none() {+            output = args.next().map(PathBuf::from);+        } else if arg == "--target" && target.is_none() {+            target = args.next().and_then(|value| value.into_string().ok());+        } else {+            return Err(+                "usage: swc-native-addon-pack --input <final-stripped.node> --output \+                 <payload.swcn> --target <supported-target>"+                    .into(),+            );+        }+    }+    let input = input.ok_or("--input is required")?;+    let output = output.ok_or("--output is required")?;+    let target = NativeTarget::parse(&target.ok_or("--target is required")?)?;+    let input = fs::canonicalize(input)?;+    let parent = output+        .parent()+        .filter(|p| !p.as_os_str().is_empty())+        .unwrap_or_else(|| std::path::Path::new("."));+    let parent = fs::canonicalize(parent)?;+    let output = parent.join(output.file_name().ok_or("output must name a file")?);+    if fs::canonicalize(&output).ok().as_ref() == Some(&input) {+        return Err("output must not overwrite the raw addon".into());+    }+    // Requiring a distinct payload suffix also prevents accidentally replacing+    // a sibling `swc` executable or treating the payload as a loadable carrier.+    if output.extension().and_then(|s| s.to_str()) != Some("swcn") {+        return Err("output must have the .swcn payload extension".into());+    }+    if fs::metadata(&input)?.len() > MAX_SIZE {+        return Err("raw addon exceeds 2 GiB".into());+    }+    let raw = fs::read(&input)?;+    let bytes = pack(&raw, target)?;+    let payload = Payload::parse(&bytes)?;+    let mut decoded = RawVerifier::new(&raw);+    payload.decode_into(&mut decoded)?;+    if decoded.written != raw.len() {+        return Err("packer verification did not reproduce the raw addon".into());+    }+    let mut staged = tempfile::Builder::new()+        .prefix(".swc-payload-")+        .tempfile_in(&parent)?;+    staged.write_all(&bytes)?;+    staged.as_file().sync_all()?;+    let staged = staged.into_temp_path();+    platform::replace_file(&staged, &output)?;+    platform::sync_directory(&parent)?;+    println!(+        "raw={} payload={} sha512={}",+        raw.len(),+        bytes.len(),+        payload.header.cache_key()+    );+    Ok(())+}++fn main() {+    if let Err(error) = run() {+        eprintln!("swc-native-addon-pack: {error}");+        std::process::exit(1);+    }+}

deps/swc/tools/swc-native-addon-pack/src/verify.rs

new file mode 100644@@ -0,0 +1,130 @@+//! Host-side release verification. Decode before extracting or replacing an+//! artifact; replacement never falls back to copying over the raw addon.++mod carrier;++use std::{+    env, fs,+    io::Write,+    path::{Path, PathBuf},+};++use sha2::{Digest, Sha512};+use swc_native_addon::{+    format::{NativeTarget, HEADER_LEN, MAX_SIZE},+    platform,+};++fn output_path(path: &Path) -> carrier::Result<PathBuf> {+    if path.extension().and_then(|extension| extension.to_str()) != Some("node") {+        return Err("destination must be a .node file, never a CLI executable".into());+    }+    let parent = path+        .parent()+        .filter(|p| !p.as_os_str().is_empty())+        .unwrap_or(Path::new("."));+    Ok(fs::canonicalize(parent)?.join(path.file_name().ok_or("destination filename is required")?))+}++fn run() -> carrier::Result<()> {+    let mut args = env::args_os().skip(1);+    let mut input = None;+    let mut target = None;+    let mut raw = None;+    let mut extract = None;+    let mut replace = None;+    while let Some(arg) = args.next() {+        let destination = match arg.to_str() {+            Some("--input") => &mut input,+            Some("--target") => &mut target,+            Some("--raw") => &mut raw,+            Some("--extract") => &mut extract,+            Some("--replace") => &mut replace,+            _ => return Err("expected --input, --target, --raw, --extract, or --replace".into()),+        };+        if destination.is_some() {+            return Err("duplicate verifier argument".into());+        }+        *destination = Some(args.next().ok_or("missing verifier argument value")?);+    }+    let input = fs::canonicalize(input.ok_or("--input is required")?)?;+    let target = target.ok_or("--target is required")?;+    let target = NativeTarget::parse(target.to_str().ok_or("target must be UTF-8")?)?;+    if fs::metadata(&input)?.len() > MAX_SIZE {+        return Err("carrier exceeds 2 GiB".into());+    }+    let bytes = fs::read(&input)?;+    let payload = carrier::payload(&bytes, target)?;+    if bytes.len() as u64 >= payload.header.raw_len {+        return Err("carrier must be smaller than the stripped raw addon".into());+    }+    let mut decoded = tempfile::tempfile()?;+    payload.decode_into(&mut decoded)?;+    // The final artifact must agree with both its runtime BLAKE3 metadata and+    // the unchanged SHA-512 release identity in the v1 payload header.+    payload.header.verify(&mut decoded)?;+    payload.verify_target(&mut decoded)?;+    let raw = raw.map(fs::canonicalize).transpose()?;+    if let Some(path) = &raw {+        payload.header.verify(&mut fs::File::open(path)?)?;+    }+    if extract.is_some() && replace.is_some() {+        return Err("--extract and --replace are mutually exclusive".into());+    }+    if let Some(path) = extract {+        let path = output_path(Path::new(&path))?;+        if path.exists() {+            return Err("extraction destination already exists".into());+        }+        let mut stage = tempfile::NamedTempFile::new_in(path.parent().unwrap())?;+        use std::io::{Seek, SeekFrom};+        decoded.seek(SeekFrom::Start(0))?;+        std::io::copy(&mut decoded, &mut stage)?;+        stage.as_file().sync_all()?;+        stage.persist_noclobber(&path)?;+        platform::sync_directory(path.parent().unwrap())?;+    }+    if let Some(path) = replace {+        let path = output_path(Path::new(&path))?;+        if raw.as_ref() != Some(&fs::canonicalize(&path)?) || path == input {+            return Err(+                "--replace must name the verified --raw addon, distinct from the carrier".into(),+            );+        }+        let parent = path.parent().unwrap();+        let mut stage = tempfile::Builder::new()+            .prefix(".native-carrier-")+            .tempfile_in(parent)?;+        stage+            .as_file()+            .set_permissions(fs::metadata(&path)?.permissions())?;+        stage.write_all(&bytes)?;+        stage.as_file().sync_all()?;+        // Close all writable handles before MoveFileExW on Windows.+        let stage = stage.into_temp_path();+        platform::replace_file(&stage, &path)?;+        platform::sync_directory(parent)?;+    }+    println!(+        "{}",+        serde_json::json!({+            "kind": "carrier",+            "target": target.as_triple(),+            "rawSize": payload.header.raw_len,+            "rawSha512": payload.header.cache_key(),+            "compressedSize": payload.header.compressed_len,+            "payloadSize": HEADER_LEN as u64 + payload.header.compressed_len,+            "carrierSize": bytes.len(),+            "carrierSha512": format!("{:x}", Sha512::digest(&bytes)),+            "reduction": 1.0 - bytes.len() as f64 / payload.header.raw_len as f64,+        })+    );+    Ok(())+}++fn main() {+    if let Err(error) = run() {+        eprintln!("swc-native-addon-verify: {error}");+        std::process::exit(1);+    }+}