Skip to content
Open
416 changes: 187 additions & 229 deletions src/exe_format/elf.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/exe_format/macho.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ impl MachoFile {
segname: SEGNAME_BUN,
addr: original_vmaddr,
size: total_size,
offset: u32::try_from(original_fileoff).expect("int cast"),
offset: sect.offset,
align: (blob_alignment as f64).log2() as u32,
reloff: 0,
nreloc: 0,
Expand Down
84 changes: 32 additions & 52 deletions src/parsers/json5.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use bun_core::StackCheck;
// `is_identifier_start/_part` landed in `bun_core::lexer`; route through there.
use bun_alloc::{ArenaVec as BumpVec, ArenaVecExt as _};
use bun_ast::{E, Expr, G};
use bun_ast::{Loc, Log, Source};
use bun_ast::{Loc, Log, Source, usize2loc};
use bun_core::lexer as identifier;
use bun_core::strings;

Expand Down Expand Up @@ -92,6 +92,7 @@ pub enum ParseError {
ExpectedClosingBracket,
InvalidIdentifier,
TrailingData,
DocumentTooLarge,
StackOverflow,
}

Expand Down Expand Up @@ -123,6 +124,7 @@ pub enum Error {
ExpectedClosingBracket { pos: usize },
InvalidIdentifier { pos: usize },
TrailingData { pos: usize },
DocumentTooLarge,
}

#[derive(Copy, Clone, PartialEq, Eq, strum::IntoStaticStr, Debug)]
Expand Down Expand Up @@ -165,9 +167,8 @@ impl Error {
| Error::ExpectedClosingBrace { pos }
| Error::ExpectedClosingBracket { pos }
| Error::InvalidIdentifier { pos }
| Error::TrailingData { pos } => Loc {
start: i32::try_from(pos).expect("int cast"),
},
| Error::TrailingData { pos } => usize2loc(pos),
Error::DocumentTooLarge => Loc { start: 0 },
};
let msg: &'static [u8] = match *self {
Error::Oom | Error::StackOverflow => unreachable!(),
Expand All @@ -191,6 +192,7 @@ impl Error {
Error::ExpectedClosingBracket { .. } => b"Expected ']'",
Error::InvalidIdentifier { .. } => b"Invalid identifier start character",
Error::TrailingData { .. } => b"Unexpected token after JSON5 value",
Error::DocumentTooLarge => b"JSON5 document is too large to parse (2 GiB maximum)",
};
log.add_error(Some(source), loc, msg);
Ok(())
Expand Down Expand Up @@ -222,6 +224,7 @@ impl<'a> JSON5Parser<'a> {
match err {
ParseError::OutOfMemory => Error::Oom,
ParseError::StackOverflow => Error::StackOverflow,
ParseError::DocumentTooLarge => Error::DocumentTooLarge,
// Scanner errors use scan position
ParseError::UnexpectedCharacter => Error::UnexpectedCharacter { pos: scan_pos },
ParseError::UnterminatedString => Error::UnterminatedString { pos: scan_pos },
Expand Down Expand Up @@ -288,13 +291,16 @@ impl<'a> JSON5Parser<'a> {
0
}

/// `parse_root` has rejected any document whose positions do not fit a `Loc`.
fn start_token(&mut self) {
self.token.loc = usize2loc(self.pos);
}

fn scan(&mut self) -> Result<(), ParseError> {
self.token.data = 'next: loop {
match self.peek() {
0 => {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
break 'next TokenData::Eof;
}
// Whitespace — skip without setting loc
Expand All @@ -304,73 +310,53 @@ impl<'a> JSON5Parser<'a> {
}
// Structural
b'{' => {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
self.pos += 1;
break 'next TokenData::LeftBrace;
}
b'}' => {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
self.pos += 1;
break 'next TokenData::RightBrace;
}
b'[' => {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
self.pos += 1;
break 'next TokenData::LeftBracket;
}
b']' => {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
self.pos += 1;
break 'next TokenData::RightBracket;
}
b':' => {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
self.pos += 1;
break 'next TokenData::Colon;
}
b',' => {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
self.pos += 1;
break 'next TokenData::Comma;
}
b'+' => {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
self.pos += 1;
break 'next TokenData::Number(self.scan_signed_value(false)?);
}
b'-' => {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
self.pos += 1;
break 'next TokenData::Number(self.scan_signed_value(true)?);
}
// Strings
b'"' | b'\'' => {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
break 'next TokenData::String(self.scan_string()?);
}
// Numbers
b'0'..=b'9' | b'.' => {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
break 'next TokenData::Number(self.scan_number()?);
}
// Comments — skip without setting loc
Expand All @@ -393,27 +379,21 @@ impl<'a> JSON5Parser<'a> {
}
c => {
if c == b't' {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
break 'next if self.scan_keyword(b"true") {
TokenData::Boolean(true)
} else {
TokenData::Identifier(self.scan_identifier()?)
};
} else if c == b'f' {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
break 'next if self.scan_keyword(b"false") {
TokenData::Boolean(false)
} else {
TokenData::Identifier(self.scan_identifier()?)
};
} else if c == b'n' {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
break 'next if self.scan_keyword(b"null") {
TokenData::Null
} else {
Expand All @@ -425,9 +405,7 @@ impl<'a> JSON5Parser<'a> {
|| c == b'$'
|| c == b'\\'
{
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
break 'next TokenData::Identifier(self.scan_identifier()?);
} else if c >= 0x80 {
// Multi-byte: check whitespace first, then identifier
Expand All @@ -436,9 +414,7 @@ impl<'a> JSON5Parser<'a> {
self.pos += usize::from(mb);
continue 'next;
}
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
let Some(cp) = self.read_codepoint() else {
return Err(ParseError::UnexpectedCharacter);
};
Expand Down Expand Up @@ -505,6 +481,10 @@ impl<'a> JSON5Parser<'a> {
// ── Parser ──

fn parse_root(&mut self) -> Result<Expr, ParseError> {
// Positions are `i32` `Loc`s, including the EOF token's at `source.len()`.
if self.source.len() > i32::MAX as usize {
return Err(ParseError::DocumentTooLarge);
}
self.scan()?;
let result = self.parse_value()?;
if !matches!(self.token.data, TokenData::Eof) {
Expand Down
32 changes: 13 additions & 19 deletions src/runtime/image/Image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,8 @@ pub struct Image {
/// Apply EXIF Orientation (JPEG) before any user ops, the way Sharp's
/// `.rotate()`-with-no-args / `autoOrient` does.
auto_orient: bool,
/// Populated after a pipeline has run once; lets `.width`/`.height` answer
/// synchronously after the first await.
last_width: Cell<i32>,
last_height: Cell<i32>,
/// Set by the first awaited terminal; `.width`/`.height` answer -1 until then.
last_size: Cell<Option<(u32, u32)>>,
/// Strong while at least one PipelineTask is in flight, weak otherwise. The
/// Strong→wrapper→sourceJS-slot chain is what keeps the borrowed ArrayBuffer
/// alive across the WorkPool roundtrip; switching to weak when idle lets GC
Expand All @@ -86,8 +84,7 @@ impl Default for Image {
pipeline: Cell::new(Pipeline::default()),
max_pixels: codecs::DEFAULT_MAX_PIXELS,
auto_orient: true,
last_width: Cell::new(-1),
last_height: Cell::new(-1),
last_size: Cell::new(None),
this_ref: JsCell::new(JsRef::empty()),
pending_tasks: Cell::new(0),
}
Expand Down Expand Up @@ -501,13 +498,13 @@ impl Image {
// coerce_int for the same NaN/Inf/huge-finite reasons as everywhere else;
// ±1e15 is plenty of headroom for "any multiple of 90 a user might pass".
let raw: i64 = coerce_int!(i64, args[0].as_number(), -1e15, 1e15);
let deg: u32 = u32::try_from(raw.rem_euclid(360)).unwrap();
let deg = raw.rem_euclid(360) as u16;
if deg != 0 && deg != 90 && deg != 180 && deg != 270 {
return Err(global.throw_invalid_arguments(format_args!(
"rotate: only multiples of 90 are supported"
)));
}
self.update_pipeline(|p| p.rotate = u16::try_from(deg).expect("int cast"));
self.update_pipeline(|p| p.rotate = deg);
Ok(callframe.this())
}

Expand Down Expand Up @@ -916,12 +913,12 @@ impl Image {
impl Image {
#[bun_jsc::host_fn(getter)]
pub(crate) fn get_width(&self, _: &JSGlobalObject) -> JSValue {
JSValue::js_number(f64::from(self.last_width.get()))
JSValue::js_number(self.last_size.get().map_or(-1.0, |(w, _)| f64::from(w)))
}

#[bun_jsc::host_fn(getter)]
pub(crate) fn get_height(&self, _: &JSGlobalObject) -> JSValue {
JSValue::js_number(f64::from(self.last_height.get()))
JSValue::js_number(self.last_size.get().map_or(-1.0, |(_, h)| f64::from(h)))
}
}

Expand All @@ -948,8 +945,7 @@ impl Image {
mem::swap(&mut w, &mut h);
}
}
self.last_width.set(i32::try_from(w).expect("int cast"));
self.last_height.set(i32::try_from(h).expect("int cast"));
self.last_size.set(Some((w, h)));
let obj = JSValue::create_empty_object(global, 3);
obj.put(global, b"width", JSValue::js_number(f64::from(w)));
obj.put(global, b"height", JSValue::js_number(f64::from(h)));
Expand Down Expand Up @@ -1226,8 +1222,7 @@ impl Image {
);
match result {
TaskResult::Encoded { out, format, w, h } => {
self.last_width.set(i32::try_from(w).expect("int cast"));
self.last_height.set(i32::try_from(h).expect("int cast"));
self.last_size.set(Some((w, h)));
Ok((out, format.mime()))
}
TaskResult::Err(e) => Err(global.throw(format_args!(
Expand Down Expand Up @@ -1589,7 +1584,7 @@ impl PipelineTask {
});
return;
}
if u64::try_from(st.st_size.max(0)).expect("int cast") > MAX_INPUT_FILE_BYTES {
if u64::try_from(st.st_size).unwrap_or(0) > MAX_INPUT_FILE_BYTES {
self.result = TaskResult::Err(codecs::Error::TooManyPixels);
return;
}
Expand Down Expand Up @@ -1763,8 +1758,7 @@ impl PipelineTask {
// so writing `image.*` there would race the synchronous getters.
match &self.result {
TaskResult::Encoded { w, h, .. } | TaskResult::Meta { w, h, .. } => {
image.last_width.set(i32::try_from(*w).expect("int cast"));
image.last_height.set(i32::try_from(*h).expect("int cast"));
image.last_size.set(Some((*w, *h)));
}
_ => {}
}
Expand Down Expand Up @@ -1945,7 +1939,7 @@ impl PipelineTask {
fn apply_pipeline(&self, d: &mut codecs::Decoded) -> Result<(), codecs::Error> {
let p = &self.pipeline;
if p.rotate != 0 {
let next = codecs::rotate(&d.rgba, d.width, d.height, u32::from(p.rotate))?;
let next = codecs::rotate(&d.rgba, d.width, d.height, p.rotate)?;
// Assignment drops
// the old `Vec<u8>`/owned buffer.
d.rgba = next.rgba;
Expand Down Expand Up @@ -2071,7 +2065,7 @@ fn apply_orientation(
if t.rotate != 0 {
// Swap pixel slots only — `next` carries no ICC profile, and the
// one on `d` (set by decode) must survive EXIF auto-orient.
let next = codecs::rotate(&d.rgba, d.width, d.height, u32::from(t.rotate))?;
let next = codecs::rotate(&d.rgba, d.width, d.height, t.rotate)?;
d.rgba = next.rgba;
d.width = next.width;
d.height = next.height;
Expand Down
Loading
Loading