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 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 => { if arg0 == 1 { 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 => { if arg0 == 0 { 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; }
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; }
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 { use std::io::Write;
629 let _ = std::io::stdout().flush();
630 } else if fd == 2 { 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}