~jojo/Carth

ref: 2d5e5be3b7e0ce88608095cd809ea805435a7658 Carth/foreign-core/src/lib.rs -rw-r--r-- 4.3 KiB
2d5e5be3JoJo Less desugaring in Parse (do in Infer instead), more concrete Parsed. 7 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#![feature(try_trait)]
#![allow(non_camel_case_types)]

mod ffi;

use libc::*;
use std::fs::File;
use std::io::{self, Read, Write};
use std::{alloc, mem, slice, str};

#[no_mangle]
pub extern "C" fn install_stackoverflow_handler() {
    extern "C" fn stackoverflow_handler(_emergency: c_int, _scp: ffi::stackoverflow_context_t) {
        println!("Stack overflow");
    }

    let extra_stack_size = 16 << 10; // 16 KB ought to be enough for anybody
    let extra_stack = heap_alloc(extra_stack_size);
    unsafe {
        ffi::stackoverflow_install_handler(
            stackoverflow_handler,
            extra_stack as *mut _,
            extra_stack_size as usize,
        );
    }
}

#[repr(C)]
pub struct Array<A> {
    elems: *mut A,
    len: u64,
}

impl<A> Array<A>
where
    A: Clone,
{
    fn new(src: &[A]) -> Array<A> {
        unsafe {
            let len = src.len();
            let p = ffi::GC_malloc(len * mem::size_of::<A>()) as *mut A;
            let dest = slice::from_raw_parts_mut(p, len);
            dest.clone_from_slice(src);
            Array {
                elems: p,
                len: len as u64,
            }
        }
    }
}

#[repr(C)]
pub struct Str {
    array: Array<u8>,
}

impl Str {
    fn new(s: &str) -> Str {
        Str {
            array: Array::new(s.as_bytes()),
        }
    }
}

#[repr(C)]
pub enum Maybe<A> {
    None,
    Some(A),
}

impl<A> std::ops::Try for Maybe<A> {
    type Ok = A;
    type Error = std::option::NoneError;

    #[inline]
    fn into_result(self) -> Result<A, std::option::NoneError> {
        match self {
            Maybe::None => Err(std::option::NoneError),
            Maybe::Some(x) => Ok(x),
        }
    }

    #[inline]
    fn from_ok(v: A) -> Self {
        Maybe::Some(v)
    }

    #[inline]
    fn from_error(_: std::option::NoneError) -> Self {
        Maybe::None
    }
}

// TODO: Do it properly.
//       https://en.cppreference.com/w/c/types/max_align_t
const MAX_ALIGN: usize = 8;

fn heap_alloc(size: u64) -> *mut u8 {
    unsafe { alloc::alloc(alloc::Layout::from_size_align(size as usize, MAX_ALIGN).unwrap()) }
}

#[no_mangle]
pub extern "C" fn carth_str_eq(s1: Str, s2: Str) -> bool {
    let (s1, s2) = (from_carth_str(&s1), from_carth_str(&s2));
    s1 == s2
}

#[export_name = "unsafe-display-inline"]
pub extern "C" fn display_inline(s: Str) {
    let s = from_carth_str(&s);
    print!("{}", s);
    io::stdout().flush().ok();
}

#[export_name = "str-append"]
pub extern "C" fn str_append(s1: Str, s2: Str) -> Str {
    let (s1, s2) = (from_carth_str(&s1), from_carth_str(&s2));
    Str::new(&(s1.to_string() + s2))
}

fn from_carth_str<'s>(s: &'s Str) -> &'s str {
    unsafe {
        let Array { elems, len } = s.array;
        let slice = slice::from_raw_parts(elems, len as usize);
        str::from_utf8_unchecked(slice)
    }
}

#[export_name = "show-int"]
pub extern "C" fn show_int(n: i64) -> Str {
    Str::new(&n.to_string())
}

#[export_name = "show-nat"]
pub extern "C" fn show_nat(n: u64) -> Str {
    Str::new(&n.to_string())
}

#[export_name = "show-f64"]
pub extern "C" fn show_f64(n: f64) -> Str {
    Str::new(&n.to_string())
}

#[export_name = "-panic"]
pub extern "C" fn panic(s: Str) {
    eprintln!("*** Panic: {}", from_carth_str(&s));
    std::process::abort()
}

#[export_name = "-get-contents"]
pub extern "C" fn get_contents() -> Str {
    let mut s = String::new();
    io::stdin()
        .read_to_string(&mut s)
        .expect("read all of stdin");
    Str::new(&s)
}

#[export_name = "unsafe-read-file"]
pub extern "C" fn read_file(fp: Str) -> Maybe<Str> {
    let fp = from_carth_str(&fp);
    let mut f = File::open(fp).ok()?;
    let mut s = String::new();
    f.read_to_string(&mut s).ok()?;
    Maybe::Some(Str::new(&s))
}

// NOTE: This is a hack to ensure that Rust links in libm.
//
//       It seems that if no non-dead code makes use of functions from libm, then rustc or
//       cargo won't link with libm. However, we need that to happen, as when running a
//       Carth program with the JIT, there is no shared library for libm to load, so we
//       need the libm functionality to be included in libforeign_core.so.
//
// TODO: Find some other way of ensuring libm is linked with when creating the shared lib.
#[no_mangle]
pub extern "C" fn dummy_ensure_rust_links_libm(a: f64) -> f64 {
    f64::sin(a)
}