lasm/
lasm_function.rs

1use std::collections::HashMap;
2use cranelift_jit::JITModule;
3use cranelift_module::FuncId;
4use super::{LasmError, Value};
5use std::ffi::CStr;
6
7#[repr(u8)]
8#[derive(Clone, Copy, Debug)]
9pub enum Tag {
10    Int = 0,
11    Float = 1,
12    Ptr = 2,
13    Null = 3,
14}
15
16#[repr(C)]
17#[derive(Clone, Copy, Debug)]
18pub struct Slot {
19    pub tag: Tag,
20    pub _pad: [u8; 7],
21    pub data: u64,
22}
23
24pub struct LasmFunction {
25    jit_module: Option<JITModule>,
26    func_id: FuncId,
27    variables: Vec<String>,
28}
29
30#[unsafe(no_mangle)]
31pub extern "C" fn write_int(value: i64) -> i64 {
32    print!("{}", value);
33    0
34}
35
36#[unsafe(no_mangle)]
37pub extern "C" fn write_float(value: f64) -> i64 {
38    print!("{}", value);
39    0
40}
41
42#[unsafe(no_mangle)]
43pub extern "C" fn lasm_malloc(size: i64) -> i64 {
44    use libc::malloc;
45    if size <= 0 {
46        return 0;
47    }
48    unsafe {
49        malloc(size as usize) as i64
50    }
51}
52
53#[unsafe(no_mangle)]
54pub extern "C" fn lasm_free(ptr: i64) {
55    use libc::free;
56    if ptr == 0 {
57        return;
58    }
59    unsafe {
60        free(ptr as *mut libc::c_void);
61    }
62}
63
64#[unsafe(no_mangle)]
65pub extern "C" fn lasm_sleep(milliseconds: i64) {
66    use std::thread;
67    use std::time::Duration;
68    thread::sleep(Duration::from_millis(milliseconds as u64));
69}
70
71#[repr(C)]
72#[derive(Clone, Copy)]
73pub struct TimeResult {
74    pub seconds: i64,
75    pub nanoseconds: i64,
76}
77
78#[unsafe(no_mangle)]
79pub extern "C" fn lasm_time() -> i64 {
80    use std::time::{SystemTime, UNIX_EPOCH};
81    let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
82    (now.as_secs() as i64 * 1_000_000_000) + now.subsec_nanos() as i64
83}
84
85#[unsafe(no_mangle)]
86pub extern "C" fn lasm_rand(seed: i64) -> i64 {
87    // Simple LCG: seed = (seed * 1103515245 + 12345) % (1 << 31)
88    let mut s = seed as u32;
89    s = ((s as u64 * 1103515245u64 + 12345) % (1u64 << 31)) as u32;
90    let value = s as f64 / (1u32 << 31) as f64;
91    value.to_bits() as i64
92}
93
94#[unsafe(no_mangle)]
95pub extern "C" fn lasm_atoi(ptr: i64) -> i64 {
96    use std::ffi::CStr;
97    if ptr == 0 {
98        return 0;
99    }
100    unsafe {
101        let c_str = CStr::from_ptr(ptr as *const i8);
102        if let Ok(s) = c_str.to_str() {
103            s.parse().unwrap_or(0)
104        } else {
105            0
106        }
107    }
108}
109
110#[unsafe(no_mangle)]
111pub extern "C" fn lasm_system(cmd_ptr: i64) -> i64 {
112    use std::ffi::CStr;
113    use std::process::Command;
114    
115    #[cfg(unix)]
116    unsafe {
117        let cmd_c = CStr::from_ptr(cmd_ptr as *const i8);
118        if let Ok(cmd_s) = cmd_c.to_str() {
119            match Command::new("sh").arg("-c").arg(cmd_s).status() {
120                Ok(status) => status.code().unwrap_or(-1) as i64,
121                Err(_) => -1,
122            }
123        } else {
124            -1
125        }
126    }
127    #[cfg(windows)]
128    unsafe {
129        let cmd_c = CStr::from_ptr(cmd_ptr as *const i8);
130        if let Ok(cmd_s) = cmd_c.to_str() {
131            match Command::new("cmd").arg("/C").arg(cmd_s).status() {
132                Ok(status) => status.code().unwrap_or(-1) as i64,
133                Err(_) => -1,
134            }
135        } else {
136            -1
137        }
138    }
139}
140
141#[unsafe(no_mangle)]
142pub extern "C" fn lasm_ends_with(str_ptr: i64, suffix_ptr: i64) -> i64 {
143    use std::ffi::CStr;
144    unsafe {
145        let str_c = CStr::from_ptr(str_ptr as *const i8);
146        let suffix_c = CStr::from_ptr(suffix_ptr as *const i8);
147        
148        if let (Ok(str_s), Ok(suffix_s)) = (str_c.to_str(), suffix_c.to_str()) {
149            if str_s.ends_with(suffix_s) {
150                1
151            } else {
152                0
153            }
154        } else {
155            0
156        }
157    }
158}
159
160#[unsafe(no_mangle)]
161pub extern "C" fn lasm_trim(str_ptr: i64, out_ptr: i64) -> i64 {
162    use std::ffi::CStr;
163    unsafe {
164        let str_c = CStr::from_ptr(str_ptr as *const i8);
165        if let Ok(s) = str_c.to_str() {
166            let trimmed = s.trim();
167            let bytes = trimmed.as_bytes();
168            let len = bytes.len().min(255);
169            std::ptr::copy_nonoverlapping(bytes.as_ptr(), out_ptr as *mut u8, len);
170            *(out_ptr as *mut u8).add(len) = 0;
171            len as i64
172        } else {
173            0
174        }
175    }
176}
177
178#[unsafe(no_mangle)]
179pub extern "C" fn lasm_trim_start(str_ptr: i64, out_ptr: i64) -> i64 {
180    use std::ffi::CStr;
181    unsafe {
182        let str_c = CStr::from_ptr(str_ptr as *const i8);
183        if let Ok(s) = str_c.to_str() {
184            let trimmed = s.trim_start();
185            let bytes = trimmed.as_bytes();
186            let len = bytes.len().min(255);
187            std::ptr::copy_nonoverlapping(bytes.as_ptr(), out_ptr as *mut u8, len);
188            *(out_ptr as *mut u8).add(len) = 0;
189            len as i64
190        } else {
191            0
192        }
193    }
194}
195
196#[unsafe(no_mangle)]
197pub extern "C" fn lasm_trim_end(str_ptr: i64, out_ptr: i64) -> i64 {
198    use std::ffi::CStr;
199    unsafe {
200        let str_c = CStr::from_ptr(str_ptr as *const i8);
201        if let Ok(s) = str_c.to_str() {
202            let trimmed = s.trim_end();
203            let bytes = trimmed.as_bytes();
204            let len = bytes.len().min(255);
205            std::ptr::copy_nonoverlapping(bytes.as_ptr(), out_ptr as *mut u8, len);
206            *(out_ptr as *mut u8).add(len) = 0;
207            len as i64
208        } else {
209            0
210        }
211    }
212}
213
214#[unsafe(no_mangle)]
215pub extern "C" fn lasm_streq(str1_ptr: i64, str2_ptr: i64) -> i64 {
216    use std::ffi::CStr;
217    unsafe {
218        let str1_c = CStr::from_ptr(str1_ptr as *const i8);
219        let str2_c = CStr::from_ptr(str2_ptr as *const i8);
220        
221        if let (Ok(str1_s), Ok(str2_s)) = (str1_c.to_str(), str2_c.to_str()) {
222            if str1_s == str2_s {
223                1
224            } else {
225                0
226            }
227        } else {
228            0
229        }
230    }
231}
232
233#[unsafe(no_mangle)]
234pub extern "C" fn lasm_isws(str_ptr: i64) -> i64 {
235    use std::ffi::CStr;
236    unsafe {
237        let str_c = CStr::from_ptr(str_ptr as *const i8);
238        if let Ok(s) = str_c.to_str() {
239            if s.trim().is_empty() {
240                1
241            } else {
242                0
243            }
244        } else {
245            0
246        }
247    }
248}
249
250#[unsafe(no_mangle)]
251#[allow(unused_variables)]
252pub extern "C" fn lasm_syscall(number: i64, arg0: i64, arg1: i64, arg2: i64, arg3: i64, arg4: i64, arg5: i64) -> i64 {
253    #[cfg(macos)]
254    {
255        unsafe { libc::syscall(number as i32, arg0, arg1, arg2, arg3, arg4, arg5) as i64 }
256    }
257    #[cfg(linux)]
258    {
259        unsafe { libc::syscall(number, arg0, arg1, arg2, arg3, arg4, arg5) }
260    }
261    #[cfg(windows)]
262    {
263        match number {
264            1 => { // write
265                if arg0 == 1 { // stdout
266                    let buf = arg1 as *const u8;
267                    let count = arg2 as usize;
268                    unsafe {
269                        let slice = std::slice::from_raw_parts(buf, count);
270                        if let Ok(s) = std::str::from_utf8(slice) {
271                            print!("{}", s);
272                            std::io::Write::flush(&mut std::io::stdout()).ok();
273                            count as i64
274                        } else {
275                            -1
276                        }
277                    }
278                } else {
279                    -1
280                }
281            }
282            0 => { // read
283                if arg0 == 0 { // stdin
284                    let buf = arg1 as *mut u8;
285                    let count = arg2 as usize;
286                    unsafe {
287                        let mut input = String::new();
288                        if std::io::stdin().read_line(&mut input).is_ok() {
289                            let bytes = input.as_bytes();
290                            let len = bytes.len().min(count);
291                            std::ptr::copy_nonoverlapping(bytes.as_ptr(), buf, len);
292                            len as i64
293                        } else {
294                            -1
295                        }
296                    }
297                } else {
298                    -1
299                }
300            }
301            _ => -1
302        }
303    }
304    #[cfg(not(any(unix, windows)))]
305    {
306        -1
307    }
308}
309
310#[unsafe(no_mangle)]
311pub extern "C" fn lasm_print_int(value: i64) {
312    print!("{}", value);
313    use std::io::Write;
314    let _ = std::io::stdout().flush();
315}
316
317#[unsafe(no_mangle)]
318pub extern "C" fn lasm_print_float(bits: i64) {
319    let value = f64::from_bits(bits as u64);
320    print!("{}", value);
321    use std::io::Write;
322    let _ = std::io::stdout().flush();
323}
324
325#[unsafe(no_mangle)]
326pub extern "C" fn lasm_print_str(ptr: i64) {
327    use std::ffi::CStr;
328    if ptr == 0 {
329        return;
330    }
331    unsafe {
332        let c_str = CStr::from_ptr(ptr as *const i8);
333        if let Ok(s) = c_str.to_str() {
334            print!("{}", s);
335        }
336    }
337    use std::io::Write;
338    let _ = std::io::stdout().flush();
339}
340
341#[unsafe(no_mangle)]
342pub extern "C" fn lasm_itoa(value: i64, buf: i64, size: i64) -> i64 {
343    if buf == 0 || size <= 0 {
344        return 0;
345    }
346    let s = format!("{}", value);
347    let bytes = s.as_bytes();
348    let len = bytes.len().min(size as usize - 1);
349    unsafe {
350        std::ptr::copy_nonoverlapping(bytes.as_ptr(), buf as *mut u8, len);
351        *(buf as *mut u8).add(len) = 0; // null terminate
352    }
353    len as i64
354}
355
356#[unsafe(no_mangle)]
357pub extern "C" fn lasm_fadd(a_bits: i64, b_bits: i64) -> i64 {
358    let a = f64::from_bits(a_bits as u64);
359    let b = f64::from_bits(b_bits as u64);
360    (a + b).to_bits() as i64
361}
362
363#[unsafe(no_mangle)]
364pub extern "C" fn lasm_fsub(a_bits: i64, b_bits: i64) -> i64 {
365    let a = f64::from_bits(a_bits as u64);
366    let b = f64::from_bits(b_bits as u64);
367    (a - b).to_bits() as i64
368}
369
370#[unsafe(no_mangle)]
371pub extern "C" fn lasm_fmul(a_bits: i64, b_bits: i64) -> i64 {
372    let a = f64::from_bits(a_bits as u64);
373    let b = f64::from_bits(b_bits as u64);
374    (a * b).to_bits() as i64
375}
376
377#[unsafe(no_mangle)]
378pub extern "C" fn lasm_fdiv(a_bits: i64, b_bits: i64) -> i64 {
379    let a = f64::from_bits(a_bits as u64);
380    let b = f64::from_bits(b_bits as u64);
381    (a / b).to_bits() as i64
382}
383
384#[unsafe(no_mangle)]
385pub extern "C" fn lasm_itof(value: i64) -> i64 {
386    (value as f64).to_bits() as i64
387}
388
389#[unsafe(no_mangle)]
390pub extern "C" fn lasm_ftoi(bits: i64) -> i64 {
391    let f = f64::from_bits(bits as u64);
392    f as i64
393}
394
395#[unsafe(no_mangle)]
396pub extern "C" fn lasm_fmod(a_bits: i64, b_bits: i64) -> i64 {
397    let a = f64::from_bits(a_bits as u64);
398    let b = f64::from_bits(b_bits as u64);
399    (a % b).to_bits() as i64
400}
401
402#[unsafe(no_mangle)]
403pub extern "C" fn lasm_atof(ptr: i64) -> i64 {
404    use std::ffi::CStr;
405    if ptr == 0 {
406        return 0.0f64.to_bits() as i64;
407    }
408    unsafe {
409        let c_str = CStr::from_ptr(ptr as *const i8);
410        if let Ok(s) = c_str.to_str() {
411            if let Ok(f) = s.parse::<f64>() {
412                f.to_bits() as i64
413            } else {
414                0.0f64.to_bits() as i64
415            }
416        } else {
417            0.0f64.to_bits() as i64
418        }
419    }
420}
421
422#[unsafe(no_mangle)]
423pub extern "C" fn lasm_ftoa(bits: i64, buf: i64, size: i64) -> i64 {
424    if buf == 0 || size <= 0 {
425        return 0;
426    }
427    let f = f64::from_bits(bits as u64);
428    let s = format!("{}", f);
429    let bytes = s.as_bytes();
430    let len = bytes.len().min(size as usize - 1);
431    unsafe {
432        std::ptr::copy_nonoverlapping(bytes.as_ptr(), buf as *mut u8, len);
433        *(buf as *mut u8).add(len) = 0; // null terminate
434    }
435    len as i64
436}
437
438#[unsafe(no_mangle)]
439pub extern "C" fn lasm_memcpy(dst: i64, src: i64, len: i64) {
440    if dst == 0 || src == 0 || len <= 0 {
441        return;
442    }
443    unsafe {
444        std::ptr::copy_nonoverlapping(src as *const u8, dst as *mut u8, len as usize);
445    }
446}
447
448#[unsafe(no_mangle)]
449pub extern "C" fn lasm_memset(dst: i64, val: i64, len: i64) {
450    if dst == 0 || len <= 0 {
451        return;
452    }
453    unsafe {
454        std::ptr::write_bytes(dst as *mut u8, val as u8, len as usize);
455    }
456}
457
458#[unsafe(no_mangle)]
459pub extern "C" fn lasm_fmt_time(total_nanos: i64, fmt_ptr: i64, buffer: i64) -> i64 {
460    use std::ffi::CStr;
461    use std::time::{SystemTime, UNIX_EPOCH};
462
463    if buffer == 0 || fmt_ptr == 0 {
464        return 0;
465    }
466
467    let fmt_str = unsafe {
468        match CStr::from_ptr(fmt_ptr as *const i8).to_str() {
469            Ok(s) => s,
470            Err(_) => "%Y-%m-%d %H:%M:%S",
471        }
472    };
473    
474    let total_seconds = if total_nanos == 0 {
475        let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
476        now.as_secs()
477    } else {
478        (total_nanos / 1_000_000_000) as u64
479    };
480    
481    let _nanoseconds = if total_nanos == 0 {
482        let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
483        now.subsec_nanos()
484    } else {
485        (total_nanos % 1_000_000_000) as u32
486    };
487    
488    let days_since_epoch = total_seconds / 86400;
489    let mut year = 1970;
490    let mut days = days_since_epoch;
491    
492    loop {
493        let is_leap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
494        let days_in_year = if is_leap { 366 } else { 365 };
495        if days >= days_in_year {
496            days -= days_in_year;
497            year += 1;
498        } else {
499            break;
500        }
501    }
502    
503    let month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
504    let is_leap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
505    let mut month = 1;
506    let mut day = days + 1;
507    
508    for (i, &days_in_month) in month_days.iter().enumerate() {
509        let actual_days = if i == 1 && is_leap { days_in_month + 1 } else { days_in_month };
510        if day > actual_days {
511            day -= actual_days;
512            month += 1;
513        } else {
514            break;
515        }
516    }
517    
518    let seconds_in_day = total_seconds % 86400;
519    let hour = seconds_in_day / 3600;
520    let minute = (seconds_in_day % 3600) / 60;
521    let second = seconds_in_day % 60;
522    
523    let mut result = String::new();
524    let mut chars = fmt_str.chars().peekable();
525    
526    while let Some(ch) = chars.next() {
527        if ch == '%' {
528            if let Some(spec) = chars.next() {
529                match spec {
530                    'Y' => result.push_str(&format!("{:04}", year)),
531                    'm' => result.push_str(&format!("{:02}", month)),
532                    'd' => result.push_str(&format!("{:02}", day)),
533                    'H' => result.push_str(&format!("{:02}", hour)),
534                    'M' => result.push_str(&format!("{:02}", minute)),
535                    'S' => result.push_str(&format!("{:02}", second)),
536                    '%' => result.push('%'),
537                    _ => {
538                        result.push('%');
539                        result.push(spec);
540                    }
541                }
542            } else {
543                result.push('%');
544            }
545        } else {
546            result.push(ch);
547        }
548    }
549    
550    let bytes = result.as_bytes();
551    let len = bytes.len().min(255);
552    
553    unsafe {
554        std::ptr::copy_nonoverlapping(bytes.as_ptr(), buffer as *mut u8, len);
555        *(buffer as *mut u8).add(len) = 0;
556    }
557    
558    len as i64
559}
560
561#[unsafe(no_mangle)]
562pub extern "C" fn lasm_starts_with(str_ptr: i64, prefix_ptr: i64) -> i64 {
563    use std::ffi::CStr;
564    
565    if str_ptr == 0 || prefix_ptr == 0 {
566        return 0;
567    }
568    
569    unsafe {
570        let str_cstr = CStr::from_ptr(str_ptr as *const i8);
571        let prefix_cstr = CStr::from_ptr(prefix_ptr as *const i8);
572        
573        if let (Ok(str), Ok(prefix)) = (str_cstr.to_str(), prefix_cstr.to_str()) {
574            if str.starts_with(prefix) {
575                1
576            } else {
577                0
578            }
579        } else {
580            0
581        }
582    }
583}
584
585#[unsafe(no_mangle)]
586pub extern "C" fn lasm_str_len(str_ptr: i64) -> i64 {
587    use std::ffi::CStr;
588    
589    if str_ptr == 0 {
590        return 0;
591    }
592    
593    unsafe {
594        CStr::from_ptr(str_ptr as *const i8).to_bytes().len() as i64
595    }
596}
597
598#[unsafe(no_mangle)]
599pub extern "C" fn lasm_strcmp(str1_ptr: i64, str2_ptr: i64) -> i64 {
600    if str1_ptr == 0 || str2_ptr == 0 {
601        return if str1_ptr == str2_ptr { 0 } else { 1 };
602    }
603    unsafe {
604        let s1 = CStr::from_ptr(str1_ptr as *const i8).to_bytes();
605        let s2 = CStr::from_ptr(str2_ptr as *const i8).to_bytes();
606        s1.cmp(s2) as i64
607    }
608}
609
610#[unsafe(no_mangle)]
611pub extern "C" fn lasm_strcpy(dst: i64, src: i64) -> i64 {
612    if dst == 0 || src == 0 {
613        return 0;
614    }
615    unsafe {
616        let src_str = CStr::from_ptr(src as *const i8).to_bytes();
617        let dst_slice = std::slice::from_raw_parts_mut(dst as *mut u8, src_str.len() + 1);
618        dst_slice[..src_str.len()].copy_from_slice(src_str);
619        dst_slice[src_str.len()] = 0;
620        dst
621    }
622}
623
624
625#[unsafe(no_mangle)]
626pub extern "C" fn lasm_flush(fd: i64) {
627    if fd == 1 { // stdout
628        use std::io::Write;
629        let _ = std::io::stdout().flush();
630    } else if fd == 2 { // stderr
631        use std::io::Write;
632        let _ = std::io::stderr().flush();
633    }
634}
635
636impl LasmFunction {
637    pub fn new(jit_module: JITModule, func_id: FuncId, variables: Vec<String>) -> Self {
638        LasmFunction {
639            jit_module: Some(jit_module),
640            func_id,
641            variables,
642        }
643    }
644
645    pub fn call(&self, variables: &HashMap<String, Value>) -> Result<(i32, HashMap<String, Value>), LasmError> {
646        if let Some(ref jit) = self.jit_module {
647            let code = jit.get_finalized_function(self.func_id);
648            let mut vars_vec: Vec<(String, Value)> = self.variables.iter().map(|var| {
649            let value = variables.get(var).cloned().unwrap_or(Value::Null);
650                (var.clone(), value)
651            }).collect();
652            vars_vec.sort_by(|a, b| a.0.cmp(&b.0));
653            let mut slots = vec![];
654            for (_, value) in &vars_vec {
655                let slot = match value {
656                    Value::Int(i) => Slot { tag: Tag::Int, _pad: [0; 7], data: *i as u64 },
657                    Value::Float(f) => Slot { tag: Tag::Float, _pad: [0; 7], data: f.to_bits() },
658                    Value::String(s) => {
659                        let c_str = std::ffi::CString::new(s.clone()).map_err(|_| LasmError::Runtime("Invalid string".to_string()))?;
660                        let ptr = c_str.as_ptr() as *mut u8;
661                        std::mem::forget(c_str);
662                        Slot { tag: Tag::Ptr, _pad: [0; 7], data: ptr as u64 }
663                    }
664                    Value::Ptr(p) => Slot { tag: Tag::Ptr, _pad: [0; 7], data: *p as u64 },
665                    Value::Null => Slot { tag: Tag::Null, _pad: [0; 7], data: 0 },
666                };
667                slots.push(slot);
668            }
669
670            let mut slot_array = slots.into_boxed_slice();
671            let ptr = slot_array.as_mut_ptr();
672            let len = slot_array.len();
673            std::mem::forget(slot_array);
674
675            let func: extern "C" fn(*mut Slot, usize) -> i32 = unsafe { std::mem::transmute(code) };
676            let exit_code = func(ptr, len);
677
678            let slots_slice = unsafe { std::slice::from_raw_parts(ptr, len) };
679            let mut final_vars = HashMap::new();
680            for i in 0..len {
681                let slot = &slots_slice[i];
682                let value = match slot.tag {
683                    Tag::Int => Value::Int(slot.data as i64),
684                    Tag::Float => Value::Float(f64::from_bits(slot.data)),
685                    Tag::Ptr => Value::Ptr(slot.data as *mut u8),
686                    Tag::Null => Value::Null,
687                };
688                final_vars.insert(vars_vec[i].0.clone(), value);
689            }
690
691            Ok((exit_code, final_vars))
692        } else {
693            Err(LasmError::Runtime("No JIT module available".to_string()))
694        }
695    }
696}