-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
280 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
use super::source_map::SourceMap; | ||
use crate::common_ffi::*; | ||
|
||
#[no_mangle] | ||
pub extern "C" fn source_map_new() -> *mut SourceMap { | ||
ptr_new(SourceMap::new()) | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn source_map_free(map: *mut SourceMap) { | ||
ptr_free(map) | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn source_map_clear(map: *mut SourceMap) -> bool { | ||
boolclosure!({ | ||
let map = map.as_mut()?; | ||
(*map).clear(); | ||
Some(()) | ||
}) | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn source_map_add( | ||
map: *mut SourceMap, | ||
path: PChar, | ||
line: u32, | ||
offset: u32, | ||
) -> bool { | ||
boolclosure!({ | ||
let path = pchar_to_str(path)?; | ||
(*map).add(path, line, offset); | ||
Some(()) | ||
}) | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn source_map_get_offset( | ||
map: *mut SourceMap, | ||
path: PChar, | ||
line: u32, | ||
out: *mut u32, | ||
) -> bool { | ||
boolclosure!({ | ||
let path = pchar_to_str(path)?; | ||
*out = (*map).get_offset(path, line)?; | ||
Some(()) | ||
}) | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn source_map_adjust_offset_by(map: *mut SourceMap, delta: u32) { | ||
(*map).adjust_offset_by(delta); | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn source_map_dump(map: *mut SourceMap, path: PChar) -> bool { | ||
boolclosure!({ | ||
let path = pchar_to_str(path)?; | ||
let mut file = std::fs::File::create(path).ok()?; | ||
let map = map.as_ref()?; | ||
serde_json::to_writer(&mut file, map).ok()?; | ||
Some(()) | ||
}) | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn source_map_new_local_variable_scope( | ||
map: *mut SourceMap, | ||
file_name: PChar, | ||
line: u32, | ||
var_name: PChar, | ||
var_index: i32, | ||
) -> bool { | ||
boolclosure!({ | ||
let file_name = pchar_to_str(file_name)?; | ||
let name = pchar_to_str(var_name)?; | ||
(*map).new_local_variable_scope(file_name, line, name, var_index); | ||
Some(()) | ||
}) | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn source_map_find_local_variable_index( | ||
map: *mut SourceMap, | ||
file_name: PChar, | ||
line: u32, | ||
var_name: PChar, | ||
out: *mut i32, | ||
) -> bool { | ||
boolclosure!({ | ||
let file_name = pchar_to_str(file_name)?; | ||
let name = pchar_to_str(var_name)?; | ||
*out = (*map).find_local_variable_index(file_name, line, name)?; | ||
Some(()) | ||
}) | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn source_map_new_global_variable( | ||
map: *mut SourceMap, | ||
name: PChar, | ||
var_index: i32, | ||
) -> bool { | ||
boolclosure!({ | ||
let name = pchar_to_str(name)?; | ||
(*map).new_global_variable(name, var_index); | ||
Some(()) | ||
}) | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn source_map_find_global_variable_index( | ||
map: *mut SourceMap, | ||
name: PChar, | ||
out: *mut i32, | ||
) -> bool { | ||
boolclosure!({ | ||
let name = pchar_to_str(name)?; | ||
*out = (*map).find_global_variable_index(name)?; | ||
Some(()) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
pub mod ffi; | ||
pub mod source_map; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
use std::collections::HashMap; | ||
|
||
use serde::Serialize; | ||
|
||
#[derive(Default, Serialize)] | ||
pub struct SourceMap { | ||
files: HashMap</*file name*/ String, HashMap</*line*/ u32, /*offset*/ u32>>, | ||
local_variables: | ||
HashMap</*file name*/ String, HashMap<String, Vec<(/*line*/ u32, /*var index*/ i32)>>>, | ||
global_variables: HashMap</*name*/ String, /*var index*/ i32>, | ||
} | ||
|
||
impl SourceMap { | ||
pub fn new() -> Self { | ||
Self::default() | ||
} | ||
|
||
pub fn clear(&mut self) { | ||
self.files.clear(); | ||
} | ||
|
||
pub fn add(&mut self, path: &str, line: u32, offset: u32) { | ||
self.files | ||
.entry(path.to_string()) | ||
.or_insert_with(HashMap::new) | ||
.insert(line, offset); | ||
} | ||
|
||
pub fn get_offset(&self, path: &str, line: u32) -> Option<u32> { | ||
let file = self.files.get(path)?; | ||
file.get(&line).cloned() | ||
} | ||
|
||
pub fn adjust_offset_by(&mut self, delta: u32) { | ||
for file in self.files.values_mut() { | ||
for offset in file.values_mut() { | ||
*offset += delta; | ||
} | ||
} | ||
} | ||
|
||
pub fn new_local_variable_scope( | ||
&mut self, | ||
file_name: &str, | ||
line: u32, | ||
name: &str, | ||
var_index: i32, | ||
) { | ||
self.local_variables | ||
.entry(file_name.to_lowercase()) | ||
.or_insert_with(HashMap::new) | ||
.entry(name.to_lowercase()) | ||
.or_insert_with(Vec::new) | ||
.push((line, var_index)); | ||
} | ||
|
||
pub fn find_local_variable_index( | ||
&self, | ||
file_name: &str, | ||
line: u32, | ||
var_name: &str, | ||
) -> Option<i32> { | ||
let file_map = self.local_variables.get(&file_name.to_lowercase())?; | ||
let scopes = file_map.get(&var_name.to_lowercase())?; | ||
|
||
for (i, scope) in scopes.iter().enumerate() { | ||
if (*scope).0 > line { | ||
if i == 0 { | ||
// our line is before the first declaration | ||
return None; | ||
} | ||
// our variable is not in this scope, which means it was declared in the previous one | ||
// so we return the index from the previous scope | ||
return Some(scopes[i - 1].1); | ||
} | ||
|
||
if i == scopes.len() - 1 { | ||
// our line is after the last declaration | ||
return Some(scope.1); | ||
} | ||
} | ||
|
||
None | ||
} | ||
|
||
pub fn new_global_variable(&mut self, name: &str, var_index: i32) { | ||
self.global_variables.insert(name.to_lowercase(), var_index); | ||
} | ||
|
||
pub fn find_global_variable_index(&self, name: &str) -> Option<i32> { | ||
self.global_variables.get(&name.to_lowercase()).cloned() | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn test_source_map() { | ||
let mut map = SourceMap::new(); | ||
map.add("file1", 1, 10); | ||
map.add("file1", 2, 20); | ||
map.add("file1", 3, 30); | ||
map.add("file2", 1, 40); | ||
map.add("file2", 2, 50); | ||
map.add("file2", 3, 60); | ||
|
||
assert_eq!(map.get_offset("file1", 1), Some(10)); | ||
assert_eq!(map.get_offset("file1", 2), Some(20)); | ||
assert_eq!(map.get_offset("file1", 3), Some(30)); | ||
assert_eq!(map.get_offset("file2", 1), Some(40)); | ||
assert_eq!(map.get_offset("file2", 2), Some(50)); | ||
assert_eq!(map.get_offset("file2", 3), Some(60)); | ||
|
||
map.adjust_offset_by(100); | ||
|
||
assert_eq!(map.get_offset("file1", 1), Some(110)); | ||
assert_eq!(map.get_offset("file1", 2), Some(120)); | ||
assert_eq!(map.get_offset("file1", 3), Some(130)); | ||
assert_eq!(map.get_offset("file2", 1), Some(140)); | ||
assert_eq!(map.get_offset("file2", 2), Some(150)); | ||
assert_eq!(map.get_offset("file2", 3), Some(160)); | ||
} | ||
|
||
#[test] | ||
fn test_local_variables() { | ||
let mut map = SourceMap::new(); | ||
map.new_local_variable_scope("file1", 1, "var1", 10); | ||
map.new_local_variable_scope("file1", 3, "var1", 20); | ||
map.new_local_variable_scope("file1", 5, "var1", 30); | ||
|
||
assert_eq!(map.find_local_variable_index("file1", 0, "var1"), None); | ||
assert_eq!(map.find_local_variable_index("file1", 1, "var1"), Some(10)); | ||
assert_eq!(map.find_local_variable_index("file1", 2, "var1"), Some(10)); | ||
assert_eq!(map.find_local_variable_index("file1", 3, "var1"), Some(20)); | ||
assert_eq!(map.find_local_variable_index("file1", 4, "var1"), Some(20)); | ||
assert_eq!(map.find_local_variable_index("file1", 6, "var1"), Some(30)); | ||
} | ||
|
||
#[test] | ||
fn test_local_variables2() { | ||
let mut map = SourceMap::new(); | ||
map.new_local_variable_scope("file1", 5, "var1", 10); | ||
|
||
assert_eq!(map.find_local_variable_index("file1", 0, "var1"), None); | ||
assert_eq!(map.find_local_variable_index("file1", 1, "var1"), None); | ||
assert_eq!(map.find_local_variable_index("file1", 2, "var1"), None); | ||
assert_eq!(map.find_local_variable_index("file1", 3, "var1"), None); | ||
assert_eq!(map.find_local_variable_index("file1", 4, "var1"), None); | ||
assert_eq!(map.find_local_variable_index("file1", 5, "var1"), Some(10)); | ||
assert_eq!(map.find_local_variable_index("file1", 6, "var1"), Some(10)); | ||
} | ||
} |