LibJS: Deduplicate adjacent source map entries during codegen
Avoid emitting consecutive source map entries when they carry the same source range. The bytecode offset for the previous entry remains valid for later PCs because source lookup now uses the largest source map entry whose offset is not greater than the program counter. This keeps stack traces stable while allowing statement-sized runs of bytecode to share one source map entry.
This commit is contained in:
parent
e926e86f8d
commit
b6ac36c200
3 changed files with 80 additions and 48 deletions
|
|
@ -405,19 +405,24 @@ Optional<SourceRange> Executable::source_range_at(size_t offset) const
|
|||
{
|
||||
if (offset >= bytecode.size())
|
||||
return {};
|
||||
auto* entry = binary_search(source_map, offset, nullptr, [](size_t needle, SourceMapEntry const& entry) -> int {
|
||||
if (needle < entry.bytecode_offset)
|
||||
return -1;
|
||||
if (needle > entry.bytecode_offset)
|
||||
return 1;
|
||||
return 0;
|
||||
});
|
||||
if (!entry)
|
||||
if (source_map.is_empty())
|
||||
return {};
|
||||
size_t low = 0;
|
||||
size_t high = source_map.size();
|
||||
while (low < high) {
|
||||
auto middle = low + (high - low) / 2;
|
||||
if (source_map[middle].bytecode_offset <= offset)
|
||||
low = middle + 1;
|
||||
else
|
||||
high = middle;
|
||||
}
|
||||
if (low == 0)
|
||||
return {};
|
||||
auto& entry = source_map[low - 1];
|
||||
return SourceRange {
|
||||
.code = source_code,
|
||||
.start = entry->source_record.start,
|
||||
.end = entry->source_record.end,
|
||||
.start = entry.source_record.start,
|
||||
.end = entry.source_record.end,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -759,7 +759,7 @@ impl Utf16String {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct Position {
|
||||
pub line: u32,
|
||||
pub column: u32,
|
||||
|
|
|
|||
|
|
@ -1645,6 +1645,15 @@ impl Generator {
|
|||
let mut bytecode: Vec<u8> = Vec::with_capacity(offset);
|
||||
let mut source_map: Vec<SourceMapEntry> = Vec::new();
|
||||
let mut exception_handlers: Vec<ExceptionHandler> = Vec::new();
|
||||
fn push_source_map_entry(source_map: &mut Vec<SourceMapEntry>, entry: SourceMapEntry) {
|
||||
let should_push = source_map.last().is_none_or(|previous| {
|
||||
previous.source_start != entry.source_start || previous.source_end != entry.source_end
|
||||
});
|
||||
if should_push {
|
||||
source_map.push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// Track which blocks actually produced instructions.
|
||||
let mut basic_block_start_offsets: Vec<usize> = Vec::with_capacity(num_blocks);
|
||||
|
||||
|
|
@ -1666,30 +1675,39 @@ impl Generator {
|
|||
}
|
||||
InstAction::Emit => {
|
||||
let instruction_offset = bytecode.len();
|
||||
source_map.push(SourceMapEntry {
|
||||
bytecode_offset: u32_from_usize(instruction_offset),
|
||||
source_start: sm.source_start,
|
||||
source_end: sm.source_end,
|
||||
});
|
||||
push_source_map_entry(
|
||||
&mut source_map,
|
||||
SourceMapEntry {
|
||||
bytecode_offset: u32_from_usize(instruction_offset),
|
||||
source_start: sm.source_start,
|
||||
source_end: sm.source_end,
|
||||
},
|
||||
);
|
||||
instruction.encode(self.strict, &mut bytecode);
|
||||
}
|
||||
InstAction::JumpToReturn(value) => {
|
||||
let instruction_offset = bytecode.len();
|
||||
source_map.push(SourceMapEntry {
|
||||
bytecode_offset: u32_from_usize(instruction_offset),
|
||||
source_start: sm.source_start,
|
||||
source_end: sm.source_end,
|
||||
});
|
||||
push_source_map_entry(
|
||||
&mut source_map,
|
||||
SourceMapEntry {
|
||||
bytecode_offset: u32_from_usize(instruction_offset),
|
||||
source_start: sm.source_start,
|
||||
source_end: sm.source_end,
|
||||
},
|
||||
);
|
||||
let replacement = Instruction::Return { value };
|
||||
replacement.encode(self.strict, &mut bytecode);
|
||||
}
|
||||
InstAction::JumpToEnd(value) => {
|
||||
let instruction_offset = bytecode.len();
|
||||
source_map.push(SourceMapEntry {
|
||||
bytecode_offset: u32_from_usize(instruction_offset),
|
||||
source_start: sm.source_start,
|
||||
source_end: sm.source_end,
|
||||
});
|
||||
push_source_map_entry(
|
||||
&mut source_map,
|
||||
SourceMapEntry {
|
||||
bytecode_offset: u32_from_usize(instruction_offset),
|
||||
source_start: sm.source_start,
|
||||
source_end: sm.source_end,
|
||||
},
|
||||
);
|
||||
let replacement = Instruction::End { value };
|
||||
replacement.encode(self.strict, &mut bytecode);
|
||||
}
|
||||
|
|
@ -1698,11 +1716,14 @@ impl Generator {
|
|||
let target_block = target.0 as usize;
|
||||
target.0 = u32_from_usize(block_offsets[target_block]);
|
||||
let instruction_offset = bytecode.len();
|
||||
source_map.push(SourceMapEntry {
|
||||
bytecode_offset: u32_from_usize(instruction_offset),
|
||||
source_start: sm.source_start,
|
||||
source_end: sm.source_end,
|
||||
});
|
||||
push_source_map_entry(
|
||||
&mut source_map,
|
||||
SourceMapEntry {
|
||||
bytecode_offset: u32_from_usize(instruction_offset),
|
||||
source_start: sm.source_start,
|
||||
source_end: sm.source_end,
|
||||
},
|
||||
);
|
||||
let replacement = Instruction::JumpFalse { condition, target };
|
||||
replacement.encode(self.strict, &mut bytecode);
|
||||
}
|
||||
|
|
@ -1710,11 +1731,14 @@ impl Generator {
|
|||
let target_block = target.0 as usize;
|
||||
target.0 = u32_from_usize(block_offsets[target_block]);
|
||||
let instruction_offset = bytecode.len();
|
||||
source_map.push(SourceMapEntry {
|
||||
bytecode_offset: u32_from_usize(instruction_offset),
|
||||
source_start: sm.source_start,
|
||||
source_end: sm.source_end,
|
||||
});
|
||||
push_source_map_entry(
|
||||
&mut source_map,
|
||||
SourceMapEntry {
|
||||
bytecode_offset: u32_from_usize(instruction_offset),
|
||||
source_start: sm.source_start,
|
||||
source_end: sm.source_end,
|
||||
},
|
||||
);
|
||||
let replacement = Instruction::JumpTrue { condition, target };
|
||||
replacement.encode(self.strict, &mut bytecode);
|
||||
}
|
||||
|
|
@ -1727,19 +1751,22 @@ impl Generator {
|
|||
undef_rewritten.offset_index_by(number_of_registers + number_of_locals);
|
||||
let end_instruction = Instruction::End { value: undef_rewritten };
|
||||
let instruction_offset = bytecode.len();
|
||||
source_map.push(SourceMapEntry {
|
||||
bytecode_offset: u32_from_usize(instruction_offset),
|
||||
source_start: Position {
|
||||
line: 0,
|
||||
column: 0,
|
||||
offset: 0,
|
||||
push_source_map_entry(
|
||||
&mut source_map,
|
||||
SourceMapEntry {
|
||||
bytecode_offset: u32_from_usize(instruction_offset),
|
||||
source_start: Position {
|
||||
line: 0,
|
||||
column: 0,
|
||||
offset: 0,
|
||||
},
|
||||
source_end: Position {
|
||||
line: 0,
|
||||
column: 0,
|
||||
offset: 0,
|
||||
},
|
||||
},
|
||||
source_end: Position {
|
||||
line: 0,
|
||||
column: 0,
|
||||
offset: 0,
|
||||
},
|
||||
});
|
||||
);
|
||||
end_instruction.encode(self.strict, &mut bytecode);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue