use crate::decoder::{decode, decode_regular, decode_slice};
use crate::encoder::{encode, Encodable};
use crate::errors::{Error, Result};
use crate::jsontypes::{FacebookScopeMapping, FacebookSources, RawSourceMap};
use crate::types::{DecodedMap, RewriteOptions, SourceMap};
use crate::utils::greatest_lower_bound;
use crate::vlq::parse_vlq_segment_into;
use crate::Token;
use std::io::{Read, Write};
use std::ops::{Deref, DerefMut};
#[derive(Debug, Clone)]
pub struct HermesScopeOffset {
line: u32,
column: u32,
name_index: u32,
}
#[derive(Debug, Clone)]
pub struct HermesFunctionMap {
names: Vec<String>,
mappings: Vec<HermesScopeOffset>,
}
#[derive(Debug, Clone)]
pub struct SourceMapHermes {
pub(crate) sm: SourceMap,
function_maps: Vec<Option<HermesFunctionMap>>,
raw_facebook_sources: FacebookSources,
}
impl Deref for SourceMapHermes {
type Target = SourceMap;
fn deref(&self) -> &Self::Target {
&self.sm
}
}
impl DerefMut for SourceMapHermes {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.sm
}
}
impl Encodable for SourceMapHermes {
fn as_raw_sourcemap(&self) -> RawSourceMap {
let mut rsm = self.sm.as_raw_sourcemap();
rsm.x_facebook_sources = self.raw_facebook_sources.clone();
rsm
}
}
impl SourceMapHermes {
pub fn from_reader<R: Read>(rdr: R) -> Result<Self> {
match decode(rdr)? {
DecodedMap::Hermes(sm) => Ok(sm),
_ => Err(Error::IncompatibleSourceMap),
}
}
pub fn from_slice(slice: &[u8]) -> Result<Self> {
match decode_slice(slice)? {
DecodedMap::Hermes(sm) => Ok(sm),
_ => Err(Error::IncompatibleSourceMap),
}
}
pub fn to_writer<W: Write>(&self, w: W) -> Result<()> {
encode(self, w)
}
pub fn get_original_function_name(&self, bytecode_offset: u32) -> Option<&str> {
let token = self.sm.lookup_token(0, bytecode_offset)?;
self.get_scope_for_token(token)
}
pub fn get_scope_for_token(&self, token: Token) -> Option<&str> {
let function_map = self
.function_maps
.get(token.get_src_id() as usize)?
.as_ref()?;
let mapping = greatest_lower_bound(&function_map.mappings, &token.get_src(), |o| {
(o.line, o.column)
})?;
function_map
.names
.get(mapping.name_index as usize)
.map(|n| n.as_str())
}
pub fn rewrite(self, options: &RewriteOptions<'_>) -> Result<Self> {
let Self {
sm,
mut function_maps,
mut raw_facebook_sources,
} = self;
let (sm, mapping) = sm.rewrite_with_mapping(options)?;
if function_maps.len() >= mapping.len() {
function_maps = mapping
.iter()
.map(|idx| function_maps[*idx as usize].take())
.collect();
raw_facebook_sources = raw_facebook_sources.map(|mut sources| {
mapping
.into_iter()
.map(|idx| sources[idx as usize].take())
.collect()
});
}
Ok(Self {
sm,
function_maps,
raw_facebook_sources,
})
}
}
pub fn decode_hermes(mut rsm: RawSourceMap) -> Result<SourceMapHermes> {
let x_facebook_sources = rsm
.x_facebook_sources
.take()
.ok_or(Error::IncompatibleSourceMap)?;
let mut nums = Vec::with_capacity(4);
let function_maps = x_facebook_sources
.iter()
.map(|v| {
let FacebookScopeMapping {
names,
mappings: raw_mappings,
} = v.as_ref()?.iter().next()?;
let mut mappings = vec![];
let mut line = 1;
let mut name_index = 0;
for line_mapping in raw_mappings.split(';') {
if line_mapping.is_empty() {
continue;
}
let mut column = 0;
for mapping in line_mapping.split(',') {
if mapping.is_empty() {
continue;
}
nums.clear();
parse_vlq_segment_into(mapping, &mut nums).ok()?;
let mut nums = nums.iter().copied();
column = (i64::from(column) + nums.next()?) as u32;
name_index = (i64::from(name_index) + nums.next().unwrap_or(0)) as u32;
line = (i64::from(line) + nums.next().unwrap_or(0)) as u32;
mappings.push(HermesScopeOffset {
column,
line,
name_index,
});
}
}
Some(HermesFunctionMap {
names: names.clone(),
mappings,
})
})
.collect();
let sm = decode_regular(rsm)?;
Ok(SourceMapHermes {
sm,
function_maps,
raw_facebook_sources: Some(x_facebook_sources),
})
}