about summary refs log tree commit diff
diff options
context:
space:
mode:
authorhkalbasi <hamidrezakalbasi@protonmail.com>2023-08-06 01:46:29 +0330
committerhkalbasi <hamidrezakalbasi@protonmail.com>2023-08-06 01:46:29 +0330
commit9b636e23265705f5ca4f5965a0e207fefd869990 (patch)
tree4a37a66ad8f61be3a788c351e9d2a7e8defcf562
parentcc5664c5a22f0b8fcd268f5b5866bc91dacdda6a (diff)
downloadrust-9b636e23265705f5ca4f5965a0e207fefd869990.tar.gz
rust-9b636e23265705f5ca4f5965a0e207fefd869990.zip
Support `libc::getenv` in mir interpreter
-rw-r--r--crates/base-db/src/fixture.rs4
-rw-r--r--crates/base-db/src/input.rs6
-rw-r--r--crates/hir-ty/src/mir/eval/shim.rs32
-rw-r--r--crates/hir-ty/src/mir/eval/tests.rs42
4 files changed, 83 insertions, 1 deletions
diff --git a/crates/base-db/src/fixture.rs b/crates/base-db/src/fixture.rs
index 323ee4260e4..aaac0fc3790 100644
--- a/crates/base-db/src/fixture.rs
+++ b/crates/base-db/src/fixture.rs
@@ -130,6 +130,7 @@ impl ChangeFixture {
         let mut default_crate_root: Option<FileId> = None;
         let mut default_target_data_layout: Option<String> = None;
         let mut default_cfg = CfgOptions::default();
+        let mut default_env = Env::new_for_test_fixture();
 
         let mut file_set = FileSet::default();
         let mut current_source_root_kind = SourceRootKind::Local;
@@ -200,6 +201,7 @@ impl ChangeFixture {
                 assert!(default_crate_root.is_none());
                 default_crate_root = Some(file_id);
                 default_cfg = meta.cfg;
+                default_env.extend(meta.env.iter().map(|(x, y)| (x.to_owned(), y.to_owned())));
                 default_target_data_layout = meta.target_data_layout;
             }
 
@@ -220,7 +222,7 @@ impl ChangeFixture {
                 None,
                 default_cfg,
                 Default::default(),
-                Env::new_for_test_fixture(),
+                default_env,
                 false,
                 CrateOrigin::Local { repo: None, name: None },
                 default_target_data_layout
diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs
index c47799f1320..b75c7079be7 100644
--- a/crates/base-db/src/input.rs
+++ b/crates/base-db/src/input.rs
@@ -686,6 +686,12 @@ impl fmt::Display for Edition {
     }
 }
 
+impl Extend<(String, String)> for Env {
+    fn extend<T: IntoIterator<Item = (String, String)>>(&mut self, iter: T) {
+        self.entries.extend(iter);
+    }
+}
+
 impl FromIterator<(String, String)> for Env {
     fn from_iter<T: IntoIterator<Item = (String, String)>>(iter: T) -> Self {
         Env { entries: FromIterator::from_iter(iter) }
diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs
index b2e29fd34b5..bfd7d7c1f2c 100644
--- a/crates/hir-ty/src/mir/eval/shim.rs
+++ b/crates/hir-ty/src/mir/eval/shim.rs
@@ -473,6 +473,38 @@ impl Evaluator<'_> {
                 self.write_memory_using_ref(destination.addr, destination.size)?.fill(0);
                 Ok(())
             }
+            "getenv" => {
+                let [name] = args else {
+                    return Err(MirEvalError::TypeError("libc::write args are not provided"));
+                };
+                let mut name_buf = vec![];
+                let name = {
+                    let mut index = Address::from_bytes(name.get(self)?)?;
+                    loop {
+                        let byte = self.read_memory(index, 1)?[0];
+                        index = index.offset(1);
+                        if byte == 0 {
+                            break;
+                        }
+                        name_buf.push(byte);
+                    }
+                    String::from_utf8_lossy(&name_buf)
+                };
+                let value = self.db.crate_graph()[self.crate_id].env.get(&name);
+                match value {
+                    None => {
+                        // Write null as fail
+                        self.write_memory_using_ref(destination.addr, destination.size)?.fill(0);
+                    }
+                    Some(mut value) => {
+                        value.push('\0');
+                        let addr = self.heap_allocate(value.len(), 1)?;
+                        self.write_memory(addr, value.as_bytes())?;
+                        self.write_memory(destination.addr, &addr.to_bytes())?;
+                    }
+                }
+                Ok(())
+            }
             _ => not_supported!("unknown external function {as_str}"),
         }
     }
diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs
index 46165cf3d69..ff30dc6dade 100644
--- a/crates/hir-ty/src/mir/eval/tests.rs
+++ b/crates/hir-ty/src/mir/eval/tests.rs
@@ -730,6 +730,48 @@ fn main() {
 }
 
 #[test]
+fn posix_getenv() {
+    check_pass(
+        r#"
+//- /main.rs env:foo=bar
+
+type c_char = u8;
+
+extern "C" {
+    pub fn getenv(s: *const c_char) -> *mut c_char;
+}
+
+fn should_not_reach() {
+    _ // FIXME: replace this function with panic when that works
+}
+
+fn main() {
+    let result = getenv(b"foo\0" as *const _);
+    if *result != b'b' {
+        should_not_reach();
+    }
+    let result = (result as usize + 1) as *const c_char;
+    if *result != b'a' {
+        should_not_reach();
+    }
+    let result = (result as usize + 1) as *const c_char;
+    if *result != b'r' {
+        should_not_reach();
+    }
+    let result = (result as usize + 1) as *const c_char;
+    if *result != 0 {
+        should_not_reach();
+    }
+    let result = getenv(b"not found\0" as *const _);
+    if result as usize != 0 {
+        should_not_reach();
+    }
+}
+"#,
+    );
+}
+
+#[test]
 fn posix_tls() {
     check_pass(
         r#"