Reduce the amount of data sent to TS and make new fields opt-in (#4913)

* Reduce the amount of data sent back to JS/TS from WASM

* Remove unneeded derives since we shouldn't expose these types

* Alias type to be clearer
This commit is contained in:
Jonathan Tran
2025-01-06 16:55:59 -05:00
committed by GitHub
parent 8b8feb8d68
commit 275a2150e7
4 changed files with 41 additions and 26 deletions

View File

@ -35,7 +35,7 @@ import { Configuration } from 'wasm-lib/kcl/bindings/Configuration'
import { DeepPartial } from 'lib/types' import { DeepPartial } from 'lib/types'
import { ProjectConfiguration } from 'wasm-lib/kcl/bindings/ProjectConfiguration' import { ProjectConfiguration } from 'wasm-lib/kcl/bindings/ProjectConfiguration'
import { Sketch } from '../wasm-lib/kcl/bindings/Sketch' import { Sketch } from '../wasm-lib/kcl/bindings/Sketch'
import { ExecState as RawExecState } from '../wasm-lib/kcl/bindings/ExecState' import { ExecOutcome as RustExecOutcome } from 'wasm-lib/kcl/bindings/ExecOutcome'
import { ProgramMemory as RawProgramMemory } from '../wasm-lib/kcl/bindings/ProgramMemory' import { ProgramMemory as RawProgramMemory } from '../wasm-lib/kcl/bindings/ProgramMemory'
import { EnvironmentRef } from '../wasm-lib/kcl/bindings/EnvironmentRef' import { EnvironmentRef } from '../wasm-lib/kcl/bindings/EnvironmentRef'
import { Environment } from '../wasm-lib/kcl/bindings/Environment' import { Environment } from '../wasm-lib/kcl/bindings/Environment'
@ -260,10 +260,10 @@ export function emptyExecState(): ExecState {
} }
} }
function execStateFromRaw(raw: RawExecState): ExecState { function execStateFromRust(execOutcome: RustExecOutcome): ExecState {
return { return {
memory: ProgramMemory.fromRaw(raw.modLocal.memory), memory: ProgramMemory.fromRaw(execOutcome.memory),
operations: raw.modLocal.operations, operations: execOutcome.operations,
} }
} }
@ -535,14 +535,14 @@ export const _executor = async (
jsAppSettings = getAllCurrentSettings(lastSettingsSnapshot) jsAppSettings = getAllCurrentSettings(lastSettingsSnapshot)
} }
} }
const execState: RawExecState = await execute( const execOutcome: RustExecOutcome = await execute(
JSON.stringify(node), JSON.stringify(node),
JSON.stringify(programMemoryOverride?.toRaw() || null), JSON.stringify(programMemoryOverride?.toRaw() || null),
JSON.stringify({ settings: jsAppSettings }), JSON.stringify({ settings: jsAppSettings }),
engineCommandManager, engineCommandManager,
fileSystemManager fileSystemManager
) )
return execStateFromRaw(execState) return execStateFromRust(execOutcome)
} catch (e: any) { } catch (e: any) {
console.log(e) console.log(e)
const parsed: KclErrorWithOutputs = JSON.parse(e.toString()) const parsed: KclErrorWithOutputs = JSON.parse(e.toString())

View File

@ -1,6 +1,5 @@
//! Functions for helping with caching an ast and finding the parts the changed. //! Functions for helping with caching an ast and finding the parts the changed.
use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
@ -9,8 +8,7 @@ use crate::{
}; };
/// Information for the caching an AST and smartly re-executing it if we can. /// Information for the caching an AST and smartly re-executing it if we can.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[ts(export)]
pub struct CacheInformation { pub struct CacheInformation {
/// The old information. /// The old information.
pub old: Option<OldAstState>, pub old: Option<OldAstState>,
@ -19,8 +17,7 @@ pub struct CacheInformation {
} }
/// The old ast and program memory. /// The old ast and program memory.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[ts(export)]
pub struct OldAstState { pub struct OldAstState {
/// The ast. /// The ast.
pub ast: Node<Program>, pub ast: Node<Program>,
@ -40,8 +37,7 @@ impl From<crate::Program> for CacheInformation {
} }
/// The result of a cache check. /// The result of a cache check.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[ts(export)]
pub struct CacheResult { pub struct CacheResult {
/// Should we clear the scene and start over? /// Should we clear the scene and start over?
pub clear_scene: bool, pub clear_scene: bool,

View File

@ -51,16 +51,14 @@ use crate::{
pub use cad_op::Operation; pub use cad_op::Operation;
/// State for executing a program. /// State for executing a program.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[ts(export)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct ExecState { pub struct ExecState {
pub global: GlobalState, pub global: GlobalState,
pub mod_local: ModuleState, pub mod_local: ModuleState,
} }
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[ts(export)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct GlobalState { pub struct GlobalState {
/// The stable artifact ID generator. /// The stable artifact ID generator.
@ -71,8 +69,7 @@ pub struct GlobalState {
pub module_infos: IndexMap<ModuleId, ModuleInfo>, pub module_infos: IndexMap<ModuleId, ModuleInfo>,
} }
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] #[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq)]
#[ts(export)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct ModuleState { pub struct ModuleState {
/// Program variable bindings. /// Program variable bindings.
@ -94,6 +91,18 @@ pub struct ModuleState {
pub settings: MetaSettings, pub settings: MetaSettings,
} }
/// Outcome of executing a program. This is used in TS.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(rename_all = "camelCase")]
pub struct ExecOutcome {
/// Program variable bindings of the top-level module.
pub memory: ProgramMemory,
/// Operations that have been performed in execution order, for display in
/// the Feature Tree.
pub operations: Vec<Operation>,
}
impl Default for ExecState { impl Default for ExecState {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new()
@ -123,6 +132,18 @@ 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) -> ExecOutcome {
// Fields are opt-in so that we don't accidentally leak private internal
// state when we add more to ExecState.
ExecOutcome {
memory: self.mod_local.memory,
operations: self.mod_local.operations,
}
}
pub fn memory(&self) -> &ProgramMemory { pub fn memory(&self) -> &ProgramMemory {
&self.mod_local.memory &self.mod_local.memory
} }
@ -424,7 +445,7 @@ impl Environment {
/// Dynamic state that depends on the dynamic flow of the program, like the call /// Dynamic state that depends on the dynamic flow of the program, like the call
/// stack. If the language had exceptions, for example, you could store the /// stack. If the language had exceptions, for example, you could store the
/// stack of exception handlers here. /// stack of exception handlers here.
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize, ts_rs::TS, JsonSchema)] #[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct DynamicState { pub struct DynamicState {
pub solid_ids: Vec<SolidLazyIds>, pub solid_ids: Vec<SolidLazyIds>,
} }
@ -462,8 +483,7 @@ impl DynamicState {
} }
/// A generator for ArtifactIds that can be stable across executions. /// A generator for ArtifactIds that can be stable across executions.
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] #[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq)]
#[ts(export)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct IdGenerator { pub struct IdGenerator {
next_id: usize, next_id: usize,
@ -1073,7 +1093,7 @@ impl Solid {
/// An solid ID and its fillet and chamfer IDs. This is needed for lazy /// An solid ID and its fillet and chamfer IDs. This is needed for lazy
/// fillet evaluation. /// fillet evaluation.
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, ts_rs::TS, JsonSchema)] #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct SolidLazyIds { pub struct SolidLazyIds {
pub solid_id: uuid::Uuid, pub solid_id: uuid::Uuid,
pub sketch_id: uuid::Uuid, pub sketch_id: uuid::Uuid,
@ -1153,8 +1173,7 @@ pub enum BodyType {
/// Info about a module. Right now, this is pretty minimal. We hope to cache /// Info about a module. Right now, this is pretty minimal. We hope to cache
/// modules here in the future. /// modules here in the future.
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)] #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
#[ts(export)]
pub struct ModuleInfo { pub struct ModuleInfo {
/// The ID of the module. /// The ID of the module.
id: ModuleId, id: ModuleId,

View File

@ -133,7 +133,7 @@ pub async fn execute(
// gloo-serialize crate instead. // gloo-serialize crate instead.
// DO NOT USE serde_wasm_bindgen::to_value(&exec_state).map_err(|e| e.to_string()) // DO NOT USE serde_wasm_bindgen::to_value(&exec_state).map_err(|e| e.to_string())
// it will break the frontend. // it will break the frontend.
JsValue::from_serde(&exec_state).map_err(|e| e.to_string()) JsValue::from_serde(&exec_state.to_wasm_outcome()).map_err(|e| e.to_string())
} }
// wasm_bindgen wrapper for execute // wasm_bindgen wrapper for execute