Separate out and simplify debug repr; reimplement with_friendly_error; fix JS copying

This commit is contained in:
Wilson Lin 2020-07-22 18:57:47 +10:00
parent 1847eca6b6
commit e9ea7bc378
4 changed files with 72 additions and 69 deletions

View File

@ -36,3 +36,49 @@ pub struct FriendlyError {
}
pub type ProcessingResult<T> = Result<T, ErrorType>;
#[inline(always)]
fn maybe_mark_indicator(line: &mut Vec<u8>, marker: u8, maybe_pos: isize, lower: usize, upper: usize) -> () {
let pos = maybe_pos as usize;
if maybe_pos > -1 && pos < upper {
let pos_in_line = pos - lower;
while line.len() <= pos_in_line {
line.push(b' ');
};
line.insert(pos_in_line, if line[pos_in_line] != b' ' { b'B' } else { marker });
};
}
// Pass -1 for read_pos or write_pos to prevent them from being represented.
pub fn debug_repr(code: &[u8], read_pos: isize, write_pos: isize) -> String {
let mut lines = Vec::<(isize, String)>::new();
let mut cur_pos = 0;
for (line_no, line) in code.split(|c| *c == b'\n').enumerate() {
// Include '\n'. Note that the last line might not have '\n' but that's OK for these calculations.
let len = line.len() + 1;
let line_as_string = unsafe { String::from_utf8_unchecked(line.to_vec()) };
lines.push((line_no as isize, line_as_string));
let new_pos = cur_pos + len;
// Rust does lazy allocation by default, so this is not wasteful.
let mut indicator_line = Vec::new();
maybe_mark_indicator(&mut indicator_line, b'R', read_pos, cur_pos, new_pos);
maybe_mark_indicator(&mut indicator_line, b'W', write_pos, cur_pos, new_pos);
if !indicator_line.is_empty() {
lines.push((-1, unsafe { String::from_utf8_unchecked(indicator_line) }));
};
cur_pos = new_pos;
};
let line_no_col_width = lines.len().to_string().len();
let mut res = String::new();
for (line_no, line) in lines {
res.push_str(&format!(
"{:>indent$}|{}\n",
if line_no == -1 { ">".repeat(line_no_col_width) } else { line_no.to_string() },
line,
indent = line_no_col_width,
));
};
res
}

View File

@ -3,6 +3,7 @@ use crate::proc::Processor;
use crate::unit::content::process_content;
use crate::spec::tag::ns::Namespace;
pub use crate::cfg::Cfg;
use crate::err::debug_repr;
mod cfg;
mod err;
@ -49,3 +50,11 @@ pub fn copy(code: &[u8], cfg: &Cfg) -> Result<Vec<u8>, Error> {
Err(e) => Err(e),
}
}
pub fn with_friendly_error(code: &mut [u8], cfg: &Cfg) -> Result<usize, FriendlyError> {
in_place(code, cfg).map_err(|err| FriendlyError {
position: err.position,
message: err.error_type.message(),
code_context: debug_repr(code, err.position as isize, -1),
})
}

View File

@ -2,12 +2,12 @@ use core::fmt;
use std::fmt::{Debug, Formatter};
use std::ops::{Index, IndexMut};
use crate::err::{Error, ErrorType, ProcessingResult};
use crate::err::{Error, ErrorType, ProcessingResult, debug_repr};
use crate::proc::MatchAction::*;
use crate::proc::MatchMode::*;
use crate::proc::range::ProcessorRange;
use memchr::memchr;
use crate::gen::codepoints::{WHITESPACE, Lookup};
use crate::gen::codepoints::Lookup;
#[cfg(feature = "js-esbuild")]
use std::sync::{Arc, Mutex};
#[cfg(feature = "js-esbuild")]
@ -337,14 +337,17 @@ impl<'d> Processor<'d> {
let mut write_next = results.get(0).map_or(self.write_next, |r| r.src.start);
for (i, JsMinSection { result, src }) in results.iter().enumerate() {
// Resulting minified JS to write.
let mut js = result.js.trim().as_bytes();
// If minified result is actually longer than source, then write source instead.
// NOTE: We still need to write source as previous iterations may have shifted code down.
if js.len() >= src.len() {
js = &self.code[src.start..src.end];
let min_js = result.js.trim().as_bytes();
let js_len = if min_js.len() < src.len() {
self.code[write_next..write_next + min_js.len()].copy_from_slice(min_js);
min_js.len()
} else {
// If minified result is actually longer than source, then write source instead.
// NOTE: We still need to write source as previous iterations may have shifted code down.
self.code.copy_within(src.start..src.end, write_next);
src.len()
};
let write_end = write_next + js.len();
self.code[write_next..write_end].copy_from_slice(js);
let write_end = write_next + js_len;
let next_start = results.get(i + 1).map_or(self.write_next, |r| r.src.start);
self.code.copy_within(src.end..next_start, write_end);
write_next = write_end + (next_start - src.end);
@ -355,66 +358,7 @@ impl<'d> Processor<'d> {
impl Debug for Processor<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let mut lines = vec![(1, String::new())];
let mut line_idx = 0;
let mut indicator_line_idx_opt: Option<usize> = None;
let mut line_cols = 0;
let mut line_no = 1;
for (i, &c) in self.code.iter().enumerate() {
if i == self.read_next || i == self.write_next {
let indicator_line_idx = if indicator_line_idx_opt.is_none() {
let indicator_line_idx = lines.len();
lines.push((-1, String::new()));
indicator_line_idx_opt = Some(indicator_line_idx);
indicator_line_idx
} else if let Some(indicator_line_idx) = indicator_line_idx_opt {
indicator_line_idx
} else {
unreachable!();
};
// At this point, `line_cols` is how many characters are on this line BEFORE this character.
while line_cols > 0 && lines[indicator_line_idx].1.len() < line_cols {
lines[indicator_line_idx].1.push(' ');
};
lines[indicator_line_idx].1.push(if i == self.read_next && i == self.write_next {
'B'
} else if i == self.read_next {
'R'
} else {
'W'
})
};
match c {
b'\n' => {
lines[line_idx].1.push_str("");
line_no += 1;
line_cols = 0;
line_idx = lines.len();
lines.push((line_no, String::new()));
indicator_line_idx_opt = None;
}
c => {
match c {
c if WHITESPACE[c] => lines[line_idx].1.push('·'),
c if c >= b'!' && c <= b'~' => lines[line_idx].1.push(c as char),
_ => lines[line_idx].1.push('<27>'),
};
line_cols += 1;
}
};
};
let max_line_no_width = (line_no as f64).log10().ceil() as usize;
// Don't use for_each as otherwise we can't return errors.
for l in lines
.iter()
.map(|(line_no, line)| if *line_no == -1 {
format!("{:>indent$}|{}\n", String::from_utf8(vec![b'>'; max_line_no_width]).unwrap(), line, indent = max_line_no_width)
} else {
format!("{:>indent$}|{}\n", line_no, line, indent = max_line_no_width)
})
{
f.write_str(l.as_str())?;
}
f.write_str(&debug_repr(self.code, self.read_next as isize, self.write_next as isize))?;
Ok(())
}
}

View File

@ -291,4 +291,8 @@ fn test_processing_instructions() {
#[test]
fn test_js_minification() {
eval_with_js_min(b"<script>let a = 1;</script>", b"<script>let a=1;</script>");
eval_with_js_min(br#"
<script>let a = 1;</script>
<script>let b = 2;</script>
"#, b"<script>let a=1;</script><script>let b=2;</script>");
}