~jojo/Carth

ref: c5a394b01aa60cb95605d66f6118cfb231955aa7 Carth/std-rs/src/lib.rs -rw-r--r-- 5.0 KiB
c5a394b0JoJo Fix class constraint inference bug when explicit scheme given 1 year, 6 days 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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#![feature(try_trait)]
#![allow(non_camel_case_types)]

mod ffi;
pub mod io;
pub mod net;

use libc::*;
use std::fs::File;
use std::io::{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 Cons<A, B>(pub A, pub B);

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

impl<A> Array<A>
where
    A: Clone,
{
    pub 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,
            }
        }
    }

    pub fn as_slice(&self) -> &[A] {
        unsafe { slice::from_raw_parts(self.elems, self.len as usize) }
    }

    pub fn as_slice_mut(&mut self) -> &mut [A] {
        unsafe { slice::from_raw_parts_mut(self.elems, self.len as usize) }
    }

    pub unsafe fn shallow_copy(&self) -> Self {
        Self {
            elems: self.elems,
            len: self.len,
        }
    }
}

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

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

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

    pub unsafe fn shallow_copy(&self) -> Self {
        Self {
            array: self.array.shallow_copy(),
        }
    }
}

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

impl<A> Maybe<A> {
    pub fn unwrap(self) -> A {
        match self {
            Maybe::None => panic!("Maybe::unwrap on None"),
            Maybe::Some(a) => 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) = (s1.as_str(), s2.as_str());
    s1 == s2
}

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

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

#[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: {}", s.as_str());
    std::process::abort()
}

#[export_name = "-get-contents"]
pub extern "C" fn get_contents() -> Str {
    let mut s = String::new();
    std::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 = fp.as_str();
    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 the .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)
}