Skip to content

Commit 884d677

Browse files
authored
Add end-user critique experiment suite (#135)
1 parent ea0e70e commit 884d677

50 files changed

Lines changed: 7322 additions & 5808 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/chidori-js/src/bytecode.rs

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,49 @@ pub enum Const {
2727
BigInt(Rc<str>),
2828
}
2929

30+
/// A compilation's source text plus its line-start index, shared (one `Rc`)
31+
/// by every `FuncProto` the compilation produced. Exists so stack frames can
32+
/// resolve a bytecode position table entry (a byte offset — see
33+
/// [`FuncProto::pos`]) into a 1-based line/column *lazily, on the error path
34+
/// only*: the happy path pays one `Rc` per proto instead of an eager per-op
35+
/// line/column resolution at compile time.
36+
#[derive(Debug)]
37+
pub struct SourceInfo {
38+
text: Rc<str>,
39+
/// Byte offset of each line start in `text` (`[0]` is always 0).
40+
line_starts: Box<[u32]>,
41+
}
42+
43+
impl SourceInfo {
44+
pub fn new(text: Rc<str>) -> SourceInfo {
45+
let mut line_starts = vec![0u32];
46+
for (i, b) in text.bytes().enumerate() {
47+
if b == b'\n' {
48+
line_starts.push(i as u32 + 1);
49+
}
50+
}
51+
SourceInfo {
52+
text,
53+
line_starts: line_starts.into_boxed_slice(),
54+
}
55+
}
56+
57+
/// 1-based (line, column) of byte `offset` in the source. Columns count
58+
/// characters, not bytes, matching the parser's diagnostics.
59+
pub fn line_col_of(&self, offset: u32) -> (u32, u32) {
60+
let offset = offset.min(self.text.len() as u32);
61+
let line = self.line_starts.partition_point(|&s| s <= offset).max(1);
62+
let start = self.line_starts[line - 1] as usize;
63+
let col = self
64+
.text
65+
.get(start..offset as usize)
66+
.map(|s| s.chars().count())
67+
.unwrap_or(0) as u32
68+
+ 1;
69+
(line as u32, col)
70+
}
71+
}
72+
3073
/// How a free variable referenced by a nested function is captured at closure
3174
/// creation time.
3275
#[derive(Clone, Copy, Debug)]
@@ -135,11 +178,24 @@ pub struct FuncProto {
135178
pub kind: FuncKind,
136179
/// Source span for stack traces (start byte offset).
137180
pub source_start: u32,
138-
/// 1-based line/column of the function's definition site in its source,
139-
/// rendered in error stack traces (`at name (line:col)`). `0` = unknown
140-
/// (synthetic protos, sources compiled without position tracking).
181+
/// 1-based line/column of the function's definition site in its source.
182+
/// `0` = unknown (synthetic protos, sources compiled without position
183+
/// tracking). Stack frames prefer the per-op position (`pos` at the
184+
/// frame's current ip); this is the fallback when no source is attached.
141185
pub source_line: u32,
142186
pub source_col: u32,
187+
/// Per-op source position table, index-parallel to `code`: the byte
188+
/// offset (into `source_info`'s text) of the statement or call the op was
189+
/// emitted for. Lets a stack frame report where the frame *is* — the
190+
/// throwing statement for the innermost frame, the call site for outer
191+
/// frames — instead of where its function was declared. Maintained by
192+
/// every code-shortening pass (fusion remaps it alongside the ops);
193+
/// resolved to line/column only on the error path.
194+
pub pos: Box<[u32]>,
195+
/// The compilation's shared source text + line index used to resolve
196+
/// `pos` entries. `None` for synthetic protos and sources compiled
197+
/// without text, which then fall back to the definition site.
198+
pub source_info: Option<Rc<SourceInfo>>,
143199
/// Which source this function came from — the module key/path supplied to
144200
/// [`crate::compiler::compile_module_labeled`] — rendered in stack frames
145201
/// as `at name (label:line:col)` so an embedder can resolve the frame back
@@ -257,6 +313,12 @@ pub struct IcEntry {
257313
}
258314

259315
impl FuncProto {
316+
/// Source position (byte offset) recorded for the op at `ip`, or `None`
317+
/// when this proto carries no position table (synthetic protos).
318+
pub fn pos_at(&self, ip: usize) -> Option<u32> {
319+
self.pos.get(ip).copied()
320+
}
321+
260322
pub fn empty(name: &str, kind: FuncKind) -> FuncProto {
261323
FuncProto {
262324
name: name.to_string(),
@@ -274,6 +336,8 @@ impl FuncProto {
274336
source_start: 0,
275337
source_line: 0,
276338
source_col: 0,
339+
pos: Box::new([]),
340+
source_info: None,
277341
source_label: None,
278342
uses_arguments: false,
279343
param_names: Vec::new(),

0 commit comments

Comments
 (0)