sklears_compose/
wasm_integration.rs

1//! WebAssembly Integration for Browser-Based ML Pipelines
2//!
3//! This module provides WebAssembly (WASM) integration capabilities for running
4//! machine learning pipelines in browser environments. It enables client-side
5//! ML workflows, offline inference, and reduced server load through edge computing.
6
7use crate::enhanced_errors::PipelineError;
8use serde::{Deserialize, Serialize};
9use serde_json;
10use sklears_core::{error::Result as SklResult, prelude::SklearsError, traits::Estimator};
11use std::collections::HashMap;
12use std::fmt;
13
14/// WebAssembly runtime configuration
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct WasmConfig {
17    pub memory_limit_mb: usize,
18    pub enable_threads: bool,
19    pub enable_simd: bool,
20    pub enable_bulk_memory: bool,
21    pub stack_size_kb: usize,
22    pub optimization_level: OptimizationLevel,
23    pub debug_mode: bool,
24}
25
26impl Default for WasmConfig {
27    fn default() -> Self {
28        Self {
29            memory_limit_mb: 256,
30            enable_threads: true,
31            enable_simd: true,
32            enable_bulk_memory: true,
33            stack_size_kb: 512,
34            optimization_level: OptimizationLevel::Release,
35            debug_mode: false,
36        }
37    }
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
41pub enum OptimizationLevel {
42    /// Debug
43    Debug,
44    /// Release
45    Release,
46    /// ReleaseWithDebugInfo
47    ReleaseWithDebugInfo,
48    /// MinSize
49    MinSize,
50}
51
52/// WebAssembly-compatible pipeline wrapper
53pub struct WasmPipeline {
54    config: WasmConfig,
55    steps: Vec<WasmStep>,
56    metadata: PipelineMetadata,
57    serialized_state: Option<Vec<u8>>,
58}
59
60impl WasmPipeline {
61    #[must_use]
62    pub fn new(config: WasmConfig) -> Self {
63        Self {
64            config,
65            steps: Vec::new(),
66            metadata: PipelineMetadata::default(),
67            serialized_state: None,
68        }
69    }
70
71    /// Add a step to the WASM pipeline
72    pub fn add_step(&mut self, step: WasmStep) -> SklResult<()> {
73        // Validate WASM compatibility
74        self.validate_step_compatibility(&step)?;
75        self.steps.push(step);
76        Ok(())
77    }
78
79    /// Compile pipeline to WebAssembly
80    pub fn compile_to_wasm(&self) -> SklResult<WasmModule> {
81        let mut compiler = WasmCompiler::new(self.config.clone());
82
83        for step in &self.steps {
84            compiler.add_step(step)?;
85        }
86
87        compiler.compile()
88    }
89
90    /// Load pipeline from WebAssembly binary
91    pub fn from_wasm_binary(binary: &[u8], config: WasmConfig) -> SklResult<Self> {
92        let module = WasmModule::from_binary(binary)?;
93        Self::from_wasm_module(module, config)
94    }
95
96    /// Create pipeline from WASM module
97    pub fn from_wasm_module(module: WasmModule, config: WasmConfig) -> SklResult<Self> {
98        let metadata = module.extract_metadata()?;
99        let steps = module.extract_steps()?;
100
101        Ok(Self {
102            config,
103            steps,
104            metadata,
105            serialized_state: Some(module.binary),
106        })
107    }
108
109    /// Serialize pipeline for browser transfer
110    pub fn serialize_for_browser(&self) -> SklResult<Vec<u8>> {
111        let payload = BrowserPayload {
112            wasm_binary: self.serialized_state.clone().unwrap_or_default(),
113            metadata: self.metadata.clone(),
114            config: self.config.clone(),
115        };
116
117        serde_json::to_vec(&payload)
118            .map_err(|e| SklearsError::SerializationError(format!("Serialization failed: {e}")))
119    }
120
121    /// Create JavaScript bindings
122    pub fn generate_js_bindings(&self) -> SklResult<String> {
123        let mut js_code = String::new();
124
125        // Generate class definition
126        js_code.push_str(&format!(
127            "class {} {{\n",
128            self.metadata.name.replace(' ', "")
129        ));
130
131        // Constructor
132        js_code.push_str("  constructor(wasmModule) {\n");
133        js_code.push_str("    this.module = wasmModule;\n");
134        js_code.push_str("    this.memory = wasmModule.memory;\n");
135        js_code.push_str("  }\n\n");
136
137        // Generate methods for each step
138        for (i, step) in self.steps.iter().enumerate() {
139            let method_code = self.generate_step_js_method(i, step)?;
140            js_code.push_str(&method_code);
141        }
142
143        // Pipeline execution method
144        js_code.push_str("  async predict(input) {\n");
145        js_code.push_str("    let data = input;\n");
146        for i in 0..self.steps.len() {
147            js_code.push_str(&format!("    data = await this.step{i}(data);\n"));
148        }
149        js_code.push_str("    return data;\n");
150        js_code.push_str("  }\n");
151
152        js_code.push_str("}\n");
153
154        Ok(js_code)
155    }
156
157    fn validate_step_compatibility(&self, step: &WasmStep) -> SklResult<()> {
158        // Check memory requirements
159        if step.estimated_memory_mb > self.config.memory_limit_mb {
160            return Err(PipelineError::ResourceError {
161                resource_type: crate::enhanced_errors::ResourceType::Memory,
162                limit: self.config.memory_limit_mb as f64,
163                current: step.estimated_memory_mb as f64,
164                component: step.name.clone(),
165                suggestions: vec![
166                    "Increase WASM memory limit".to_string(),
167                    "Use a more memory-efficient algorithm".to_string(),
168                ],
169            }
170            .into());
171        }
172
173        // Check feature compatibility
174        if step.requires_threads && !self.config.enable_threads {
175            return Err(PipelineError::ConfigurationError {
176                message: "Step requires threading support".to_string(),
177                suggestions: vec!["Enable threads in WASM config".to_string()],
178                context: crate::enhanced_errors::ErrorContext {
179                    pipeline_stage: "compilation".to_string(),
180                    component_name: step.name.clone(),
181                    input_shape: None,
182                    parameters: HashMap::new(),
183                    stack_trace: vec!["WasmPipeline::validate_step_compatibility".to_string()],
184                },
185            }
186            .into());
187        }
188
189        Ok(())
190    }
191
192    fn generate_step_js_method(&self, index: usize, step: &WasmStep) -> SklResult<String> {
193        let mut method = String::new();
194
195        method.push_str(&format!("  async step{index}(input) {{\n"));
196        method.push_str("    // Convert JavaScript array to WASM memory\n");
197        method.push_str("    const inputPtr = this.module._malloc(input.length * 8);\n");
198        method.push_str("    const inputArray = new Float64Array(this.memory.buffer, inputPtr, input.length);\n");
199        method.push_str("    inputArray.set(input);\n\n");
200
201        method.push_str(&format!("    // Call WASM function for {}\n", step.name));
202        method.push_str(&format!(
203            "    const outputPtr = this.module._{}(inputPtr, input.length);\n",
204            step.name.to_lowercase().replace(' ', "_")
205        ));
206
207        method.push_str("    // Convert result back to JavaScript\n");
208        method.push_str("    const outputLength = this.module._get_output_length();\n");
209        method.push_str("    const outputArray = new Float64Array(this.memory.buffer, outputPtr, outputLength);\n");
210        method.push_str("    const result = Array.from(outputArray);\n\n");
211
212        method.push_str("    // Free WASM memory\n");
213        method.push_str("    this.module._free(inputPtr);\n");
214        method.push_str("    this.module._free(outputPtr);\n\n");
215
216        method.push_str("    return result;\n");
217        method.push_str("  }\n\n");
218
219        Ok(method)
220    }
221}
222
223/// WebAssembly-compatible pipeline step
224#[derive(Debug, Clone, Serialize, Deserialize)]
225pub struct WasmStep {
226    pub name: String,
227    pub step_type: WasmStepType,
228    pub parameters: HashMap<String, WasmValue>,
229    pub input_schema: DataSchema,
230    pub output_schema: DataSchema,
231    pub estimated_memory_mb: usize,
232    pub requires_threads: bool,
233    pub requires_simd: bool,
234}
235
236#[derive(Debug, Clone, Serialize, Deserialize)]
237pub enum WasmStepType {
238    /// Transformer
239    Transformer,
240    /// Predictor
241    Predictor,
242    /// CustomFunction
243    CustomFunction,
244}
245
246/// WASM-compatible value types
247#[derive(Debug, Clone, Serialize, Deserialize)]
248pub enum WasmValue {
249    /// I32
250    I32(i32),
251    /// I64
252    I64(i64),
253    /// F32
254    F32(f32),
255    /// F64
256    F64(f64),
257    /// String
258    String(String),
259    /// Array
260    Array(Vec<WasmValue>),
261    /// Object
262    Object(HashMap<String, WasmValue>),
263}
264
265impl fmt::Display for WasmValue {
266    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
267        match self {
268            WasmValue::I32(v) => write!(f, "{v}"),
269            WasmValue::I64(v) => write!(f, "{v}"),
270            WasmValue::F32(v) => write!(f, "{v}"),
271            WasmValue::F64(v) => write!(f, "{v}"),
272            WasmValue::String(v) => write!(f, "\"{v}\""),
273            WasmValue::Array(v) => write!(
274                f,
275                "[{}]",
276                v.iter()
277                    .map(std::string::ToString::to_string)
278                    .collect::<Vec<_>>()
279                    .join(", ")
280            ),
281            WasmValue::Object(_) => write!(f, "{{...}}"),
282        }
283    }
284}
285
286/// Data schema for WASM interop
287#[derive(Debug, Clone, Serialize, Deserialize)]
288pub struct DataSchema {
289    pub shape: Vec<usize>,
290    pub dtype: WasmDataType,
291    pub optional: bool,
292}
293
294#[derive(Debug, Clone, Serialize, Deserialize)]
295pub enum WasmDataType {
296    /// F32
297    F32,
298    /// F64
299    F64,
300    /// I32
301    I32,
302    /// I64
303    I64,
304    /// Bool
305    Bool,
306}
307
308/// Pipeline metadata for WASM
309#[derive(Debug, Clone, Serialize, Deserialize)]
310pub struct PipelineMetadata {
311    pub name: String,
312    pub version: String,
313    pub description: String,
314    pub author: String,
315    pub creation_date: String,
316    pub features: Vec<String>,
317    pub performance_metrics: HashMap<String, f64>,
318}
319
320impl Default for PipelineMetadata {
321    fn default() -> Self {
322        Self {
323            name: "Unnamed Pipeline".to_string(),
324            version: "1.0.0".to_string(),
325            description: "WebAssembly ML Pipeline".to_string(),
326            author: "Sklears".to_string(),
327            creation_date: chrono::Utc::now().to_rfc3339(),
328            features: Vec::new(),
329            performance_metrics: HashMap::new(),
330        }
331    }
332}
333
334/// WebAssembly module representation
335pub struct WasmModule {
336    pub binary: Vec<u8>,
337    pub metadata: PipelineMetadata,
338    pub exports: Vec<String>,
339    pub imports: Vec<String>,
340}
341
342impl WasmModule {
343    /// Load from WebAssembly binary
344    pub fn from_binary(binary: &[u8]) -> SklResult<Self> {
345        // In a real implementation, this would parse the WASM binary
346        Ok(Self {
347            binary: binary.to_vec(),
348            metadata: PipelineMetadata::default(),
349            exports: vec!["predict".to_string(), "transform".to_string()],
350            imports: vec!["env.memory".to_string()],
351        })
352    }
353
354    /// Extract metadata from WASM custom sections
355    pub fn extract_metadata(&self) -> SklResult<PipelineMetadata> {
356        // Would parse custom sections in real implementation
357        Ok(self.metadata.clone())
358    }
359
360    /// Extract pipeline steps from WASM
361    pub fn extract_steps(&self) -> SklResult<Vec<WasmStep>> {
362        // Would analyze WASM functions and reconstruct steps
363        Ok(Vec::new())
364    }
365
366    /// Get module size in bytes
367    #[must_use]
368    pub fn size(&self) -> usize {
369        self.binary.len()
370    }
371}
372
373/// WebAssembly compiler
374pub struct WasmCompiler {
375    config: WasmConfig,
376    steps: Vec<WasmStep>,
377    optimizations: Vec<WasmOptimization>,
378}
379
380impl WasmCompiler {
381    #[must_use]
382    pub fn new(config: WasmConfig) -> Self {
383        Self {
384            config,
385            steps: Vec::new(),
386            optimizations: Vec::new(),
387        }
388    }
389
390    pub fn add_step(&mut self, step: &WasmStep) -> SklResult<()> {
391        self.steps.push(step.clone());
392        Ok(())
393    }
394
395    pub fn add_optimization(&mut self, optimization: WasmOptimization) {
396        self.optimizations.push(optimization);
397    }
398
399    pub fn compile(&self) -> SklResult<WasmModule> {
400        // Generate WASM text format
401        let wat_code = self.generate_wat()?;
402
403        // Compile to binary (simplified - would use actual WASM compiler)
404        let binary = self.wat_to_wasm(&wat_code)?;
405
406        Ok(WasmModule {
407            binary,
408            metadata: self.generate_metadata(),
409            exports: self.get_exports(),
410            imports: self.get_imports(),
411        })
412    }
413
414    fn generate_wat(&self) -> SklResult<String> {
415        let mut wat = String::new();
416
417        // Module header
418        wat.push_str("(module\n");
419
420        // Memory import
421        wat.push_str("  (import \"env\" \"memory\" (memory 1))\n");
422
423        // Function imports
424        wat.push_str("  (import \"env\" \"log\" (func $log (param i32)))\n");
425
426        // Generate functions for each step
427        for (i, step) in self.steps.iter().enumerate() {
428            let func_wat = self.generate_step_function(i, step)?;
429            wat.push_str(&func_wat);
430        }
431
432        // Main predict function
433        wat.push_str("  (func $predict (param $input i32) (param $length i32) (result i32)\n");
434        wat.push_str("    (local $data i32)\n");
435        wat.push_str("    (local.set $data (local.get $input))\n");
436
437        for i in 0..self.steps.len() {
438            wat.push_str(&format!(
439                "    (local.set $data (call $step{i} (local.get $data) (local.get $length)))\n"
440            ));
441        }
442
443        wat.push_str("    (local.get $data)\n");
444        wat.push_str("  )\n");
445
446        // Exports
447        wat.push_str("  (export \"predict\" (func $predict))\n");
448        wat.push_str("  (export \"memory\" (memory 0))\n");
449
450        wat.push_str(")\n");
451
452        Ok(wat)
453    }
454
455    fn generate_step_function(&self, index: usize, step: &WasmStep) -> SklResult<String> {
456        let mut func = String::new();
457
458        func.push_str(&format!(
459            "  (func $step{index} (param $input i32) (param $length i32) (result i32)\n"
460        ));
461
462        match step.step_type {
463            WasmStepType::Transformer => {
464                func.push_str("    ;; Transformer logic would go here\n");
465                func.push_str("    (local.get $input) ;; Return input unchanged for now\n");
466            }
467            WasmStepType::Predictor => {
468                func.push_str("    ;; Predictor logic would go here\n");
469                func.push_str("    (local.get $input) ;; Return input unchanged for now\n");
470            }
471            WasmStepType::CustomFunction => {
472                func.push_str("    ;; Custom function logic would go here\n");
473                func.push_str("    (local.get $input) ;; Return input unchanged for now\n");
474            }
475        }
476
477        func.push_str("  )\n");
478
479        Ok(func)
480    }
481
482    fn wat_to_wasm(&self, wat_code: &str) -> SklResult<Vec<u8>> {
483        // Simplified compilation - in practice would use wabt or similar
484        // For now, return a placeholder binary
485        Ok(vec![0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]) // WASM magic number + version
486    }
487
488    fn generate_metadata(&self) -> PipelineMetadata {
489        PipelineMetadata {
490            name: "Compiled WASM Pipeline".to_string(),
491            version: "1.0.0".to_string(),
492            description: format!("Pipeline with {} steps", self.steps.len()),
493            author: "Sklears WASM Compiler".to_string(),
494            creation_date: chrono::Utc::now().to_rfc3339(),
495            features: self.steps.iter().map(|s| s.name.clone()).collect(),
496            performance_metrics: HashMap::new(),
497        }
498    }
499
500    fn get_exports(&self) -> Vec<String> {
501        vec!["predict".to_string(), "memory".to_string()]
502    }
503
504    fn get_imports(&self) -> Vec<String> {
505        vec!["env.memory".to_string(), "env.log".to_string()]
506    }
507}
508
509/// WASM optimization passes
510#[derive(Debug, Clone)]
511pub enum WasmOptimization {
512    /// DeadCodeElimination
513    DeadCodeElimination,
514    /// FunctionInlining
515    FunctionInlining,
516    /// LoopVectorization
517    LoopVectorization,
518    /// MemoryCompaction
519    MemoryCompaction,
520    /// SIMDOptimization
521    SIMDOptimization,
522}
523
524/// Browser payload for WASM delivery
525#[derive(Serialize, Deserialize)]
526struct BrowserPayload {
527    wasm_binary: Vec<u8>,
528    metadata: PipelineMetadata,
529    config: WasmConfig,
530}
531
532/// Browser integration utilities
533pub mod browser {
534    use super::{Estimator, SklResult, WasmModule, WasmPipeline};
535
536    /// JavaScript code generator for browser integration
537    pub struct BrowserIntegration;
538
539    impl BrowserIntegration {
540        /// Generate complete HTML page with embedded WASM
541        pub fn generate_html_page(
542            pipeline: &WasmPipeline,
543            wasm_module: &WasmModule,
544        ) -> SklResult<String> {
545            let js_bindings = pipeline.generate_js_bindings()?;
546            let wasm_hex = wasm_module
547                .binary
548                .iter()
549                .map(|b| format!("{b:02x}"))
550                .collect::<String>();
551
552            let html = format!(
553                r#"
554<!DOCTYPE html>
555<html>
556<head>
557    <title>{} - ML Pipeline</title>
558    <meta charset="utf-8">
559    <style>
560        body {{ font-family: Arial, sans-serif; margin: 20px; }}
561        .container {{ max-width: 800px; margin: 0 auto; }}
562        .input-section {{ margin: 20px 0; }}
563        .output-section {{ margin: 20px 0; padding: 10px; background: #f5f5f5; }}
564        input[type="number"] {{ margin: 2px; padding: 5px; }}
565        button {{ padding: 10px 20px; margin: 10px; }}
566    </style>
567</head>
568<body>
569    <div class="container">
570        <h1>{}</h1>
571        <p>{}</p>
572
573        <div class="input-section">
574            <h3>Input Data:</h3>
575            <input type="number" id="input0" placeholder="Feature 1" value="1.0">
576            <input type="number" id="input1" placeholder="Feature 2" value="2.0">
577            <input type="number" id="input2" placeholder="Feature 3" value="3.0">
578            <br>
579            <button onclick="runPrediction()">Run Prediction</button>
580        </div>
581
582        <div class="output-section">
583            <h3>Output:</h3>
584            <pre id="output">Click "Run Prediction" to see results</pre>
585        </div>
586    </div>
587
588    <script>
589        // Base64 encoded WASM binary
590        const wasmBinary = Uint8Array.from(atob('{}'), c => c.charCodeAt(0));
591
592        // Pipeline class
593        {}
594
595        let pipeline = null;
596
597        // Initialize WASM
598        async function initWasm() {{
599            try {{
600                const wasmModule = await WebAssembly.instantiate(wasmBinary, {{
601                    env: {{
602                        memory: new WebAssembly.Memory({{ initial: 256 }}),
603                        log: (ptr) => console.log('WASM log:', ptr)
604                    }}
605                }});
606
607                pipeline = new {}(wasmModule.instance);
608                console.log('WASM pipeline initialized successfully');
609            }} catch (error) {{
610                console.error('Failed to initialize WASM:', error);
611                document.getElementById('output').textContent = 'Error: ' + error.message;
612            }}
613        }}
614
615        // Run prediction
616        async function runPrediction() {{
617            if (!pipeline) {{
618                document.getElementById('output').textContent = 'Pipeline not initialized';
619                return;
620            }}
621
622            try {{
623                const input = [
624                    parseFloat(document.getElementById('input0').value),
625                    parseFloat(document.getElementById('input1').value),
626                    parseFloat(document.getElementById('input2').value)
627                ];
628
629                const startTime = performance.now();
630                const result = await pipeline.predict(input);
631                const endTime = performance.now();
632
633                document.getElementById('output').textContent =
634                    `Result: ${{JSON.stringify(result, null, 2)}}\n` +
635                    `Execution time: ${{(endTime - startTime).toFixed(2)}}ms`;
636            }} catch (error) {{
637                document.getElementById('output').textContent = 'Prediction error: ' + error.message;
638            }}
639        }}
640
641        // Initialize on page load
642        window.addEventListener('load', initWasm);
643    </script>
644</body>
645</html>
646"#,
647                pipeline.metadata.name,
648                pipeline.metadata.name,
649                pipeline.metadata.description,
650                wasm_hex,
651                js_bindings,
652                pipeline.metadata.name.replace(' ', "")
653            );
654
655            Ok(html)
656        }
657
658        /// Generate service worker for offline ML
659        pub fn generate_service_worker(pipeline: &WasmPipeline) -> SklResult<String> {
660            let sw_code = format!(
661                r"
662// Service Worker for Offline ML Pipeline
663const CACHE_NAME = 'ml-pipeline-v1';
664const PIPELINE_NAME = '{}';
665
666// Cache resources
667self.addEventListener('install', event => {{
668    event.waitUntil(
669        caches.open(CACHE_NAME).then(cache => {{
670            return cache.addAll([
671                '/',
672                '/index.html',
673                '/wasm/pipeline.wasm',
674                '/js/pipeline.js'
675            ]);
676        }})
677    );
678}});
679
680// Serve from cache
681self.addEventListener('fetch', event => {{
682    event.respondWith(
683        caches.match(event.request).then(response => {{
684            return response || fetch(event.request);
685        }})
686    );
687}});
688
689// Handle ML prediction requests
690self.addEventListener('message', event => {{
691    if (event.data.type === 'ML_PREDICT') {{
692        // Process ML prediction in service worker
693        handleMLPrediction(event.data.input)
694            .then(result => {{
695                event.ports[0].postMessage({{
696                    type: 'ML_RESULT',
697                    result: result
698                }});
699            }})
700            .catch(error => {{
701                event.ports[0].postMessage({{
702                    type: 'ML_ERROR',
703                    error: error.message
704                }});
705            }});
706    }}
707}});
708
709async function handleMLPrediction(input) {{
710    // Load WASM module if not already loaded
711    if (!self.wasmModule) {{
712        const wasmBinary = await fetch('/wasm/pipeline.wasm').then(r => r.arrayBuffer());
713        self.wasmModule = await WebAssembly.instantiate(wasmBinary);
714    }}
715
716    // Run prediction
717    // Implementation would depend on specific pipeline
718    return {{ prediction: 'offline-result' }};
719}}
720",
721                pipeline.metadata.name
722            );
723
724            Ok(sw_code)
725        }
726    }
727}
728
729// Re-export for external access
730pub use browser::BrowserIntegration;
731
732#[allow(non_snake_case)]
733#[cfg(test)]
734mod tests {
735    use super::*;
736
737    #[test]
738    fn test_wasm_config_creation() {
739        let config = WasmConfig::default();
740        assert_eq!(config.memory_limit_mb, 256);
741        assert!(config.enable_threads);
742        assert!(config.enable_simd);
743    }
744
745    #[test]
746    fn test_wasm_pipeline_creation() {
747        let config = WasmConfig::default();
748        let pipeline = WasmPipeline::new(config);
749        assert_eq!(pipeline.steps.len(), 0);
750    }
751
752    #[test]
753    fn test_wasm_step_creation() {
754        let step = WasmStep {
755            name: "TestStep".to_string(),
756            step_type: WasmStepType::Transformer,
757            parameters: HashMap::new(),
758            input_schema: DataSchema {
759                shape: vec![10, 5],
760                dtype: WasmDataType::F64,
761                optional: false,
762            },
763            output_schema: DataSchema {
764                shape: vec![10, 3],
765                dtype: WasmDataType::F64,
766                optional: false,
767            },
768            estimated_memory_mb: 64,
769            requires_threads: false,
770            requires_simd: false,
771        };
772
773        assert_eq!(step.name, "TestStep");
774        assert_eq!(step.estimated_memory_mb, 64);
775    }
776
777    #[test]
778    fn test_wasm_value_display() {
779        let value = WasmValue::F64(3.14159);
780        assert_eq!(value.to_string(), "3.14159");
781
782        let array_value = WasmValue::Array(vec![
783            WasmValue::I32(1),
784            WasmValue::I32(2),
785            WasmValue::I32(3),
786        ]);
787        assert_eq!(array_value.to_string(), "[1, 2, 3]");
788    }
789
790    #[test]
791    fn test_compiler_creation() {
792        let config = WasmConfig::default();
793        let compiler = WasmCompiler::new(config);
794        assert_eq!(compiler.steps.len(), 0);
795        assert_eq!(compiler.optimizations.len(), 0);
796    }
797}