wasmer_compiler/engine/
inner.rs

1use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
2use std::sync::{Arc, Mutex};
3
4use crate::engine::builder::EngineBuilder;
5#[cfg(feature = "compiler")]
6use crate::{Compiler, CompilerConfig};
7
8#[cfg(not(target_arch = "wasm32"))]
9use wasmer_types::CompilationProgressCallback;
10#[cfg(feature = "compiler")]
11use wasmer_types::Features;
12use wasmer_types::{CompileError, target::Target};
13
14#[cfg(not(target_arch = "wasm32"))]
15use shared_buffer::OwnedBuffer;
16#[cfg(not(target_arch = "wasm32"))]
17use std::ffi::c_void;
18#[cfg(all(not(target_arch = "wasm32"), feature = "compiler"))]
19use std::io::Write;
20#[cfg(not(target_arch = "wasm32"))]
21use std::io::{Read, Seek};
22#[cfg(all(not(target_arch = "wasm32"), unix))]
23use std::os::fd::RawFd;
24#[cfg(not(target_arch = "wasm32"))]
25use std::path::Path;
26#[cfg(all(not(target_arch = "wasm32"), feature = "compiler"))]
27use wasmer_types::ModuleInfo;
28#[cfg(not(target_arch = "wasm32"))]
29use wasmer_types::{
30    DeserializeError, FunctionIndex, FunctionType, LocalFunctionIndex, SignatureHash,
31    SignatureIndex, entity::PrimaryMap,
32};
33
34#[cfg(not(target_arch = "wasm32"))]
35use crate::{
36    Artifact, BaseTunables, CodeMemory, FunctionExtent, GlobalFrameInfoRegistration, Tunables,
37    engine::mapped_binary::MemoryMappedBinary,
38    types::{
39        function::FunctionBodyLike,
40        section::{CustomSectionLike, CustomSectionProtection, SectionIndex},
41    },
42};
43
44#[cfg(not(target_arch = "wasm32"))]
45use wasmer_vm::{
46    FunctionBodyPtr, SectionBodyPtr, SignatureRegistry, VMFunctionBody, VMSignatureHash,
47    VMTrampoline,
48};
49
50/// A WebAssembly Engine.
51#[derive(Clone)]
52pub struct Engine {
53    inner: Arc<Mutex<EngineInner>>,
54    /// The target for the compiler
55    target: Arc<Target>,
56    engine_id: EngineId,
57    #[cfg(not(target_arch = "wasm32"))]
58    tunables: Arc<dyn Tunables + Send + Sync>,
59    name: String,
60}
61
62impl Engine {
63    /// Create a new `Engine` with the given config
64    #[cfg(feature = "compiler")]
65    pub fn new(
66        compiler_config: Box<dyn CompilerConfig>,
67        target: Target,
68        features: Features,
69    ) -> Self {
70        #[cfg(not(target_arch = "wasm32"))]
71        let tunables = BaseTunables::for_target(&target);
72        let compiler = compiler_config.compiler();
73        let name = format!("engine-{}", compiler.name());
74        Self {
75            inner: Arc::new(Mutex::new(EngineInner {
76                compiler: Some(compiler),
77                features,
78                #[cfg(not(target_arch = "wasm32"))]
79                code_memory: vec![],
80                #[cfg(not(target_arch = "wasm32"))]
81                elf_mapped_binary: vec![],
82                #[cfg(not(target_arch = "wasm32"))]
83                signatures: SignatureRegistry::new(),
84            })),
85            target: Arc::new(target),
86            engine_id: EngineId::default(),
87            #[cfg(not(target_arch = "wasm32"))]
88            tunables: Arc::new(tunables),
89            name,
90        }
91    }
92
93    /// Returns the name of this engine
94    pub fn name(&self) -> &str {
95        self.name.as_str()
96    }
97
98    /// Returns the deterministic id of this engine
99    pub fn deterministic_id(&self) -> String {
100        #[cfg(feature = "compiler")]
101        {
102            let i = self.inner();
103            if let Some(ref c) = i.compiler {
104                return c.deterministic_id();
105            } else {
106                return self.name.clone();
107            }
108        }
109
110        #[allow(unreachable_code)]
111        {
112            self.name.to_string()
113        }
114    }
115
116    /// Create a headless `Engine`
117    ///
118    /// A headless engine is an engine without any compiler attached.
119    /// This is useful for assuring a minimal runtime for running
120    /// WebAssembly modules.
121    ///
122    /// For example, for running in IoT devices where compilers are very
123    /// expensive, or also to optimize startup speed.
124    ///
125    /// # Important
126    ///
127    /// Headless engines can't compile or validate any modules,
128    /// they just take already processed Modules (via `Module::serialize`).
129    pub fn headless() -> Self {
130        let target = Target::default();
131        #[cfg(not(target_arch = "wasm32"))]
132        let tunables = BaseTunables::for_target(&target);
133        Self {
134            inner: Arc::new(Mutex::new(EngineInner {
135                #[cfg(feature = "compiler")]
136                compiler: None,
137                #[cfg(feature = "compiler")]
138                features: Features::default(),
139                #[cfg(not(target_arch = "wasm32"))]
140                code_memory: vec![],
141                #[cfg(not(target_arch = "wasm32"))]
142                elf_mapped_binary: vec![],
143                #[cfg(not(target_arch = "wasm32"))]
144                signatures: SignatureRegistry::new(),
145            })),
146            target: Arc::new(target),
147            engine_id: EngineId::default(),
148            #[cfg(not(target_arch = "wasm32"))]
149            tunables: Arc::new(tunables),
150            name: "engine-headless".to_string(),
151        }
152    }
153
154    /// Get reference to `EngineInner`.
155    pub fn inner(&self) -> std::sync::MutexGuard<'_, EngineInner> {
156        self.inner.lock().unwrap()
157    }
158
159    /// Get mutable reference to `EngineInner`.
160    pub fn inner_mut(&self) -> std::sync::MutexGuard<'_, EngineInner> {
161        self.inner.lock().unwrap()
162    }
163
164    /// Gets the target
165    pub fn target(&self) -> &Target {
166        &self.target
167    }
168
169    /// Register a signature
170    #[cfg(not(target_arch = "wasm32"))]
171    pub fn register_signature(&self, func_type: &FunctionType) -> VMSignatureHash {
172        let compiler = self.inner();
173        compiler
174            .signatures()
175            .register(func_type, SignatureHash(func_type.signature_hash()))
176    }
177
178    /// Look up a registered signature by its hash.
179    #[cfg(not(target_arch = "wasm32"))]
180    pub fn lookup_signature(&self, sig_hash: VMSignatureHash) -> Option<FunctionType> {
181        let compiler = self.inner();
182        compiler.signatures().lookup_signature(sig_hash)
183    }
184
185    /// Validates a WebAssembly module
186    #[cfg(feature = "compiler")]
187    pub fn validate(&self, binary: &[u8]) -> Result<(), CompileError> {
188        self.inner().validate(binary)
189    }
190
191    /// Compile a WebAssembly binary
192    #[cfg(feature = "compiler")]
193    #[cfg(not(target_arch = "wasm32"))]
194    pub fn compile(&self, binary: &[u8]) -> Result<Arc<Artifact>, CompileError> {
195        Ok(Arc::new(Artifact::new(
196            self,
197            binary,
198            self.tunables.as_ref(),
199            None,
200        )?))
201    }
202
203    /// Compile a WebAssembly binary with a progress callback.
204    #[cfg(feature = "compiler")]
205    pub fn compile_with_progress(
206        &self,
207        binary: &[u8],
208        progress_callback: Option<CompilationProgressCallback>,
209    ) -> Result<Arc<Artifact>, CompileError> {
210        Ok(Arc::new(Artifact::new(
211            self,
212            binary,
213            self.tunables.as_ref(),
214            progress_callback,
215        )?))
216    }
217
218    /// Compile a WebAssembly binary (the progress_callback argument is unused).
219    #[cfg(not(feature = "compiler"))]
220    #[cfg(not(target_arch = "wasm32"))]
221    pub fn compile_with_progress(
222        &self,
223        binary: &[u8],
224        _progress_callback: Option<CompilationProgressCallback>,
225    ) -> Result<Arc<Artifact>, CompileError> {
226        self.compile(binary, self.tunables.as_ref())
227    }
228
229    /// Compile a WebAssembly binary
230    #[cfg(not(feature = "compiler"))]
231    #[cfg(not(target_arch = "wasm32"))]
232    pub fn compile(
233        &self,
234        _binary: &[u8],
235        _tunables: &dyn Tunables,
236    ) -> Result<Arc<Artifact>, CompileError> {
237        Err(CompileError::Codegen(
238            "The Engine is operating in headless mode, so it can not compile Modules.".to_string(),
239        ))
240    }
241
242    #[cfg(not(target_arch = "wasm32"))]
243    /// Deserializes a WebAssembly module which was previously serialized with
244    /// [`wasmer::Module::serialize`].
245    ///
246    /// # Safety
247    ///
248    /// See [`Artifact::deserialize_unchecked`].
249    pub unsafe fn deserialize_unchecked(
250        &self,
251        bytes: OwnedBuffer,
252    ) -> Result<Arc<Artifact>, DeserializeError> {
253        unsafe { Ok(Arc::new(Artifact::deserialize_unchecked(self, bytes)?)) }
254    }
255
256    /// Deserializes a WebAssembly module which was previously serialized with
257    /// [`wasmer::Module::serialize`].
258    ///
259    /// # Safety
260    ///
261    /// See [`Artifact::deserialize`].
262    #[cfg(not(target_arch = "wasm32"))]
263    pub unsafe fn deserialize(
264        &self,
265        bytes: OwnedBuffer,
266    ) -> Result<Arc<Artifact>, DeserializeError> {
267        unsafe { Ok(Arc::new(Artifact::deserialize(self, bytes)?)) }
268    }
269
270    /// Deserializes a WebAssembly module from a path.
271    ///
272    /// # Safety
273    /// See [`Artifact::deserialize`].
274    #[cfg(not(target_arch = "wasm32"))]
275    pub unsafe fn deserialize_from_file(
276        &self,
277        file_ref: &Path,
278    ) -> Result<Arc<Artifact>, DeserializeError> {
279        unsafe {
280            let mut file = std::fs::File::open(file_ref)?;
281            let mut magic = [0; 4];
282            let is_elf = file.read_exact(&mut magic).is_ok() && magic == object::elf::ELFMAG;
283            if is_elf {
284                return Ok(Arc::new(Artifact::deserialize_file(self, file_ref)?));
285            }
286            file.rewind()?;
287            self.deserialize(
288                OwnedBuffer::from_file(&file)
289                    .map_err(|e| DeserializeError::Generic(e.to_string()))?,
290            )
291        }
292    }
293
294    /// Deserialize from a file path.
295    ///
296    /// # Safety
297    ///
298    /// See [`Artifact::deserialize_unchecked`].
299    #[cfg(not(target_arch = "wasm32"))]
300    pub unsafe fn deserialize_from_file_unchecked(
301        &self,
302        file_ref: &Path,
303    ) -> Result<Arc<Artifact>, DeserializeError> {
304        unsafe {
305            let file = std::fs::File::open(file_ref)?;
306            self.deserialize_unchecked(
307                OwnedBuffer::from_file(&file)
308                    .map_err(|e| DeserializeError::Generic(e.to_string()))?,
309            )
310        }
311    }
312
313    /// A unique identifier for this object.
314    ///
315    /// This exists to allow us to compare two Engines for equality. Otherwise,
316    /// comparing two trait objects unsafely relies on implementation details
317    /// of trait representation.
318    pub fn id(&self) -> &EngineId {
319        &self.engine_id
320    }
321
322    /// Clone the engine
323    pub fn cloned(&self) -> Self {
324        self.clone()
325    }
326
327    /// Attach a Tunable to this engine
328    #[cfg(not(target_arch = "wasm32"))]
329    pub fn set_tunables(&mut self, tunables: impl Tunables + Send + Sync + 'static) {
330        self.tunables = Arc::new(tunables);
331    }
332
333    /// Get a reference to attached Tunable of this engine
334    #[cfg(not(target_arch = "wasm32"))]
335    pub fn tunables(&self) -> &dyn Tunables {
336        self.tunables.as_ref()
337    }
338
339    /// Add suggested optimizations to this engine.
340    ///
341    /// # Note
342    ///
343    /// Not every backend supports every optimization. This function may fail (i.e. not set the
344    /// suggested optimizations) silently if the underlying engine backend does not support one or
345    /// more optimizations.
346    pub fn with_opts(
347        &mut self,
348        _suggested_opts: &wasmer_types::target::UserCompilerOptimizations,
349    ) -> Result<(), CompileError> {
350        #[cfg(feature = "compiler")]
351        {
352            let mut i = self.inner_mut();
353            if let Some(ref mut c) = i.compiler {
354                c.with_opts(_suggested_opts)?;
355            }
356        }
357
358        Ok(())
359    }
360}
361
362impl std::fmt::Debug for Engine {
363    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
364        write!(f, "{}", self.deterministic_id())
365    }
366}
367
368/// The inner contents of `Engine`
369pub struct EngineInner {
370    #[cfg(feature = "compiler")]
371    /// The compiler and cpu features
372    compiler: Option<Box<dyn Compiler>>,
373    #[cfg(feature = "compiler")]
374    /// The compiler and cpu features
375    features: Features,
376    /// The code memory is responsible of publishing the compiled
377    /// functions to memory.
378    #[cfg(not(target_arch = "wasm32"))]
379    code_memory: Vec<CodeMemory>,
380    /// Memory-mapped ELF artifact image, produced by `--experimental-artifact`.
381    #[cfg(not(target_arch = "wasm32"))]
382    elf_mapped_binary: Vec<MemoryMappedBinary>,
383    /// The signature registry is used mainly to operate with trampolines
384    /// performantly.
385    #[cfg(not(target_arch = "wasm32"))]
386    signatures: SignatureRegistry,
387}
388
389impl std::fmt::Debug for EngineInner {
390    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
391        let mut formatter = f.debug_struct("EngineInner");
392        #[cfg(feature = "compiler")]
393        {
394            formatter.field("compiler", &self.compiler);
395            formatter.field("features", &self.features);
396        }
397
398        #[cfg(not(target_arch = "wasm32"))]
399        {
400            formatter.field("signatures", &self.signatures);
401        }
402
403        formatter.finish()
404    }
405}
406
407impl EngineInner {
408    /// Gets the compiler associated to this engine.
409    #[cfg(feature = "compiler")]
410    pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> {
411        match self.compiler.as_ref() {
412            None => Err(CompileError::Codegen(
413                "No compiler compiled into executable".to_string(),
414            )),
415            Some(compiler) => Ok(&**compiler),
416        }
417    }
418
419    /// Validate the module
420    #[cfg(feature = "compiler")]
421    pub fn validate(&self, data: &[u8]) -> Result<(), CompileError> {
422        let compiler = self.compiler()?;
423        compiler.validate_module(&self.features, data)
424    }
425
426    /// The Wasm features
427    #[cfg(feature = "compiler")]
428    pub fn features(&self) -> &Features {
429        &self.features
430    }
431
432    /// Allocate compiled functions into memory
433    #[cfg(not(target_arch = "wasm32"))]
434    #[allow(clippy::type_complexity)]
435    pub(crate) fn allocate<'a, FunctionBody, CustomSection>(
436        &'a mut self,
437        _module: &wasmer_types::ModuleInfo,
438        functions: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
439        function_call_trampolines: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
440        dynamic_function_trampolines: impl ExactSizeIterator<Item = &'a FunctionBody> + 'a,
441        custom_sections: impl ExactSizeIterator<Item = &'a CustomSection> + Clone + 'a,
442    ) -> Result<
443        (
444            PrimaryMap<LocalFunctionIndex, FunctionExtent>,
445            PrimaryMap<SignatureIndex, VMTrampoline>,
446            PrimaryMap<FunctionIndex, FunctionBodyPtr>,
447            PrimaryMap<SectionIndex, SectionBodyPtr>,
448        ),
449        CompileError,
450    >
451    where
452        FunctionBody: FunctionBodyLike<'a> + 'a,
453        CustomSection: CustomSectionLike<'a> + 'a,
454    {
455        let functions_len = functions.len();
456        let function_call_trampolines_len = function_call_trampolines.len();
457
458        let function_bodies = functions
459            .chain(function_call_trampolines)
460            .chain(dynamic_function_trampolines)
461            .collect::<Vec<_>>();
462        let (executable_sections, data_sections): (Vec<_>, _) = custom_sections
463            .clone()
464            .partition(|section| section.protection() == CustomSectionProtection::ReadExecute);
465        self.code_memory.push(CodeMemory::new());
466
467        let (mut allocated_functions, allocated_executable_sections, allocated_data_sections) =
468            self.code_memory
469                .last_mut()
470                .unwrap()
471                .allocate(
472                    function_bodies.as_slice(),
473                    executable_sections.as_slice(),
474                    data_sections.as_slice(),
475                )
476                .map_err(|message| {
477                    CompileError::Resource(format!(
478                        "failed to allocate memory for functions: {message}",
479                    ))
480                })?;
481
482        let allocated_functions_result = allocated_functions
483            .drain(0..functions_len)
484            .map(|slice| FunctionExtent {
485                ptr: FunctionBodyPtr(slice.as_ptr()),
486                length: slice.len(),
487            })
488            .collect::<PrimaryMap<LocalFunctionIndex, _>>();
489
490        let mut allocated_function_call_trampolines: PrimaryMap<SignatureIndex, VMTrampoline> =
491            PrimaryMap::new();
492        for ptr in allocated_functions
493            .drain(0..function_call_trampolines_len)
494            .map(|slice| slice.as_ptr())
495        {
496            let trampoline =
497                unsafe { std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(ptr) };
498            allocated_function_call_trampolines.push(trampoline);
499        }
500
501        let allocated_dynamic_function_trampolines = allocated_functions
502            .drain(..)
503            .map(|slice| FunctionBodyPtr(slice.as_ptr()))
504            .collect::<PrimaryMap<FunctionIndex, _>>();
505
506        let mut exec_iter = allocated_executable_sections.iter();
507        let mut data_iter = allocated_data_sections.iter();
508        let allocated_custom_sections = custom_sections
509            .map(|section| {
510                SectionBodyPtr(
511                    if section.protection() == CustomSectionProtection::ReadExecute {
512                        exec_iter.next()
513                    } else {
514                        data_iter.next()
515                    }
516                    .unwrap()
517                    .as_ptr(),
518                )
519            })
520            .collect::<PrimaryMap<SectionIndex, _>>();
521        Ok((
522            allocated_functions_result,
523            allocated_function_call_trampolines,
524            allocated_dynamic_function_trampolines,
525            allocated_custom_sections,
526        ))
527    }
528
529    #[cfg(not(target_arch = "wasm32"))]
530    /// Make memory containing compiled code executable.
531    pub(crate) fn publish_compiled_code(&mut self) {
532        self.code_memory.last_mut().unwrap().publish();
533    }
534
535    #[cfg(not(target_arch = "wasm32"))]
536    #[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
537    /// Register DWARF-type exception handling information associated with the code.
538    pub(crate) fn publish_eh_frame(&mut self, eh_frame: Option<&[u8]>) -> Result<(), CompileError> {
539        self.code_memory
540            .last_mut()
541            .unwrap()
542            .unwind_registry_mut()
543            .publish_eh_frame(eh_frame)
544            .map_err(|e| {
545                CompileError::Resource(format!("Error while publishing the unwind code: {e}"))
546            })?;
547        Ok(())
548    }
549    #[cfg(all(target_os = "macos", target_arch = "aarch64"))]
550    /// Register macos-specific exception handling information associated with the code.
551    pub(crate) fn publish_compact_unwind(
552        &mut self,
553        compact_unwind: &[u8],
554        eh_personality_addr_in_got: Option<usize>,
555    ) -> Result<(), CompileError> {
556        self.code_memory
557            .last_mut()
558            .unwrap()
559            .unwind_registry_mut()
560            .publish_compact_unwind(compact_unwind, eh_personality_addr_in_got)
561            .map_err(|e| {
562                CompileError::Resource(format!("Error while publishing the unwind code: {e}"))
563            })?;
564        Ok(())
565    }
566
567    /// Memory-map a compiled ELF artifact image, keeping the mapping alive
568    /// for the lifetime of the engine. Returns the base address of the
569    /// mapping, which section/symbol offsets from the image are relative to.
570    #[cfg(not(target_arch = "wasm32"))]
571    pub(crate) fn map_elf_binary<'a, R: object::ReadRef<'a>>(
572        &mut self,
573        object_file: &object::File<'a, R>,
574        data: &[u8],
575    ) -> Result<*mut c_void, CompileError> {
576        let map = MemoryMappedBinary::try_from_bytes(object_file, data)
577            .map_err(CompileError::Resource)?;
578        let base = map.base();
579        self.elf_mapped_binary.push(map);
580        Ok(base)
581    }
582
583    /// Memory-map a compiled ELF artifact directly from a file.
584    #[cfg(all(not(target_arch = "wasm32"), unix))]
585    pub(crate) fn map_elf_binary_file<'a, R: object::ReadRef<'a>>(
586        &mut self,
587        object_file: &object::File<'a, R>,
588        file: RawFd,
589    ) -> Result<*mut c_void, CompileError> {
590        let map =
591            MemoryMappedBinary::try_from_file(object_file, file).map_err(CompileError::Resource)?;
592        let base = map.base();
593        self.elf_mapped_binary.push(map);
594        Ok(base)
595    }
596
597    /// Register DWARF-type exception handling information associated with the code.
598    #[cfg(not(target_arch = "wasm32"))]
599    pub(crate) fn publish_elf_eh_frame(
600        &mut self,
601        address: u64,
602        size: u64,
603    ) -> Result<(), CompileError> {
604        self.elf_mapped_binary
605            .last_mut()
606            .unwrap()
607            .publish_eh_frame_section(address, size)
608            .map_err(|e| {
609                CompileError::Resource(format!("Error while publishing the unwind code: {e}"))
610            })
611    }
612
613    /// Shared signature registry.
614    #[cfg(not(target_arch = "wasm32"))]
615    pub fn signatures(&self) -> &SignatureRegistry {
616        &self.signatures
617    }
618
619    #[cfg(not(target_arch = "wasm32"))]
620    /// Register the frame info for the code memory
621    pub(crate) fn register_frame_info(&mut self, frame_info: GlobalFrameInfoRegistration) {
622        self.code_memory
623            .last_mut()
624            .unwrap()
625            .register_frame_info(frame_info);
626    }
627
628    #[cfg(not(target_arch = "wasm32"))]
629    /// Register the frame info for the most recently mapped ELF binary.
630    pub(crate) fn register_elf_frame_info(&mut self, frame_info: GlobalFrameInfoRegistration) {
631        self.elf_mapped_binary
632            .last_mut()
633            .unwrap()
634            .register_frame_info(frame_info);
635    }
636
637    #[cfg(all(not(target_arch = "wasm32"), feature = "compiler"))]
638    pub(crate) fn register_perfmap(
639        &self,
640        finished_functions: &PrimaryMap<LocalFunctionIndex, FunctionExtent>,
641        module_info: &ModuleInfo,
642    ) -> Result<(), CompileError> {
643        if self
644            .compiler
645            .as_ref()
646            .is_some_and(|v| v.get_perfmap_enabled())
647        {
648            use std::fs::OpenOptions;
649
650            let filename = format!("/tmp/perf-{}.map", std::process::id());
651            // We might be loading shared libraries and so we must append to the file.
652            let file = OpenOptions::new()
653                .append(true)
654                .create(true)
655                .open(&filename)
656                .map_err(|e| {
657                    CompileError::Codegen(format!("failed to open perf map file {filename}: {e}"))
658                })?;
659            let mut file = std::io::BufWriter::new(file);
660
661            for (func_index, code) in finished_functions.iter() {
662                let func_index = module_info.func_index(func_index);
663                if let Some(func_name) = module_info.function_names.get(&func_index) {
664                    let sanitized_name = func_name.replace(['\n', '\r'], "_");
665                    let line = format!(
666                        "{:p} {:x} {sanitized_name}\n",
667                        code.ptr.0 as *const _, code.length
668                    );
669                    write!(file, "{line}").map_err(|e| CompileError::Codegen(e.to_string()))?;
670                }
671            }
672
673            file.flush()
674                .map_err(|e| CompileError::Codegen(e.to_string()))?;
675        }
676
677        Ok(())
678    }
679}
680
681#[cfg(feature = "compiler")]
682impl From<Box<dyn CompilerConfig>> for Engine {
683    fn from(config: Box<dyn CompilerConfig>) -> Self {
684        EngineBuilder::new(config).engine()
685    }
686}
687
688impl From<EngineBuilder> for Engine {
689    fn from(engine_builder: EngineBuilder) -> Self {
690        engine_builder.engine()
691    }
692}
693
694impl From<&Self> for Engine {
695    fn from(engine_ref: &Self) -> Self {
696        engine_ref.cloned()
697    }
698}
699
700#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
701#[repr(transparent)]
702/// A unique identifier for an Engine.
703pub struct EngineId {
704    id: usize,
705}
706
707impl EngineId {
708    /// Format this identifier as a string.
709    pub fn id(&self) -> String {
710        format!("{}", self.id)
711    }
712}
713
714impl Clone for EngineId {
715    fn clone(&self) -> Self {
716        Self::default()
717    }
718}
719
720impl Default for EngineId {
721    fn default() -> Self {
722        static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
723        Self {
724            id: NEXT_ID.fetch_add(1, SeqCst),
725        }
726    }
727}