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
@@ -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
@@ -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
@@ -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
@@ -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);+ }+}