aether/
ffi.rs

1//! C-FFI interface for Aether language bindings
2//!
3//! This module provides C-compatible functions for use with other languages
4//! through Foreign Function Interface (FFI).
5
6use std::ffi::{CStr, CString};
7use std::os::raw::{c_char, c_int};
8use std::panic;
9use std::sync::Mutex;
10
11use crate::{Aether, Value};
12use serde_json::json;
13
14/// Opaque handle for Aether engine
15#[repr(C)]
16pub struct AetherHandle {
17    _opaque: [u8; 0],
18}
19
20/// Error codes returned by C-FFI functions
21#[repr(C)]
22pub enum AetherErrorCode {
23    Success = 0,
24    ParseError = 1,
25    RuntimeError = 2,
26    NullPointer = 3,
27    Panic = 4,
28    InvalidJSON = 5,
29    VariableNotFound = 6,
30}
31
32/// Execution limits configuration
33#[repr(C)]
34pub struct AetherLimits {
35    pub max_steps: c_int,
36    pub max_recursion_depth: c_int,
37    pub max_duration_ms: c_int,
38}
39
40/// Cache statistics
41#[repr(C)]
42pub struct AetherCacheStats {
43    pub hits: c_int,
44    pub misses: c_int,
45    pub size: c_int,
46}
47
48/// Thread-safe wrapper for Aether engine
49struct ThreadSafeEngine {
50    #[allow(dead_code)]
51    engine: Aether,
52    #[allow(dead_code)]
53    mutex: Mutex<()>,
54}
55
56impl ThreadSafeEngine {
57    #[allow(dead_code)]
58    fn new(engine: Aether) -> Self {
59        Self {
60            engine,
61            mutex: Mutex::new(()),
62        }
63    }
64}
65
66/// Create a new Aether engine instance
67///
68/// Returns: Pointer to AetherHandle (must be freed with aether_free)
69#[unsafe(no_mangle)]
70pub extern "C" fn aether_new() -> *mut AetherHandle {
71    let engine = Box::new(Aether::new());
72    Box::into_raw(engine) as *mut AetherHandle
73}
74
75/// Create a new Aether engine with all IO permissions enabled
76///
77/// Returns: Pointer to AetherHandle (must be freed with aether_free)
78#[unsafe(no_mangle)]
79pub extern "C" fn aether_new_with_permissions() -> *mut AetherHandle {
80    let engine = Box::new(Aether::with_all_permissions());
81    Box::into_raw(engine) as *mut AetherHandle
82}
83
84/// Evaluate Aether code
85///
86/// # Parameters
87/// - handle: Aether engine handle
88/// - code: C string containing Aether code
89/// - result: Output parameter for result (must be freed with aether_free_string)
90/// - error: Output parameter for error message (must be freed with aether_free_string)
91///
92/// # Returns
93/// - 0 (Success) if evaluation succeeded
94/// - Non-zero error code if evaluation failed
95#[unsafe(no_mangle)]
96pub extern "C" fn aether_eval(
97    handle: *mut AetherHandle,
98    code: *const c_char,
99    result: *mut *mut c_char,
100    error: *mut *mut c_char,
101) -> c_int {
102    #![allow(clippy::not_unsafe_ptr_arg_deref)]
103    if handle.is_null() || code.is_null() || result.is_null() || error.is_null() {
104        return AetherErrorCode::NullPointer as c_int;
105    }
106
107    // Catch panics and convert them to errors
108    let panic_result = panic::catch_unwind(|| unsafe {
109        let engine = &mut *(handle as *mut Aether);
110        let code_str = match CStr::from_ptr(code).to_str() {
111            Ok(s) => s,
112            Err(_) => return AetherErrorCode::RuntimeError as c_int,
113        };
114
115        match engine.eval(code_str) {
116            Ok(val) => {
117                let result_str = value_to_string(&val);
118                match CString::new(result_str) {
119                    Ok(cstr) => {
120                        *result = cstr.into_raw();
121                        *error = std::ptr::null_mut();
122                        AetherErrorCode::Success as c_int
123                    }
124                    Err(_) => AetherErrorCode::RuntimeError as c_int,
125                }
126            }
127            Err(e) => {
128                let error_str = e.to_string();
129                match CString::new(error_str) {
130                    Ok(cstr) => {
131                        *error = cstr.into_raw();
132                        *result = std::ptr::null_mut();
133                        // Determine error type from message
134                        if e.contains("Parse error") {
135                            AetherErrorCode::ParseError as c_int
136                        } else {
137                            AetherErrorCode::RuntimeError as c_int
138                        }
139                    }
140                    Err(_) => AetherErrorCode::RuntimeError as c_int,
141                }
142            }
143        }
144    });
145
146    match panic_result {
147        Ok(code) => code,
148        Err(_) => {
149            unsafe {
150                let panic_msg = CString::new("Panic occurred during evaluation").unwrap();
151                *error = panic_msg.into_raw();
152                *result = std::ptr::null_mut();
153            }
154            AetherErrorCode::Panic as c_int
155        }
156    }
157}
158
159/// Get the version string of Aether
160///
161/// Returns: C string with version (must NOT be freed)
162#[unsafe(no_mangle)]
163pub extern "C" fn aether_version() -> *const c_char {
164    static VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "\0");
165    VERSION.as_ptr() as *const c_char
166}
167
168/// Free an Aether engine handle
169#[unsafe(no_mangle)]
170pub extern "C" fn aether_free(handle: *mut AetherHandle) {
171    if !handle.is_null() {
172        unsafe {
173            let _ = Box::from_raw(handle as *mut Aether);
174        }
175    }
176}
177
178/// Free a string allocated by Aether
179#[unsafe(no_mangle)]
180pub extern "C" fn aether_free_string(s: *mut c_char) {
181    #![allow(clippy::not_unsafe_ptr_arg_deref)]
182    if !s.is_null() {
183        unsafe {
184            let _ = CString::from_raw(s);
185        }
186    }
187}
188
189/// Helper function to convert Value to string representation
190fn value_to_string(value: &Value) -> String {
191    match value {
192        Value::Number(n) => {
193            // Format number nicely - remove trailing zeros
194            if n.fract() == 0.0 {
195                format!("{:.0}", n)
196            } else {
197                n.to_string()
198            }
199        }
200        Value::String(s) => s.clone(),
201        Value::Boolean(b) => b.to_string(),
202        Value::Array(arr) => {
203            let items: Vec<String> = arr.iter().map(value_to_string).collect();
204            format!("[{}]", items.join(", "))
205        }
206        Value::Dict(map) => {
207            let items: Vec<String> = map
208                .iter()
209                .map(|(k, v)| format!("{}: {}", k, value_to_string(v)))
210                .collect();
211            format!("{{{}}}", items.join(", "))
212        }
213        Value::Null => "null".to_string(),
214        Value::Function { .. } => "<function>".to_string(),
215        Value::BuiltIn { name, .. } => format!("<builtin: {}>", name),
216        Value::Generator { .. } => "<generator>".to_string(),
217        Value::Lazy { .. } => "<lazy>".to_string(),
218        Value::Fraction(f) => f.to_string(),
219    }
220}
221
222/// Helper function to convert Value to JSON string
223fn value_to_json(value: &Value) -> String {
224    match value {
225        Value::Number(n) => {
226            // 直接返回数字的 JSON 表示
227            json!(n).to_string()
228        }
229        Value::String(s) => json!(s).to_string(),
230        Value::Boolean(b) => json!(b).to_string(),
231        Value::Array(arr) => {
232            let items: Vec<serde_json::Value> = arr.iter().map(json_from_value).collect();
233            json!(items).to_string()
234        }
235        Value::Dict(map) => {
236            let mut obj = serde_json::Map::new();
237            for (k, v) in map {
238                obj.insert(k.clone(), json_from_value(v));
239            }
240            json!(obj).to_string()
241        }
242        Value::Null => "null".to_string(),
243        Value::Function { .. } => json!("<function>").to_string(),
244        Value::BuiltIn { name, .. } => json!(format!("<builtin: {}>", name)).to_string(),
245        Value::Generator { .. } => json!("<generator>").to_string(),
246        Value::Lazy { .. } => json!("<lazy>").to_string(),
247        Value::Fraction(f) => json!(f.to_string()).to_string(),
248    }
249}
250
251/// Helper function to convert Value to serde_json::Value
252fn json_from_value(value: &Value) -> serde_json::Value {
253    match value {
254        Value::Number(n) => json!(n),
255        Value::String(s) => json!(s),
256        Value::Boolean(b) => json!(b),
257        Value::Array(arr) => {
258            let items: Vec<serde_json::Value> = arr.iter().map(json_from_value).collect();
259            json!(items)
260        }
261        Value::Dict(map) => {
262            let mut obj = serde_json::Map::new();
263            for (k, v) in map {
264                obj.insert(k.clone(), json_from_value(v));
265            }
266            json!(obj)
267        }
268        Value::Null => json!(null),
269        Value::Function { .. } => json!("<function>"),
270        Value::BuiltIn { name, .. } => json!(format!("<builtin: {}>", name)),
271        Value::Generator { .. } => json!("<generator>"),
272        Value::Lazy { .. } => json!("<lazy>"),
273        Value::Fraction(f) => json!(f.to_string()),
274    }
275}
276
277/// Helper function to parse JSON to Value
278fn json_to_value(json_str: &str) -> Result<Value, String> {
279    let v: serde_json::Value =
280        serde_json::from_str(json_str).map_err(|e| format!("Invalid JSON: {}", e))?;
281
282    Ok(match v {
283        serde_json::Value::Number(n) => {
284            if n.is_i64() {
285                Value::Number(n.as_i64().unwrap() as f64)
286            } else {
287                Value::Number(n.as_f64().unwrap())
288            }
289        }
290        serde_json::Value::String(s) => Value::String(s),
291        serde_json::Value::Bool(b) => Value::Boolean(b),
292        serde_json::Value::Array(arr) => {
293            let items: Result<Vec<_>, _> =
294                arr.iter().map(|v| json_to_value(&v.to_string())).collect();
295            Value::Array(items?)
296        }
297        serde_json::Value::Object(obj) => {
298            let mut map = std::collections::HashMap::new();
299            for (k, v) in obj {
300                map.insert(k, json_to_value(&v.to_string())?);
301            }
302            Value::Dict(map)
303        }
304        serde_json::Value::Null => Value::Null,
305    })
306}
307
308// ============================================================
309// Variable Operations
310// ============================================================
311
312/// Set a global variable from host application
313///
314/// # Parameters
315/// - handle: Aether engine handle
316/// - name: Variable name
317/// - value_json: Variable value as JSON string
318///
319/// # Returns
320/// - 0 (Success) if variable was set
321/// - Non-zero error code if failed
322///
323/// # Safety
324/// - `handle` must be a valid pointer to an AetherHandle created by `aether_new` or `aether_new_with_permissions`
325/// - `name` must be a valid pointer to a null-terminated C string
326/// - `value_json` must be a valid pointer to a null-terminated C string
327#[unsafe(no_mangle)]
328pub unsafe extern "C" fn aether_set_global(
329    handle: *mut AetherHandle,
330    name: *const c_char,
331    value_json: *const c_char,
332) -> c_int {
333    if handle.is_null() || name.is_null() || value_json.is_null() {
334        return AetherErrorCode::NullPointer as c_int;
335    }
336
337    let panic_result = panic::catch_unwind(|| unsafe {
338        let engine = &mut *(handle as *mut Aether);
339        let name_str = match CStr::from_ptr(name).to_str() {
340            Ok(s) => s,
341            Err(_) => return AetherErrorCode::RuntimeError as c_int,
342        };
343        let json_str = match CStr::from_ptr(value_json).to_str() {
344            Ok(s) => s,
345            Err(_) => return AetherErrorCode::RuntimeError as c_int,
346        };
347
348        // Parse JSON to Value
349        let value = match json_to_value(json_str) {
350            Ok(v) => v,
351            Err(_) => return AetherErrorCode::InvalidJSON as c_int,
352        };
353
354        engine.set_global(name_str, value);
355        AetherErrorCode::Success as c_int
356    });
357
358    match panic_result {
359        Ok(code) => code,
360        Err(_) => AetherErrorCode::Panic as c_int,
361    }
362}
363
364/// Get a variable's value as JSON
365///
366/// # Parameters
367/// - handle: Aether engine handle
368/// - name: Variable name
369/// - value_json: Output parameter (must be freed with aether_free_string)
370///
371/// # Returns
372/// - 0 (Success) if variable was found
373/// - VariableNotFound (6) if variable doesn't exist
374/// - Non-zero error code for other failures
375///
376/// # Safety
377/// - `handle` must be a valid pointer to an AetherHandle created by `aether_new` or `aether_new_with_permissions`
378/// - `name` must be a valid pointer to a null-terminated C string
379/// - `value_json` must be a valid pointer to a `*mut c_char` that will be set to point to the result
380#[unsafe(no_mangle)]
381pub unsafe extern "C" fn aether_get_global(
382    handle: *mut AetherHandle,
383    name: *const c_char,
384    value_json: *mut *mut c_char,
385) -> c_int {
386    if handle.is_null() || name.is_null() || value_json.is_null() {
387        return AetherErrorCode::NullPointer as c_int;
388    }
389
390    let panic_result = panic::catch_unwind(|| unsafe {
391        let engine = &mut *(handle as *mut Aether);
392        let name_str = match CStr::from_ptr(name).to_str() {
393            Ok(s) => s,
394            Err(_) => return AetherErrorCode::RuntimeError as c_int,
395        };
396
397        // 直接从环境获取变量值
398        let value = engine.evaluator.get_global(name_str);
399
400        match value {
401            Some(val) => {
402                let json_str = value_to_json(&val);
403                match CString::new(json_str) {
404                    Ok(cstr) => {
405                        *value_json = cstr.into_raw();
406                        AetherErrorCode::Success as c_int
407                    }
408                    Err(_) => AetherErrorCode::RuntimeError as c_int,
409                }
410            }
411            None => AetherErrorCode::VariableNotFound as c_int,
412        }
413    });
414
415    match panic_result {
416        Ok(code) => code,
417        Err(_) => AetherErrorCode::Panic as c_int,
418    }
419}
420
421/// Reset the runtime environment (clears all variables)
422///
423/// # Parameters
424/// - handle: Aether engine handle
425#[unsafe(no_mangle)]
426pub extern "C" fn aether_reset_env(handle: *mut AetherHandle) {
427    if handle.is_null() {
428        return;
429    }
430
431    let _ = panic::catch_unwind(|| unsafe {
432        let engine = &mut *(handle as *mut Aether);
433        engine.reset_env();
434    });
435}
436
437// ============================================================
438// Trace Operations
439// ============================================================
440
441/// Get all trace entries as JSON array
442///
443/// # Parameters
444/// - handle: Aether engine handle
445/// - trace_json: Output parameter (must be freed with aether_free_string)
446///
447/// # Returns
448/// - 0 (Success) if trace was retrieved
449/// - Non-zero error code if failed
450///
451/// # Safety
452/// - `handle` must be a valid pointer to an AetherHandle created by `aether_new` or `aether_new_with_permissions`
453/// - `trace_json` must be a valid pointer to a `*mut c_char` that will be set to point to the result
454#[unsafe(no_mangle)]
455pub unsafe extern "C" fn aether_take_trace(
456    handle: *mut AetherHandle,
457    trace_json: *mut *mut c_char,
458) -> c_int {
459    if handle.is_null() || trace_json.is_null() {
460        return AetherErrorCode::NullPointer as c_int;
461    }
462
463    let panic_result = panic::catch_unwind(|| unsafe {
464        let engine = &mut *(handle as *mut Aether);
465        let traces = engine.take_trace();
466
467        let json_array = json!(traces).to_string();
468        match CString::new(json_array) {
469            Ok(cstr) => {
470                *trace_json = cstr.into_raw();
471                AetherErrorCode::Success as c_int
472            }
473            Err(_) => AetherErrorCode::RuntimeError as c_int,
474        }
475    });
476
477    match panic_result {
478        Ok(code) => code,
479        Err(_) => AetherErrorCode::Panic as c_int,
480    }
481}
482
483/// Clear the trace buffer
484///
485/// # Parameters
486/// - handle: Aether engine handle
487#[unsafe(no_mangle)]
488pub extern "C" fn aether_clear_trace(handle: *mut AetherHandle) {
489    if handle.is_null() {
490        return;
491    }
492
493    let _ = panic::catch_unwind(|| unsafe {
494        let engine = &mut *(handle as *mut Aether);
495        engine.clear_trace();
496    });
497}
498
499/// Get structured trace entries as JSON
500///
501/// # Parameters
502/// - handle: Aether engine handle
503/// - trace_json: Output parameter (must be freed with aether_free_string)
504///
505/// # Returns
506/// - 0 (Success) if trace was retrieved
507/// - Non-zero error code if failed
508///
509/// # Safety
510/// - `handle` must be a valid pointer to an AetherHandle created by `aether_new` or `aether_new_with_permissions`
511/// - `trace_json` must be a valid pointer to a `*mut c_char` that will be set to point to the result
512#[unsafe(no_mangle)]
513pub unsafe extern "C" fn aether_trace_records(
514    handle: *mut AetherHandle,
515    trace_json: *mut *mut c_char,
516) -> c_int {
517    if handle.is_null() || trace_json.is_null() {
518        return AetherErrorCode::NullPointer as c_int;
519    }
520
521    let panic_result = panic::catch_unwind(|| unsafe {
522        let engine = &mut *(handle as *mut Aether);
523        let records = engine.trace_records();
524
525        // Convert TraceEntry to JSON
526        let json_array: Vec<serde_json::Value> = records
527            .iter()
528            .map(|entry| {
529                json!({
530                    "level": format!("{:?}", entry.level),
531                    "category": entry.category,
532                    "timestamp": entry.timestamp.elapsed().as_secs(),
533                    "values": entry.values.iter().map(value_to_json).collect::<Vec<_>>(),
534                    "label": entry.label,
535                })
536            })
537            .collect();
538
539        match CString::new(json!(json_array).to_string()) {
540            Ok(cstr) => {
541                *trace_json = cstr.into_raw();
542                AetherErrorCode::Success as c_int
543            }
544            Err(_) => AetherErrorCode::RuntimeError as c_int,
545        }
546    });
547
548    match panic_result {
549        Ok(code) => code,
550        Err(_) => AetherErrorCode::Panic as c_int,
551    }
552}
553
554/// Get trace statistics as JSON
555///
556/// # Parameters
557/// - handle: Aether engine handle
558/// - stats_json: Output parameter (must be freed with aether_free_string)
559///
560/// # Returns
561/// - 0 (Success) if stats were retrieved
562/// - Non-zero error code if failed
563///
564/// # Safety
565/// - `handle` must be a valid pointer to an AetherHandle created by `aether_new` or `aether_new_with_permissions`
566/// - `stats_json` must be a valid pointer to a `*mut c_char` that will be set to point to the result
567#[unsafe(no_mangle)]
568pub unsafe extern "C" fn aether_trace_stats(
569    handle: *mut AetherHandle,
570    stats_json: *mut *mut c_char,
571) -> c_int {
572    if handle.is_null() || stats_json.is_null() {
573        return AetherErrorCode::NullPointer as c_int;
574    }
575
576    let panic_result = panic::catch_unwind(|| unsafe {
577        let engine = &mut *(handle as *mut Aether);
578        let stats = engine.trace_stats();
579
580        let json_stats = json!({
581            "total_entries": stats.total_entries,
582            "by_level": stats.by_level,
583            "by_category": stats.by_category,
584            "buffer_size": stats.buffer_size,
585            "buffer_full": stats.buffer_full,
586        })
587        .to_string();
588
589        match CString::new(json_stats) {
590            Ok(cstr) => {
591                *stats_json = cstr.into_raw();
592                AetherErrorCode::Success as c_int
593            }
594            Err(_) => AetherErrorCode::RuntimeError as c_int,
595        }
596    });
597
598    match panic_result {
599        Ok(code) => code,
600        Err(_) => AetherErrorCode::Panic as c_int,
601    }
602}
603
604// ============================================================
605// Execution Limits
606// ============================================================
607
608/// Set execution limits
609///
610/// # Parameters
611/// - handle: Aether engine handle
612/// - limits: Limits configuration
613///
614/// # Safety
615/// - `handle` must be a valid pointer to an AetherHandle created by `aether_new` or `aether_new_with_permissions`
616/// - `limits` must be a valid pointer to an AetherLimits struct
617#[unsafe(no_mangle)]
618pub unsafe extern "C" fn aether_set_limits(handle: *mut AetherHandle, limits: *const AetherLimits) {
619    if handle.is_null() || limits.is_null() {
620        return;
621    }
622
623    let _ = panic::catch_unwind(|| unsafe {
624        let engine = &mut *(handle as *mut Aether);
625        let limits_ref = &*limits;
626
627        let rust_limits = crate::runtime::ExecutionLimits {
628            max_steps: if limits_ref.max_steps < 0 {
629                None
630            } else {
631                Some(limits_ref.max_steps as usize)
632            },
633            max_recursion_depth: if limits_ref.max_recursion_depth < 0 {
634                None
635            } else {
636                Some(limits_ref.max_recursion_depth as usize)
637            },
638            max_duration_ms: if limits_ref.max_duration_ms < 0 {
639                None
640            } else {
641                Some(limits_ref.max_duration_ms as u64)
642            },
643            max_memory_bytes: None,
644        };
645
646        engine.set_limits(rust_limits);
647    });
648}
649
650/// Get current execution limits
651///
652/// # Parameters
653/// - handle: Aether engine handle
654/// - limits: Output parameter
655///
656/// # Safety
657/// - `handle` must be a valid pointer to an AetherHandle created by `aether_new` or `aether_new_with_permissions`
658/// - `limits` must be a valid pointer to an AetherLimits struct that will be filled with the current limits
659#[unsafe(no_mangle)]
660pub unsafe extern "C" fn aether_get_limits(handle: *mut AetherHandle, limits: *mut AetherLimits) {
661    if handle.is_null() || limits.is_null() {
662        return;
663    }
664
665    let _ = panic::catch_unwind(|| unsafe {
666        let engine = &mut *(handle as *mut Aether);
667        let rust_limits = engine.limits();
668
669        (*limits).max_steps = match rust_limits.max_steps {
670            Some(v) => v as c_int,
671            None => -1,
672        };
673        (*limits).max_recursion_depth = match rust_limits.max_recursion_depth {
674            Some(v) => v as c_int,
675            None => -1,
676        };
677        (*limits).max_duration_ms = match rust_limits.max_duration_ms {
678            Some(v) => v as c_int,
679            None => -1,
680        };
681    });
682}
683
684// ============================================================
685// Cache Control
686// ============================================================
687
688/// Clear the AST cache
689///
690/// # Parameters
691/// - handle: Aether engine handle
692#[unsafe(no_mangle)]
693pub extern "C" fn aether_clear_cache(handle: *mut AetherHandle) {
694    if handle.is_null() {
695        return;
696    }
697
698    let _ = panic::catch_unwind(|| unsafe {
699        let engine = &mut *(handle as *mut Aether);
700        engine.clear_cache();
701    });
702}
703
704/// Get cache statistics
705///
706/// # Parameters
707/// - handle: Aether engine handle
708/// - stats: Output parameter
709///
710/// # Safety
711/// - `handle` must be a valid pointer to an AetherHandle created by `aether_new` or `aether_new_with_permissions`
712/// - `stats` must be a valid pointer to an AetherCacheStats struct that will be filled with the statistics
713#[unsafe(no_mangle)]
714pub unsafe extern "C" fn aether_cache_stats(
715    handle: *mut AetherHandle,
716    stats: *mut AetherCacheStats,
717) {
718    if handle.is_null() || stats.is_null() {
719        return;
720    }
721
722    let _ = panic::catch_unwind(|| unsafe {
723        let engine = &mut *(handle as *mut Aether);
724        let rust_stats = engine.cache_stats();
725
726        (*stats).hits = rust_stats.hits as c_int;
727        (*stats).misses = rust_stats.misses as c_int;
728        (*stats).size = rust_stats.size as c_int;
729    });
730}
731
732// ============================================================
733// Optimization Control
734// ============================================================
735
736/// Set optimization options
737///
738/// # Parameters
739/// - handle: Aether engine handle
740/// - constant_folding: Enable constant folding (1 = yes, 0 = no)
741/// - dead_code_elimination: Enable dead code elimination (1 = yes, 0 = no)
742/// - tail_recursion: Enable tail recursion optimization (1 = yes, 0 = no)
743#[unsafe(no_mangle)]
744pub extern "C" fn aether_set_optimization(
745    handle: *mut AetherHandle,
746    constant_folding: c_int,
747    dead_code_elimination: c_int,
748    tail_recursion: c_int,
749) {
750    if handle.is_null() {
751        return;
752    }
753
754    let _ = panic::catch_unwind(|| unsafe {
755        let engine = &mut *(handle as *mut Aether);
756        engine.set_optimization(
757            constant_folding != 0,
758            dead_code_elimination != 0,
759            tail_recursion != 0,
760        );
761    });
762}