deterministic id generator per module (#5811)
* deterministic id generator per module Signed-off-by: Jess Frazelle <github@jessfraz.com> * non Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * do not remake the planes if they are alreaady made; Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * do not remake the planes if they are alreaady made; Signed-off-by: Jess Frazelle <github@jessfraz.com> * clippy Signed-off-by: Jess Frazelle <github@jessfraz.com> --------- Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
		@ -1153,7 +1153,7 @@ fn find_examples(text: &str, filename: &str) -> Vec<(String, String)> {
 | 
			
		||||
async fn run_example(text: &str) -> Result<()> {
 | 
			
		||||
    let program = crate::Program::parse_no_errs(text)?;
 | 
			
		||||
    let ctx = ExecutorContext::new_with_default_client(crate::UnitLength::Mm).await?;
 | 
			
		||||
    let mut exec_state = crate::execution::ExecState::new(&ctx.settings);
 | 
			
		||||
    let mut exec_state = crate::execution::ExecState::new(&ctx);
 | 
			
		||||
    ctx.run(&program, &mut exec_state).await?;
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -378,22 +378,8 @@ impl EngineManager for EngineConnection {
 | 
			
		||||
        original
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn default_planes(
 | 
			
		||||
        &self,
 | 
			
		||||
        id_generator: &mut IdGenerator,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<DefaultPlanes, KclError> {
 | 
			
		||||
        {
 | 
			
		||||
            let opt = self.default_planes.read().await.as_ref().cloned();
 | 
			
		||||
            if let Some(planes) = opt {
 | 
			
		||||
                return Ok(planes);
 | 
			
		||||
            }
 | 
			
		||||
        } // drop the read lock
 | 
			
		||||
 | 
			
		||||
        let new_planes = self.new_default_planes(id_generator, source_range).await?;
 | 
			
		||||
        *self.default_planes.write().await = Some(new_planes.clone());
 | 
			
		||||
 | 
			
		||||
        Ok(new_planes)
 | 
			
		||||
    fn get_default_planes(&self) -> Arc<RwLock<Option<DefaultPlanes>>> {
 | 
			
		||||
        self.default_planes.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn clear_scene_post_hook(
 | 
			
		||||
 | 
			
		||||
@ -30,6 +30,8 @@ pub struct EngineConnection {
 | 
			
		||||
    batch_end: Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
 | 
			
		||||
    artifact_commands: Arc<RwLock<Vec<ArtifactCommand>>>,
 | 
			
		||||
    execution_kind: Arc<RwLock<ExecutionKind>>,
 | 
			
		||||
    /// The default planes for the scene.
 | 
			
		||||
    default_planes: Arc<RwLock<Option<DefaultPlanes>>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl EngineConnection {
 | 
			
		||||
@ -39,6 +41,7 @@ impl EngineConnection {
 | 
			
		||||
            batch_end: Arc::new(RwLock::new(IndexMap::new())),
 | 
			
		||||
            artifact_commands: Arc::new(RwLock::new(Vec::new())),
 | 
			
		||||
            execution_kind: Default::default(),
 | 
			
		||||
            default_planes: Default::default(),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -73,12 +76,8 @@ impl crate::engine::EngineManager for EngineConnection {
 | 
			
		||||
        original
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn default_planes(
 | 
			
		||||
        &self,
 | 
			
		||||
        _id_generator: &mut IdGenerator,
 | 
			
		||||
        _source_range: SourceRange,
 | 
			
		||||
    ) -> Result<DefaultPlanes, KclError> {
 | 
			
		||||
        Ok(DefaultPlanes::default())
 | 
			
		||||
    fn get_default_planes(&self) -> Arc<RwLock<Option<DefaultPlanes>>> {
 | 
			
		||||
        self.default_planes.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn clear_scene_post_hook(
 | 
			
		||||
 | 
			
		||||
@ -31,12 +31,6 @@ extern "C" {
 | 
			
		||||
        idToRangeStr: String,
 | 
			
		||||
    ) -> Result<js_sys::Promise, js_sys::Error>;
 | 
			
		||||
 | 
			
		||||
    #[wasm_bindgen(method, js_name = wasmGetDefaultPlanes, catch)]
 | 
			
		||||
    fn get_default_planes(this: &EngineCommandManager) -> Result<js_sys::Promise, js_sys::Error>;
 | 
			
		||||
 | 
			
		||||
    #[wasm_bindgen(method, js_name = clearDefaultPlanes, catch)]
 | 
			
		||||
    fn clear_default_planes(this: &EngineCommandManager) -> Result<(), js_sys::Error>;
 | 
			
		||||
 | 
			
		||||
    #[wasm_bindgen(method, js_name = startNewSession, catch)]
 | 
			
		||||
    fn start_new_session(this: &EngineCommandManager) -> Result<js_sys::Promise, js_sys::Error>;
 | 
			
		||||
}
 | 
			
		||||
@ -49,6 +43,8 @@ pub struct EngineConnection {
 | 
			
		||||
    responses: Arc<RwLock<IndexMap<Uuid, WebSocketResponse>>>,
 | 
			
		||||
    artifact_commands: Arc<RwLock<Vec<ArtifactCommand>>>,
 | 
			
		||||
    execution_kind: Arc<RwLock<ExecutionKind>>,
 | 
			
		||||
    /// The default planes for the scene.
 | 
			
		||||
    default_planes: Arc<RwLock<Option<DefaultPlanes>>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Safety: WebAssembly will only ever run in a single-threaded context.
 | 
			
		||||
@ -65,6 +61,7 @@ impl EngineConnection {
 | 
			
		||||
            responses: Arc::new(RwLock::new(IndexMap::new())),
 | 
			
		||||
            artifact_commands: Arc::new(RwLock::new(Vec::new())),
 | 
			
		||||
            execution_kind: Default::default(),
 | 
			
		||||
            default_planes: Default::default(),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -160,59 +157,18 @@ impl crate::engine::EngineManager for EngineConnection {
 | 
			
		||||
        original
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn default_planes(
 | 
			
		||||
        &self,
 | 
			
		||||
        _id_generator: &mut IdGenerator,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<DefaultPlanes, KclError> {
 | 
			
		||||
        // Get the default planes.
 | 
			
		||||
        let promise = self.manager.get_default_planes().map_err(|e| {
 | 
			
		||||
            KclError::Engine(KclErrorDetails {
 | 
			
		||||
                message: e.to_string().into(),
 | 
			
		||||
                source_ranges: vec![source_range],
 | 
			
		||||
            })
 | 
			
		||||
        })?;
 | 
			
		||||
 | 
			
		||||
        let value = crate::wasm::JsFuture::from(promise).await.map_err(|e| {
 | 
			
		||||
            KclError::Engine(KclErrorDetails {
 | 
			
		||||
                message: format!("Failed to wait for promise from get default planes: {:?}", e),
 | 
			
		||||
                source_ranges: vec![source_range],
 | 
			
		||||
            })
 | 
			
		||||
        })?;
 | 
			
		||||
 | 
			
		||||
        // Parse the value as a string.
 | 
			
		||||
        let s = value.as_string().ok_or_else(|| {
 | 
			
		||||
            KclError::Engine(KclErrorDetails {
 | 
			
		||||
                message: format!(
 | 
			
		||||
                    "Failed to get string from response from get default planes: `{:?}`",
 | 
			
		||||
                    value
 | 
			
		||||
                ),
 | 
			
		||||
                source_ranges: vec![source_range],
 | 
			
		||||
            })
 | 
			
		||||
        })?;
 | 
			
		||||
 | 
			
		||||
        // Deserialize the response.
 | 
			
		||||
        let default_planes: DefaultPlanes = serde_json::from_str(&s).map_err(|e| {
 | 
			
		||||
            KclError::Engine(KclErrorDetails {
 | 
			
		||||
                message: format!("Failed to deserialize default planes: {:?}", e),
 | 
			
		||||
                source_ranges: vec![source_range],
 | 
			
		||||
            })
 | 
			
		||||
        })?;
 | 
			
		||||
 | 
			
		||||
        Ok(default_planes)
 | 
			
		||||
    fn get_default_planes(&self) -> Arc<RwLock<Option<DefaultPlanes>>> {
 | 
			
		||||
        self.default_planes.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn clear_scene_post_hook(
 | 
			
		||||
        &self,
 | 
			
		||||
        _id_generator: &mut IdGenerator,
 | 
			
		||||
        id_generator: &mut IdGenerator,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<(), KclError> {
 | 
			
		||||
        self.manager.clear_default_planes().map_err(|e| {
 | 
			
		||||
            KclError::Engine(KclErrorDetails {
 | 
			
		||||
                message: e.to_string().into(),
 | 
			
		||||
                source_ranges: vec![source_range],
 | 
			
		||||
            })
 | 
			
		||||
        })?;
 | 
			
		||||
        // Remake the default planes, since they would have been removed after the scene was cleared.
 | 
			
		||||
        let new_planes = self.new_default_planes(id_generator, source_range).await?;
 | 
			
		||||
        *self.default_planes.write().await = Some(new_planes);
 | 
			
		||||
 | 
			
		||||
        // Start a new session.
 | 
			
		||||
        let promise = self.manager.start_new_session().map_err(|e| {
 | 
			
		||||
 | 
			
		||||
@ -95,11 +95,26 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
 | 
			
		||||
    async fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind;
 | 
			
		||||
 | 
			
		||||
    /// Get the default planes.
 | 
			
		||||
    fn get_default_planes(&self) -> Arc<RwLock<Option<DefaultPlanes>>>;
 | 
			
		||||
 | 
			
		||||
    /// Get the default planes, creating them if they don't exist.
 | 
			
		||||
    async fn default_planes(
 | 
			
		||||
        &self,
 | 
			
		||||
        id_generator: &mut IdGenerator,
 | 
			
		||||
        _source_range: SourceRange,
 | 
			
		||||
    ) -> Result<DefaultPlanes, crate::errors::KclError>;
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<DefaultPlanes, KclError> {
 | 
			
		||||
        {
 | 
			
		||||
            let opt = self.get_default_planes().read().await.as_ref().cloned();
 | 
			
		||||
            if let Some(planes) = opt {
 | 
			
		||||
                return Ok(planes);
 | 
			
		||||
            }
 | 
			
		||||
        } // drop the read lock
 | 
			
		||||
 | 
			
		||||
        let new_planes = self.new_default_planes(id_generator, source_range).await?;
 | 
			
		||||
        *self.get_default_planes().write().await = Some(new_planes.clone());
 | 
			
		||||
 | 
			
		||||
        Ok(new_planes)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Helpers to be called after clearing a scene.
 | 
			
		||||
    /// (These really only apply to wasm for now).
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ use thiserror::Error;
 | 
			
		||||
use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    execution::{ArtifactCommand, ArtifactGraph, Operation},
 | 
			
		||||
    execution::{ArtifactCommand, ArtifactGraph, DefaultPlanes, Operation},
 | 
			
		||||
    lsp::IntoDiagnostic,
 | 
			
		||||
    modules::{ModulePath, ModuleSource},
 | 
			
		||||
    source_range::SourceRange,
 | 
			
		||||
@ -131,6 +131,7 @@ pub struct KclErrorWithOutputs {
 | 
			
		||||
    pub artifact_graph: ArtifactGraph,
 | 
			
		||||
    pub filenames: IndexMap<ModuleId, ModulePath>,
 | 
			
		||||
    pub source_files: IndexMap<ModuleId, ModuleSource>,
 | 
			
		||||
    pub default_planes: Option<DefaultPlanes>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl KclErrorWithOutputs {
 | 
			
		||||
@ -141,6 +142,7 @@ impl KclErrorWithOutputs {
 | 
			
		||||
        artifact_graph: ArtifactGraph,
 | 
			
		||||
        filenames: IndexMap<ModuleId, ModulePath>,
 | 
			
		||||
        source_files: IndexMap<ModuleId, ModuleSource>,
 | 
			
		||||
        default_planes: Option<DefaultPlanes>,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            error,
 | 
			
		||||
@ -149,6 +151,7 @@ impl KclErrorWithOutputs {
 | 
			
		||||
            artifact_graph,
 | 
			
		||||
            filenames,
 | 
			
		||||
            source_files,
 | 
			
		||||
            default_planes,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn no_outputs(error: KclError) -> Self {
 | 
			
		||||
@ -159,6 +162,7 @@ impl KclErrorWithOutputs {
 | 
			
		||||
            artifact_graph: Default::default(),
 | 
			
		||||
            filenames: Default::default(),
 | 
			
		||||
            source_files: Default::default(),
 | 
			
		||||
            default_planes: Default::default(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn into_miette_report_with_outputs(self, code: &str) -> anyhow::Result<ReportWithOutputs> {
 | 
			
		||||
 | 
			
		||||
@ -94,6 +94,7 @@ impl ExecutorContext {
 | 
			
		||||
        exec_state: &mut ExecState,
 | 
			
		||||
        exec_kind: ExecutionKind,
 | 
			
		||||
        preserve_mem: bool,
 | 
			
		||||
        module_id: ModuleId,
 | 
			
		||||
        path: &ModulePath,
 | 
			
		||||
    ) -> Result<(Option<KclValue>, EnvironmentRef, Vec<String>), KclError> {
 | 
			
		||||
        crate::log::log(format!("enter module {path} {}", exec_state.stack()));
 | 
			
		||||
@ -101,7 +102,12 @@ impl ExecutorContext {
 | 
			
		||||
        let old_units = exec_state.length_unit();
 | 
			
		||||
        let original_execution = self.engine.replace_execution_kind(exec_kind).await;
 | 
			
		||||
 | 
			
		||||
        let mut local_state = ModuleState::new(&self.settings, path.std_path(), exec_state.stack().memory.clone());
 | 
			
		||||
        let mut local_state = ModuleState::new(
 | 
			
		||||
            &self.settings,
 | 
			
		||||
            path.std_path(),
 | 
			
		||||
            exec_state.stack().memory.clone(),
 | 
			
		||||
            Some(module_id),
 | 
			
		||||
        );
 | 
			
		||||
        if !preserve_mem {
 | 
			
		||||
            std::mem::swap(&mut exec_state.mod_local, &mut local_state);
 | 
			
		||||
        }
 | 
			
		||||
@ -452,7 +458,7 @@ impl ExecutorContext {
 | 
			
		||||
            ModuleRepr::Root => Err(exec_state.circular_import_error(&path, source_range)),
 | 
			
		||||
            ModuleRepr::Kcl(_, Some((env_ref, items))) => Ok((*env_ref, items.clone())),
 | 
			
		||||
            ModuleRepr::Kcl(program, cache) => self
 | 
			
		||||
                .exec_module_from_ast(program, &path, exec_state, exec_kind, source_range)
 | 
			
		||||
                .exec_module_from_ast(program, module_id, &path, exec_state, exec_kind, source_range)
 | 
			
		||||
                .await
 | 
			
		||||
                .map(|(_, er, items)| {
 | 
			
		||||
                    *cache = Some((er, items.clone()));
 | 
			
		||||
@ -483,7 +489,7 @@ impl ExecutorContext {
 | 
			
		||||
        let result = match &repr {
 | 
			
		||||
            ModuleRepr::Root => Err(exec_state.circular_import_error(&path, source_range)),
 | 
			
		||||
            ModuleRepr::Kcl(program, _) => self
 | 
			
		||||
                .exec_module_from_ast(program, &path, exec_state, exec_kind, source_range)
 | 
			
		||||
                .exec_module_from_ast(program, module_id, &path, exec_state, exec_kind, source_range)
 | 
			
		||||
                .await
 | 
			
		||||
                .map(|(val, _, _)| val),
 | 
			
		||||
            ModuleRepr::Foreign(geom) => super::import::send_to_engine(geom.clone(), self)
 | 
			
		||||
@ -499,13 +505,16 @@ impl ExecutorContext {
 | 
			
		||||
    async fn exec_module_from_ast(
 | 
			
		||||
        &self,
 | 
			
		||||
        program: &Node<Program>,
 | 
			
		||||
        module_id: ModuleId,
 | 
			
		||||
        path: &ModulePath,
 | 
			
		||||
        exec_state: &mut ExecState,
 | 
			
		||||
        exec_kind: ExecutionKind,
 | 
			
		||||
        source_range: SourceRange,
 | 
			
		||||
    ) -> Result<(Option<KclValue>, EnvironmentRef, Vec<String>), KclError> {
 | 
			
		||||
        exec_state.global.mod_loader.enter_module(path);
 | 
			
		||||
        let result = self.exec_module_body(program, exec_state, exec_kind, false, path).await;
 | 
			
		||||
        let result = self
 | 
			
		||||
            .exec_module_body(program, exec_state, exec_kind, false, module_id, path)
 | 
			
		||||
            .await;
 | 
			
		||||
        exec_state.global.mod_loader.leave_module(path);
 | 
			
		||||
 | 
			
		||||
        result.map_err(|err| {
 | 
			
		||||
@ -699,7 +708,7 @@ fn coerce(value: KclValue, ty: &Node<Type>, exec_state: &mut ExecState) -> Resul
 | 
			
		||||
                        meta: meta.clone(),
 | 
			
		||||
                    })?;
 | 
			
		||||
 | 
			
		||||
                let id = exec_state.global.id_generator.next_uuid();
 | 
			
		||||
                let id = exec_state.next_uuid();
 | 
			
		||||
                let plane = Plane {
 | 
			
		||||
                    id,
 | 
			
		||||
                    artifact_id: id.into(),
 | 
			
		||||
@ -1966,14 +1975,16 @@ impl FunctionSource {
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use std::sync::Arc;
 | 
			
		||||
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::{
 | 
			
		||||
        execution::{memory::Stack, parse_execute},
 | 
			
		||||
        execution::{memory::Stack, parse_execute, ContextType},
 | 
			
		||||
        parsing::ast::types::{DefaultParamVal, Identifier, Parameter},
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_assign_args_to_params() {
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
    async fn test_assign_args_to_params() {
 | 
			
		||||
        // Set up a little framework for this test.
 | 
			
		||||
        fn mem(number: usize) -> KclValue {
 | 
			
		||||
            KclValue::Number {
 | 
			
		||||
@ -2084,7 +2095,16 @@ mod test {
 | 
			
		||||
                digest: None,
 | 
			
		||||
            });
 | 
			
		||||
            let args = args.into_iter().map(Arg::synthetic).collect();
 | 
			
		||||
            let mut exec_state = ExecState::new(&Default::default());
 | 
			
		||||
            let exec_ctxt = ExecutorContext {
 | 
			
		||||
                engine: Arc::new(Box::new(
 | 
			
		||||
                    crate::engine::conn_mock::EngineConnection::new().await.unwrap(),
 | 
			
		||||
                )),
 | 
			
		||||
                fs: Arc::new(crate::fs::FileManager::new()),
 | 
			
		||||
                stdlib: Arc::new(crate::std::StdLib::new()),
 | 
			
		||||
                settings: Default::default(),
 | 
			
		||||
                context_type: ContextType::Mock,
 | 
			
		||||
            };
 | 
			
		||||
            let mut exec_state = ExecState::new(&exec_ctxt);
 | 
			
		||||
            exec_state.mod_local.stack = Stack::new_for_tests();
 | 
			
		||||
            let actual = assign_args_to_params(func_expr, args, &mut exec_state).map(|_| exec_state.mod_local.stack);
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
 | 
			
		||||
@ -370,7 +370,7 @@ impl Plane {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn from_plane_data(value: PlaneData, exec_state: &mut ExecState) -> Self {
 | 
			
		||||
        let id = exec_state.global.id_generator.next_uuid();
 | 
			
		||||
        let id = exec_state.next_uuid();
 | 
			
		||||
        match value {
 | 
			
		||||
            PlaneData::XY => Plane {
 | 
			
		||||
                id,
 | 
			
		||||
@ -443,17 +443,20 @@ impl Plane {
 | 
			
		||||
                x_axis,
 | 
			
		||||
                y_axis,
 | 
			
		||||
                z_axis,
 | 
			
		||||
            } => Plane {
 | 
			
		||||
                id,
 | 
			
		||||
                artifact_id: id.into(),
 | 
			
		||||
                origin,
 | 
			
		||||
                x_axis,
 | 
			
		||||
                y_axis,
 | 
			
		||||
                z_axis,
 | 
			
		||||
                value: PlaneType::Custom,
 | 
			
		||||
                units: exec_state.length_unit(),
 | 
			
		||||
                meta: vec![],
 | 
			
		||||
            },
 | 
			
		||||
            } => {
 | 
			
		||||
                let id = exec_state.next_uuid();
 | 
			
		||||
                Plane {
 | 
			
		||||
                    id,
 | 
			
		||||
                    artifact_id: id.into(),
 | 
			
		||||
                    origin,
 | 
			
		||||
                    x_axis,
 | 
			
		||||
                    y_axis,
 | 
			
		||||
                    z_axis,
 | 
			
		||||
                    value: PlaneType::Custom,
 | 
			
		||||
                    units: exec_state.length_unit(),
 | 
			
		||||
                    meta: vec![],
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										83
									
								
								rust/kcl-lib/src/execution/id_generator.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								rust/kcl-lib/src/execution/id_generator.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,83 @@
 | 
			
		||||
//! A generator for ArtifactIds that can be stable across executions.
 | 
			
		||||
 | 
			
		||||
use crate::execution::ModuleId;
 | 
			
		||||
 | 
			
		||||
const NAMESPACE_KCL: uuid::Uuid = uuid::uuid!("efcd6508-4ce6-4a09-8317-e6a6994a3cd7");
 | 
			
		||||
 | 
			
		||||
/// A generator for ArtifactIds that can be stable across executions.
 | 
			
		||||
#[derive(Debug, Clone, Default, PartialEq)]
 | 
			
		||||
pub struct IdGenerator {
 | 
			
		||||
    module_id: Option<ModuleId>,
 | 
			
		||||
    next_id: u64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl IdGenerator {
 | 
			
		||||
    pub fn new(module_id: Option<ModuleId>) -> Self {
 | 
			
		||||
        Self { module_id, next_id: 0 }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn next_uuid(&mut self) -> uuid::Uuid {
 | 
			
		||||
        let next_id = self.next_id;
 | 
			
		||||
 | 
			
		||||
        let next = format!(
 | 
			
		||||
            "{} {}",
 | 
			
		||||
            self.module_id.map(|id| id.to_string()).unwrap_or("none".to_string()),
 | 
			
		||||
            next_id
 | 
			
		||||
        );
 | 
			
		||||
        let next_uuid = uuid::Uuid::new_v5(&NAMESPACE_KCL, next.as_bytes());
 | 
			
		||||
 | 
			
		||||
        self.next_id += 1;
 | 
			
		||||
 | 
			
		||||
        next_uuid
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_id_generator() {
 | 
			
		||||
        let mut generator = IdGenerator::new(Some(ModuleId::default()));
 | 
			
		||||
 | 
			
		||||
        let uuid1 = generator.next_uuid();
 | 
			
		||||
        let uuid2 = generator.next_uuid();
 | 
			
		||||
 | 
			
		||||
        assert_ne!(uuid1, uuid2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    // Test that the same generator produces the same UUIDs.
 | 
			
		||||
    fn test_id_generator_stable() {
 | 
			
		||||
        let mut generator = IdGenerator::new(Some(ModuleId::default()));
 | 
			
		||||
 | 
			
		||||
        let uuid1 = generator.next_uuid();
 | 
			
		||||
        let uuid2 = generator.next_uuid();
 | 
			
		||||
 | 
			
		||||
        let mut generator = IdGenerator::new(Some(ModuleId::default()));
 | 
			
		||||
 | 
			
		||||
        let uuid3 = generator.next_uuid();
 | 
			
		||||
        let uuid4 = generator.next_uuid();
 | 
			
		||||
 | 
			
		||||
        assert_eq!(uuid1, uuid3);
 | 
			
		||||
        assert_eq!(uuid2, uuid4);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    // Generate 20 uuids and make sure all are unique.
 | 
			
		||||
    fn test_id_generator_unique() {
 | 
			
		||||
        let mut generator = IdGenerator::new(Some(ModuleId::default()));
 | 
			
		||||
 | 
			
		||||
        let mut uuids = Vec::new();
 | 
			
		||||
 | 
			
		||||
        for _ in 0..20 {
 | 
			
		||||
            uuids.push(generator.next_uuid());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for i in 0..uuids.len() {
 | 
			
		||||
            for j in i + 1..uuids.len() {
 | 
			
		||||
                assert_ne!(uuids[i], uuids[j]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -10,6 +10,7 @@ use cache::OldAstState;
 | 
			
		||||
pub use cache::{bust_cache, clear_mem_cache};
 | 
			
		||||
pub use cad_op::Operation;
 | 
			
		||||
pub use geometry::*;
 | 
			
		||||
pub use id_generator::IdGenerator;
 | 
			
		||||
pub(crate) use import::{
 | 
			
		||||
    import_foreign, send_to_engine as send_import_to_engine, PreImportedGeometry, ZOO_COORD_SYSTEM,
 | 
			
		||||
};
 | 
			
		||||
@ -25,7 +26,7 @@ use kittycad_modeling_cmds as kcmc;
 | 
			
		||||
pub use memory::EnvironmentRef;
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
pub use state::{ExecState, IdGenerator, MetaSettings};
 | 
			
		||||
pub use state::{ExecState, MetaSettings};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    engine::EngineManager,
 | 
			
		||||
@ -49,6 +50,7 @@ pub(crate) mod cache;
 | 
			
		||||
mod cad_op;
 | 
			
		||||
mod exec_ast;
 | 
			
		||||
mod geometry;
 | 
			
		||||
mod id_generator;
 | 
			
		||||
mod import;
 | 
			
		||||
pub(crate) mod kcl_value;
 | 
			
		||||
mod memory;
 | 
			
		||||
@ -72,6 +74,8 @@ pub struct ExecOutcome {
 | 
			
		||||
    pub errors: Vec<CompilationError>,
 | 
			
		||||
    /// File Names in module Id array index order
 | 
			
		||||
    pub filenames: IndexMap<ModuleId, ModulePath>,
 | 
			
		||||
    /// The default planes.
 | 
			
		||||
    pub default_planes: Option<DefaultPlanes>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
@ -367,22 +371,14 @@ impl ExecutorContext {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[cfg(target_arch = "wasm32")]
 | 
			
		||||
    pub async fn new(
 | 
			
		||||
        engine_manager: crate::engine::conn_wasm::EngineCommandManager,
 | 
			
		||||
        fs_manager: crate::fs::wasm::FileSystemManager,
 | 
			
		||||
        settings: ExecutorSettings,
 | 
			
		||||
    ) -> Result<Self, String> {
 | 
			
		||||
        Ok(ExecutorContext {
 | 
			
		||||
            engine: Arc::new(Box::new(
 | 
			
		||||
                crate::engine::conn_wasm::EngineConnection::new(engine_manager)
 | 
			
		||||
                    .await
 | 
			
		||||
                    .map_err(|e| format!("{:?}", e))?,
 | 
			
		||||
            )),
 | 
			
		||||
            fs: Arc::new(FileManager::new(fs_manager)),
 | 
			
		||||
    pub fn new(engine: Arc<Box<dyn EngineManager>>, fs: Arc<FileManager>, settings: ExecutorSettings) -> Self {
 | 
			
		||||
        ExecutorContext {
 | 
			
		||||
            engine,
 | 
			
		||||
            fs,
 | 
			
		||||
            stdlib: Arc::new(StdLib::new()),
 | 
			
		||||
            settings,
 | 
			
		||||
            context_type: ContextType::Live,
 | 
			
		||||
        })
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[cfg(not(target_arch = "wasm32"))]
 | 
			
		||||
@ -499,7 +495,7 @@ impl ExecutorContext {
 | 
			
		||||
        source_range: crate::execution::SourceRange,
 | 
			
		||||
    ) -> Result<(), KclError> {
 | 
			
		||||
        self.engine
 | 
			
		||||
            .clear_scene(&mut exec_state.global.id_generator, source_range)
 | 
			
		||||
            .clear_scene(&mut exec_state.mod_local.id_generator, source_range)
 | 
			
		||||
            .await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -518,7 +514,7 @@ impl ExecutorContext {
 | 
			
		||||
    ) -> Result<ExecOutcome, KclErrorWithOutputs> {
 | 
			
		||||
        assert!(self.is_mock());
 | 
			
		||||
 | 
			
		||||
        let mut exec_state = ExecState::new(&self.settings);
 | 
			
		||||
        let mut exec_state = ExecState::new(self);
 | 
			
		||||
        if use_prev_memory {
 | 
			
		||||
            match cache::read_old_memory().await {
 | 
			
		||||
                Some(mem) => *exec_state.mut_stack() = mem,
 | 
			
		||||
@ -539,7 +535,7 @@ impl ExecutorContext {
 | 
			
		||||
        // memory, not to the exec_state which is not cached for mock execution.
 | 
			
		||||
 | 
			
		||||
        let mut mem = exec_state.stack().clone();
 | 
			
		||||
        let outcome = exec_state.to_mock_wasm_outcome(result.0);
 | 
			
		||||
        let outcome = exec_state.to_mock_wasm_outcome(result.0).await;
 | 
			
		||||
 | 
			
		||||
        mem.squash_env(result.0);
 | 
			
		||||
        cache::write_old_memory(mem).await;
 | 
			
		||||
@ -607,13 +603,13 @@ impl ExecutorContext {
 | 
			
		||||
                        })
 | 
			
		||||
                        .await;
 | 
			
		||||
 | 
			
		||||
                        let outcome = old_state.to_wasm_outcome(result_env);
 | 
			
		||||
                        let outcome = old_state.to_wasm_outcome(result_env).await;
 | 
			
		||||
                        return Ok(outcome);
 | 
			
		||||
                    }
 | 
			
		||||
                    (true, program)
 | 
			
		||||
                }
 | 
			
		||||
                CacheResult::NoAction(false) => {
 | 
			
		||||
                    let outcome = old_state.to_wasm_outcome(result_env);
 | 
			
		||||
                    let outcome = old_state.to_wasm_outcome(result_env).await;
 | 
			
		||||
                    return Ok(outcome);
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
@ -621,7 +617,7 @@ impl ExecutorContext {
 | 
			
		||||
            let (exec_state, preserve_mem) = if clear_scene {
 | 
			
		||||
                // Pop the execution state, since we are starting fresh.
 | 
			
		||||
                let mut exec_state = old_state;
 | 
			
		||||
                exec_state.reset(&self.settings);
 | 
			
		||||
                exec_state.reset(self);
 | 
			
		||||
 | 
			
		||||
                // We don't do this in mock mode since there is no engine connection
 | 
			
		||||
                // anyways and from the TS side we override memory and don't want to clear it.
 | 
			
		||||
@ -638,7 +634,7 @@ impl ExecutorContext {
 | 
			
		||||
 | 
			
		||||
            (program, exec_state, preserve_mem)
 | 
			
		||||
        } else {
 | 
			
		||||
            let mut exec_state = ExecState::new(&self.settings);
 | 
			
		||||
            let mut exec_state = ExecState::new(self);
 | 
			
		||||
            self.send_clear_scene(&mut exec_state, Default::default())
 | 
			
		||||
                .await
 | 
			
		||||
                .map_err(KclErrorWithOutputs::no_outputs)?;
 | 
			
		||||
@ -663,7 +659,7 @@ impl ExecutorContext {
 | 
			
		||||
        })
 | 
			
		||||
        .await;
 | 
			
		||||
 | 
			
		||||
        let outcome = exec_state.to_wasm_outcome(result.0);
 | 
			
		||||
        let outcome = exec_state.to_wasm_outcome(result.0).await;
 | 
			
		||||
        Ok(outcome)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -699,6 +695,7 @@ impl ExecutorContext {
 | 
			
		||||
            .await
 | 
			
		||||
            .map_err(KclErrorWithOutputs::no_outputs)?;
 | 
			
		||||
 | 
			
		||||
        let default_planes = self.engine.get_default_planes().read().await.clone();
 | 
			
		||||
        let env_ref = self
 | 
			
		||||
            .execute_and_build_graph(&program.ast, exec_state, preserve_mem)
 | 
			
		||||
            .await
 | 
			
		||||
@ -717,6 +714,7 @@ impl ExecutorContext {
 | 
			
		||||
                    exec_state.global.artifact_graph.clone(),
 | 
			
		||||
                    module_id_to_module_path,
 | 
			
		||||
                    exec_state.global.id_to_source.clone(),
 | 
			
		||||
                    default_planes,
 | 
			
		||||
                )
 | 
			
		||||
            })?;
 | 
			
		||||
 | 
			
		||||
@ -754,6 +752,7 @@ impl ExecutorContext {
 | 
			
		||||
                exec_state,
 | 
			
		||||
                ExecutionKind::Normal,
 | 
			
		||||
                preserve_mem,
 | 
			
		||||
                ModuleId::default(),
 | 
			
		||||
                &ModulePath::Main,
 | 
			
		||||
            )
 | 
			
		||||
            .await;
 | 
			
		||||
@ -933,7 +932,7 @@ pub(crate) async fn parse_execute(code: &str) -> Result<ExecTestResults, KclErro
 | 
			
		||||
        settings: Default::default(),
 | 
			
		||||
        context_type: ContextType::Mock,
 | 
			
		||||
    };
 | 
			
		||||
    let mut exec_state = ExecState::new(&exec_ctxt.settings);
 | 
			
		||||
    let mut exec_state = ExecState::new(&exec_ctxt);
 | 
			
		||||
    let result = exec_ctxt.run(&program, &mut exec_state).await?;
 | 
			
		||||
 | 
			
		||||
    Ok(ExecTestResults {
 | 
			
		||||
@ -1880,10 +1879,14 @@ let w = f() + f()
 | 
			
		||||
        let old_program = crate::Program::parse_no_errs(code).unwrap();
 | 
			
		||||
 | 
			
		||||
        // Execute the program.
 | 
			
		||||
        ctx.run_with_caching(old_program).await.unwrap();
 | 
			
		||||
        if let Err(err) = ctx.run_with_caching(old_program).await {
 | 
			
		||||
            let report = err.into_miette_report_with_outputs(code).unwrap();
 | 
			
		||||
            let report = miette::Report::new(report);
 | 
			
		||||
            panic!("Error executing program: {:?}", report);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Get the id_generator from the first execution.
 | 
			
		||||
        let id_generator = cache::read_old_ast().await.unwrap().exec_state.global.id_generator;
 | 
			
		||||
        let id_generator = cache::read_old_ast().await.unwrap().exec_state.mod_local.id_generator;
 | 
			
		||||
 | 
			
		||||
        let code = r#"sketch001 = startSketchOn(XZ)
 | 
			
		||||
|> startProfileAt([62.74, 206.13], %)
 | 
			
		||||
@ -1904,7 +1907,7 @@ let w = f() + f()
 | 
			
		||||
        // Execute the program.
 | 
			
		||||
        ctx.run_with_caching(program).await.unwrap();
 | 
			
		||||
 | 
			
		||||
        let new_id_generator = cache::read_old_ast().await.unwrap().exec_state.global.id_generator;
 | 
			
		||||
        let new_id_generator = cache::read_old_ast().await.unwrap().exec_state.mod_local.id_generator;
 | 
			
		||||
 | 
			
		||||
        assert_eq!(id_generator, new_id_generator);
 | 
			
		||||
    }
 | 
			
		||||
@ -1933,7 +1936,6 @@ let w = f() + f()
 | 
			
		||||
        // Execute the program.
 | 
			
		||||
        ctx.run_with_caching(old_program.clone()).await.unwrap();
 | 
			
		||||
 | 
			
		||||
        // Get the id_generator from the first execution.
 | 
			
		||||
        let settings_state = cache::read_old_ast().await.unwrap().settings;
 | 
			
		||||
 | 
			
		||||
        // Ensure the settings are as expected.
 | 
			
		||||
@ -1945,7 +1947,6 @@ let w = f() + f()
 | 
			
		||||
        // Execute the program.
 | 
			
		||||
        ctx.run_with_caching(old_program.clone()).await.unwrap();
 | 
			
		||||
 | 
			
		||||
        // Get the id_generator from the first execution.
 | 
			
		||||
        let settings_state = cache::read_old_ast().await.unwrap().settings;
 | 
			
		||||
 | 
			
		||||
        // Ensure the settings are as expected.
 | 
			
		||||
@ -1957,7 +1958,6 @@ let w = f() + f()
 | 
			
		||||
        // Execute the program.
 | 
			
		||||
        ctx.run_with_caching(old_program).await.unwrap();
 | 
			
		||||
 | 
			
		||||
        // Get the id_generator from the first execution.
 | 
			
		||||
        let settings_state = cache::read_old_ast().await.unwrap().settings;
 | 
			
		||||
 | 
			
		||||
        // Ensure the settings are as expected.
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,9 @@ use uuid::Uuid;
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails, Severity},
 | 
			
		||||
    execution::{
 | 
			
		||||
        annotations, kcl_value,
 | 
			
		||||
        annotations,
 | 
			
		||||
        id_generator::IdGenerator,
 | 
			
		||||
        kcl_value,
 | 
			
		||||
        memory::{ProgramMemory, Stack},
 | 
			
		||||
        Artifact, ArtifactCommand, ArtifactGraph, ArtifactId, EnvironmentRef, ExecOutcome, ExecutorSettings, KclValue,
 | 
			
		||||
        Operation, UnitAngle, UnitLen,
 | 
			
		||||
@ -26,12 +28,11 @@ use crate::{
 | 
			
		||||
pub struct ExecState {
 | 
			
		||||
    pub(super) global: GlobalState,
 | 
			
		||||
    pub(super) mod_local: ModuleState,
 | 
			
		||||
    pub(super) exec_context: Option<super::ExecutorContext>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub(super) struct GlobalState {
 | 
			
		||||
    /// The stable artifact ID generator.
 | 
			
		||||
    pub id_generator: IdGenerator,
 | 
			
		||||
    /// Map from source file absolute path to module ID.
 | 
			
		||||
    pub path_to_source_id: IndexMap<ModulePath, ModuleId>,
 | 
			
		||||
    /// Map from module ID to source file.
 | 
			
		||||
@ -62,6 +63,8 @@ pub(super) struct GlobalState {
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub(super) struct ModuleState {
 | 
			
		||||
    /// The id generator for this module.
 | 
			
		||||
    pub id_generator: IdGenerator,
 | 
			
		||||
    pub stack: Stack,
 | 
			
		||||
    /// The current value of the pipe operator returned from the previous
 | 
			
		||||
    /// expression.  If we're not currently in a pipeline, this will be None.
 | 
			
		||||
@ -73,25 +76,21 @@ pub(super) struct ModuleState {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ExecState {
 | 
			
		||||
    pub fn new(exec_settings: &ExecutorSettings) -> Self {
 | 
			
		||||
    pub fn new(exec_context: &super::ExecutorContext) -> Self {
 | 
			
		||||
        ExecState {
 | 
			
		||||
            global: GlobalState::new(exec_settings),
 | 
			
		||||
            mod_local: ModuleState::new(exec_settings, None, ProgramMemory::new()),
 | 
			
		||||
            global: GlobalState::new(&exec_context.settings),
 | 
			
		||||
            mod_local: ModuleState::new(&exec_context.settings, None, ProgramMemory::new(), Default::default()),
 | 
			
		||||
            exec_context: Some(exec_context.clone()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(super) fn reset(&mut self, exec_settings: &ExecutorSettings) {
 | 
			
		||||
        let mut id_generator = self.global.id_generator.clone();
 | 
			
		||||
        // We do not pop the ids, since we want to keep the same id generator.
 | 
			
		||||
        // This is for the front end to keep track of the ids.
 | 
			
		||||
        id_generator.next_id = 0;
 | 
			
		||||
 | 
			
		||||
        let mut global = GlobalState::new(exec_settings);
 | 
			
		||||
        global.id_generator = id_generator;
 | 
			
		||||
    pub(super) fn reset(&mut self, exec_context: &super::ExecutorContext) {
 | 
			
		||||
        let global = GlobalState::new(&exec_context.settings);
 | 
			
		||||
 | 
			
		||||
        *self = ExecState {
 | 
			
		||||
            global,
 | 
			
		||||
            mod_local: ModuleState::new(exec_settings, None, ProgramMemory::new()),
 | 
			
		||||
            mod_local: ModuleState::new(&exec_context.settings, None, ProgramMemory::new(), Default::default()),
 | 
			
		||||
            exec_context: Some(exec_context.clone()),
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -113,7 +112,7 @@ impl ExecState {
 | 
			
		||||
    /// Convert to execution outcome when running in WebAssembly.  We want to
 | 
			
		||||
    /// reduce the amount of data that crosses the WASM boundary as much as
 | 
			
		||||
    /// possible.
 | 
			
		||||
    pub fn to_wasm_outcome(self, main_ref: EnvironmentRef) -> ExecOutcome {
 | 
			
		||||
    pub async fn to_wasm_outcome(self, main_ref: EnvironmentRef) -> ExecOutcome {
 | 
			
		||||
        // Fields are opt-in so that we don't accidentally leak private internal
 | 
			
		||||
        // state when we add more to ExecState.
 | 
			
		||||
        ExecOutcome {
 | 
			
		||||
@ -132,10 +131,15 @@ impl ExecState {
 | 
			
		||||
                .iter()
 | 
			
		||||
                .map(|(k, v)| ((*v), k.clone()))
 | 
			
		||||
                .collect(),
 | 
			
		||||
            default_planes: if let Some(ctx) = &self.exec_context {
 | 
			
		||||
                ctx.engine.get_default_planes().read().await.clone()
 | 
			
		||||
            } else {
 | 
			
		||||
                None
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn to_mock_wasm_outcome(self, main_ref: EnvironmentRef) -> ExecOutcome {
 | 
			
		||||
    pub async fn to_mock_wasm_outcome(self, main_ref: EnvironmentRef) -> ExecOutcome {
 | 
			
		||||
        // Fields are opt-in so that we don't accidentally leak private internal
 | 
			
		||||
        // state when we add more to ExecState.
 | 
			
		||||
        ExecOutcome {
 | 
			
		||||
@ -149,6 +153,11 @@ impl ExecState {
 | 
			
		||||
            artifact_graph: Default::default(),
 | 
			
		||||
            errors: self.global.errors,
 | 
			
		||||
            filenames: Default::default(),
 | 
			
		||||
            default_planes: if let Some(ctx) = &self.exec_context {
 | 
			
		||||
                ctx.engine.get_default_planes().read().await.clone()
 | 
			
		||||
            } else {
 | 
			
		||||
                None
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -160,8 +169,12 @@ impl ExecState {
 | 
			
		||||
        &mut self.mod_local.stack
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn next_uuid(&mut self) -> Uuid {
 | 
			
		||||
        self.global.id_generator.next_uuid()
 | 
			
		||||
    pub fn next_uuid(&mut self) -> Uuid {
 | 
			
		||||
        self.mod_local.id_generator.next_uuid()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn id_generator(&mut self) -> &mut IdGenerator {
 | 
			
		||||
        &mut self.mod_local.id_generator
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn add_artifact(&mut self, artifact: Artifact) {
 | 
			
		||||
@ -241,7 +254,6 @@ impl ExecState {
 | 
			
		||||
impl GlobalState {
 | 
			
		||||
    fn new(settings: &ExecutorSettings) -> Self {
 | 
			
		||||
        let mut global = GlobalState {
 | 
			
		||||
            id_generator: Default::default(),
 | 
			
		||||
            path_to_source_id: Default::default(),
 | 
			
		||||
            module_infos: Default::default(),
 | 
			
		||||
            artifacts: Default::default(),
 | 
			
		||||
@ -274,8 +286,14 @@ impl GlobalState {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ModuleState {
 | 
			
		||||
    pub(super) fn new(exec_settings: &ExecutorSettings, std_path: Option<String>, memory: Arc<ProgramMemory>) -> Self {
 | 
			
		||||
    pub(super) fn new(
 | 
			
		||||
        exec_settings: &ExecutorSettings,
 | 
			
		||||
        std_path: Option<String>,
 | 
			
		||||
        memory: Arc<ProgramMemory>,
 | 
			
		||||
        module_id: Option<ModuleId>,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        ModuleState {
 | 
			
		||||
            id_generator: IdGenerator::new(module_id),
 | 
			
		||||
            stack: memory.new_stack(),
 | 
			
		||||
            pipe_value: Default::default(),
 | 
			
		||||
            module_exports: Default::default(),
 | 
			
		||||
@ -332,29 +350,3 @@ impl MetaSettings {
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A generator for ArtifactIds that can be stable across executions.
 | 
			
		||||
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
pub struct IdGenerator {
 | 
			
		||||
    pub(super) next_id: usize,
 | 
			
		||||
    ids: Vec<uuid::Uuid>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl IdGenerator {
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        Self::default()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn next_uuid(&mut self) -> uuid::Uuid {
 | 
			
		||||
        if let Some(id) = self.ids.get(self.next_id) {
 | 
			
		||||
            self.next_id += 1;
 | 
			
		||||
            *id
 | 
			
		||||
        } else {
 | 
			
		||||
            let id = uuid::Uuid::new_v4();
 | 
			
		||||
            self.ids.push(id);
 | 
			
		||||
            self.next_id += 1;
 | 
			
		||||
            id
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -8,11 +8,11 @@
 | 
			
		||||
#[allow(unused_macros)]
 | 
			
		||||
macro_rules! println {
 | 
			
		||||
    ($($rest:tt)*) => {
 | 
			
		||||
        #[cfg(feature = "disable-println")]
 | 
			
		||||
        #[cfg(all(feature = "disable-println", not(test)))]
 | 
			
		||||
        {
 | 
			
		||||
            let _ = format!($($rest)*);
 | 
			
		||||
        }
 | 
			
		||||
        #[cfg(not(feature = "disable-println"))]
 | 
			
		||||
        #[cfg(any(not(feature = "disable-println"), test))]
 | 
			
		||||
        std::println!($($rest)*)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -20,11 +20,11 @@ macro_rules! println {
 | 
			
		||||
#[allow(unused_macros)]
 | 
			
		||||
macro_rules! eprintln {
 | 
			
		||||
    ($($rest:tt)*) => {
 | 
			
		||||
        #[cfg(feature = "disable-println")]
 | 
			
		||||
        #[cfg(all(feature = "disable-println", not(test)))]
 | 
			
		||||
        {
 | 
			
		||||
            let _ = format!($($rest)*);
 | 
			
		||||
        }
 | 
			
		||||
        #[cfg(not(feature = "disable-println"))]
 | 
			
		||||
        #[cfg(any(not(feature = "disable-println"), test))]
 | 
			
		||||
        std::eprintln!($($rest)*)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -32,11 +32,11 @@ macro_rules! eprintln {
 | 
			
		||||
#[allow(unused_macros)]
 | 
			
		||||
macro_rules! print {
 | 
			
		||||
    ($($rest:tt)*) => {
 | 
			
		||||
        #[cfg(feature = "disable-println")]
 | 
			
		||||
        #[cfg(all(feature = "disable-println", not(test)))]
 | 
			
		||||
        {
 | 
			
		||||
            let _ = format!($($rest)*);
 | 
			
		||||
        }
 | 
			
		||||
        #[cfg(not(feature = "disable-println"))]
 | 
			
		||||
        #[cfg(any(not(feature = "disable-println"), test))]
 | 
			
		||||
        std::print!($($rest)*)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -44,11 +44,11 @@ macro_rules! print {
 | 
			
		||||
#[allow(unused_macros)]
 | 
			
		||||
macro_rules! eprint {
 | 
			
		||||
    ($($rest:tt)*) => {
 | 
			
		||||
        #[cfg(feature = "disable-println")]
 | 
			
		||||
        #[cfg(all(feature = "disable-println", not(test)))]
 | 
			
		||||
        {
 | 
			
		||||
            let _ = format!($($rest)*);
 | 
			
		||||
        }
 | 
			
		||||
        #[cfg(not(feature = "disable-println"))]
 | 
			
		||||
        #[cfg(any(not(feature = "disable-println"), test))]
 | 
			
		||||
        std::eprint!($($rest)*)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -108,7 +108,7 @@ pub mod wasm_engine {
 | 
			
		||||
    pub use crate::{
 | 
			
		||||
        coredump::wasm::{CoreDumpManager, CoreDumper},
 | 
			
		||||
        engine::conn_wasm::{EngineCommandManager, EngineConnection},
 | 
			
		||||
        fs::wasm::FileSystemManager,
 | 
			
		||||
        fs::wasm::{FileManager, FileSystemManager},
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -36,7 +36,7 @@ macro_rules! logln {
 | 
			
		||||
}
 | 
			
		||||
pub(crate) use logln;
 | 
			
		||||
 | 
			
		||||
#[cfg(all(not(feature = "disable-println"), not(target_arch = "wasm32")))]
 | 
			
		||||
#[cfg(any(test, all(not(feature = "disable-println"), not(target_arch = "wasm32"))))]
 | 
			
		||||
#[inline]
 | 
			
		||||
fn log_inner(msg: String) {
 | 
			
		||||
    eprintln!("{msg}");
 | 
			
		||||
@ -48,7 +48,7 @@ fn log_inner(msg: String) {
 | 
			
		||||
    web_sys::console::log_1(&msg.into());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "disable-println")]
 | 
			
		||||
#[cfg(all(feature = "disable-println", not(test)))]
 | 
			
		||||
#[inline]
 | 
			
		||||
fn log_inner(_msg: String) {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -36,6 +36,6 @@ async fn main() {
 | 
			
		||||
    )
 | 
			
		||||
    .await
 | 
			
		||||
    .unwrap();
 | 
			
		||||
    let mut exec_state = ExecState::new(&ctx.settings);
 | 
			
		||||
    let mut exec_state = ExecState::new(&ctx);
 | 
			
		||||
    ctx.run(&program, &mut exec_state).await.unwrap();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -33,6 +33,12 @@ impl ModuleId {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::fmt::Display for ModuleId {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        write!(f, "{}", self.0)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Default)]
 | 
			
		||||
pub(crate) struct ModuleLoader {
 | 
			
		||||
    /// The stack of import statements for detecting circular module imports.
 | 
			
		||||
 | 
			
		||||
@ -149,7 +149,7 @@ async fn execute_test(test: &Test, render_to_png: bool, export_step: bool) {
 | 
			
		||||
                // due to SSI and GPU.
 | 
			
		||||
                std::fs::write(test.output_dir.join(EXPORTED_STEP_NAME), step).unwrap();
 | 
			
		||||
            }
 | 
			
		||||
            let outcome = exec_state.to_wasm_outcome(env_ref);
 | 
			
		||||
            let outcome = exec_state.to_wasm_outcome(env_ref).await;
 | 
			
		||||
            assert_common_snapshots(
 | 
			
		||||
                test,
 | 
			
		||||
                outcome.operations,
 | 
			
		||||
 | 
			
		||||
@ -1098,6 +1098,8 @@ async fn make_sketch_plane_from_orientation(
 | 
			
		||||
    let hide = Some(true);
 | 
			
		||||
    match data {
 | 
			
		||||
        PlaneData::XY | PlaneData::NegXY | PlaneData::XZ | PlaneData::NegXZ | PlaneData::YZ | PlaneData::NegYZ => {
 | 
			
		||||
            // TODO: ignoring the default planes here since we already created them, breaks the
 | 
			
		||||
            // front end for the feature tree which is stupid and we should fix it.
 | 
			
		||||
            let x_axis = match data {
 | 
			
		||||
                PlaneData::NegXY => Point3d::new(-1.0, 0.0, 0.0),
 | 
			
		||||
                PlaneData::NegXZ => Point3d::new(-1.0, 0.0, 0.0),
 | 
			
		||||
 | 
			
		||||
@ -81,7 +81,7 @@ async fn do_execute_and_snapshot(
 | 
			
		||||
    ctx: &ExecutorContext,
 | 
			
		||||
    program: Program,
 | 
			
		||||
) -> Result<(ExecState, EnvironmentRef, image::DynamicImage), ExecErrorWithState> {
 | 
			
		||||
    let mut exec_state = ExecState::new(&ctx.settings);
 | 
			
		||||
    let mut exec_state = ExecState::new(ctx);
 | 
			
		||||
    let result = ctx
 | 
			
		||||
        .run(&program, &mut exec_state)
 | 
			
		||||
        .await
 | 
			
		||||
@ -156,7 +156,7 @@ pub async fn execute_and_export_step(
 | 
			
		||||
    ExecErrorWithState,
 | 
			
		||||
> {
 | 
			
		||||
    let ctx = new_context(units, true, current_file).await?;
 | 
			
		||||
    let mut exec_state = ExecState::new(&ctx.settings);
 | 
			
		||||
    let mut exec_state = ExecState::new(&ctx);
 | 
			
		||||
    let program = Program::parse_no_errs(code)
 | 
			
		||||
        .map_err(|err| ExecErrorWithState::new(KclErrorWithOutputs::no_outputs(err).into(), exec_state.clone()))?;
 | 
			
		||||
    let result = ctx
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user