start of cache: don't re-execute on whitespace / top level code comment changes (#4663)
* start Signed-off-by: Jess Frazelle <github@jessfraz.com> working for whitespace Signed-off-by: Jess Frazelle <github@jessfraz.com> pull thru Signed-off-by: Jess Frazelle <github@jessfraz.com> fix wasm Signed-off-by: Jess Frazelle <github@jessfraz.com> pull thru to js start Signed-off-by: Jess Frazelle <github@jessfraz.com> actually use the cache in ts Signed-off-by: Jess Frazelle <github@jessfraz.com> rust owns clearing the scene Signed-off-by: Jess Frazelle <github@jessfraz.com> fixes Signed-off-by: Jess Frazelle <github@jessfraz.com> empty stupid log Signed-off-by: Jess Frazelle <github@jessfraz.com> updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> fix tests Signed-off-by: Jess Frazelle <github@jessfraz.com> updatez Signed-off-by: Jess Frazelle <github@jessfraz.com> updates Signed-off-by: Jess Frazelle <github@jessfraz.com> save the state Signed-off-by: Jess Frazelle <github@jessfraz.com> save the state Signed-off-by: Jess Frazelle <github@jessfraz.com> updates Signed-off-by: Jess Frazelle <github@jessfraz.com> use the old memory Signed-off-by: Jess Frazelle <github@jessfraz.com> cleanup to use the old exec state Signed-off-by: Jess Frazelle <github@jessfraz.com> fices Signed-off-by: Jess Frazelle <github@jessfraz.com> updates; Signed-off-by: Jess Frazelle <github@jessfraz.com> fixes Signed-off-by: Jess Frazelle <github@jessfraz.com> fmt Signed-off-by: Jess Frazelle <github@jessfraz.com> updates Signed-off-by: Jess Frazelle <github@jessfraz.com> fixes Signed-off-by: Jess Frazelle <github@jessfraz.com> updates Signed-off-by: Jess Frazelle <github@jessfraz.com> cleanup Signed-off-by: Jess Frazelle <github@jessfraz.com> fixes Signed-off-by: Jess Frazelle <github@jessfraz.com> * rebase and compile Signed-off-by: Jess Frazelle <github@jessfraz.com> * Look at this (photo)Graph *in the voice of Nickelback* * fix the lsp to use the cache Signed-off-by: Jess Frazelle <github@jessfraz.com> * add comment Signed-off-by: Jess Frazelle <github@jessfraz.com> * use a global static instead; Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix rust test Signed-off-by: Jess Frazelle <github@jessfraz.com> * cleanup more Signed-off-by: Jess Frazelle <github@jessfraz.com> * cleanups Signed-off-by: Jess Frazelle <github@jessfraz.com> * cleanup the api even more Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * Look at this (photo)Graph *in the voice of Nickelback* * bust the cache on unit changes Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * Look at this (photo)Graph *in the voice of Nickelback* * stupid codespell Signed-off-by: Jess Frazelle <github@jessfraz.com> --------- Signed-off-by: Jess Frazelle <github@jessfraz.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
@ -943,6 +943,110 @@ sketch002 = startSketchOn(extrude001, 'END')
|
|||||||
`.replace(/\s/g, '')
|
`.replace(/\s/g, '')
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/* TODO: once we fix bug turn on.
|
||||||
|
test('empty-scene default-planes act as expected when spaces in file', async ({
|
||||||
|
page,
|
||||||
|
browserName,
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
const XYPlanePoint = { x: 774, y: 116 } as const
|
||||||
|
const unHoveredColor: [number, number, number] = [47, 47, 93]
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
|
||||||
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
|
// color should not change for having been hovered
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
|
||||||
|
await u.openAndClearDebugPanel()
|
||||||
|
|
||||||
|
// Fill with spaces
|
||||||
|
await u.codeLocator.fill(`
|
||||||
|
`)
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
|
||||||
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
|
// color should not change for having been hovered
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('empty-scene default-planes act as expected when only code comments in file', async ({
|
||||||
|
page,
|
||||||
|
browserName,
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
const XYPlanePoint = { x: 774, y: 116 } as const
|
||||||
|
const unHoveredColor: [number, number, number] = [47, 47, 93]
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
|
||||||
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
|
// color should not change for having been hovered
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
|
||||||
|
await u.openAndClearDebugPanel()
|
||||||
|
|
||||||
|
// Fill with spaces
|
||||||
|
await u.codeLocator.fill(`// this is a code comments ya nerds
|
||||||
|
`)
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
|
||||||
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
|
// color should not change for having been hovered
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
})*/
|
||||||
|
|
||||||
test('empty-scene default-planes act as expected', async ({
|
test('empty-scene default-planes act as expected', async ({
|
||||||
page,
|
page,
|
||||||
browserName,
|
browserName,
|
||||||
|
@ -155,7 +155,6 @@ export class CameraControls {
|
|||||||
this.camera.zoom = camProps.zoom || 1
|
this.camera.zoom = camProps.zoom || 1
|
||||||
}
|
}
|
||||||
this.camera.updateProjectionMatrix()
|
this.camera.updateProjectionMatrix()
|
||||||
console.log('doing this thing', camProps)
|
|
||||||
this.update(true)
|
this.update(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ import {
|
|||||||
recast,
|
recast,
|
||||||
defaultSourceRange,
|
defaultSourceRange,
|
||||||
resultIsOk,
|
resultIsOk,
|
||||||
|
ProgramMemory,
|
||||||
} from 'lang/wasm'
|
} from 'lang/wasm'
|
||||||
import { CustomIcon, CustomIconName } from 'components/CustomIcon'
|
import { CustomIcon, CustomIconName } from 'components/CustomIcon'
|
||||||
import { ConstrainInfo } from 'lang/std/stdTypes'
|
import { ConstrainInfo } from 'lang/std/stdTypes'
|
||||||
@ -420,9 +421,9 @@ export async function deleteSegment({
|
|||||||
|
|
||||||
const testExecute = await executeAst({
|
const testExecute = await executeAst({
|
||||||
ast: modifiedAst,
|
ast: modifiedAst,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: engineCommandManager,
|
engineCommandManager: engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
|
programMemoryOverride: ProgramMemory.empty(),
|
||||||
})
|
})
|
||||||
if (testExecute.errors.length) {
|
if (testExecute.errors.length) {
|
||||||
toast.error('Segment tag used outside of current Sketch. Could not delete.')
|
toast.error('Segment tag used outside of current Sketch. Could not delete.')
|
||||||
|
@ -498,10 +498,9 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: truncatedAst,
|
ast: truncatedAst,
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
const sketch = sketchFromPathToNode({
|
const sketch = sketchFromPathToNode({
|
||||||
@ -955,10 +954,9 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: truncatedAst,
|
ast: truncatedAst,
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
this.sceneProgramMemory = programMemory
|
this.sceneProgramMemory = programMemory
|
||||||
@ -1019,10 +1017,9 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: _ast,
|
ast: _ast,
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
|
|
||||||
@ -1120,10 +1117,9 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: truncatedAst,
|
ast: truncatedAst,
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
this.sceneProgramMemory = programMemory
|
this.sceneProgramMemory = programMemory
|
||||||
@ -1187,10 +1183,9 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: _ast,
|
ast: _ast,
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
|
|
||||||
@ -1306,10 +1301,9 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: modded,
|
ast: modded,
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
this.sceneProgramMemory = programMemory
|
this.sceneProgramMemory = programMemory
|
||||||
@ -1691,10 +1685,9 @@ export class SceneEntities {
|
|||||||
codeManager.updateCodeEditor(code)
|
codeManager.updateCodeEditor(code)
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: truncatedAst,
|
ast: truncatedAst,
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
})
|
})
|
||||||
const programMemory = execState.memory
|
const programMemory = execState.memory
|
||||||
this.sceneProgramMemory = programMemory
|
this.sceneProgramMemory = programMemory
|
||||||
|
@ -163,9 +163,8 @@ export function useCalc({
|
|||||||
executeAst({
|
executeAst({
|
||||||
ast,
|
ast,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
useFakeExecutor: true,
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride: kclManager.programMemory.clone(),
|
programMemoryOverride: kclManager.programMemory.clone(),
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
}).then(({ execState }) => {
|
}).then(({ execState }) => {
|
||||||
const resultDeclaration = ast.body.find(
|
const resultDeclaration = ast.body.find(
|
||||||
(a) =>
|
(a) =>
|
||||||
|
@ -34,6 +34,10 @@ describe('processMemory', () => {
|
|||||||
expect(output.myVar).toEqual(5)
|
expect(output.myVar).toEqual(5)
|
||||||
expect(output.otherVar).toEqual(3)
|
expect(output.otherVar).toEqual(3)
|
||||||
expect(output).toEqual({
|
expect(output).toEqual({
|
||||||
|
HALF_TURN: 180,
|
||||||
|
QUARTER_TURN: 90,
|
||||||
|
THREE_QUARTER_TURN: 270,
|
||||||
|
ZERO: 0,
|
||||||
myVar: 5,
|
myVar: 5,
|
||||||
myFn: '__function(a)__',
|
myFn: '__function(a)__',
|
||||||
otherVar: 3,
|
otherVar: 3,
|
||||||
|
@ -68,8 +68,8 @@ function AppLogoLink({
|
|||||||
data-testid="app-logo"
|
data-testid="app-logo"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onProjectClose(file || null, project?.path || null, false)
|
onProjectClose(file || null, project?.path || null, false)
|
||||||
// Clear the scene and end the session.
|
// Clear the scene.
|
||||||
engineCommandManager.endSession()
|
engineCommandManager.clearScene()
|
||||||
}}
|
}}
|
||||||
to={PATHS.HOME}
|
to={PATHS.HOME}
|
||||||
className={wrapperClassName + ' hover:before:brightness-110'}
|
className={wrapperClassName + ' hover:before:brightness-110'}
|
||||||
@ -190,8 +190,8 @@ function ProjectMenuPopover({
|
|||||||
className: !isDesktop() ? 'hidden' : '',
|
className: !isDesktop() ? 'hidden' : '',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
onProjectClose(file || null, project?.path || null, true)
|
onProjectClose(file || null, project?.path || null, true)
|
||||||
// Clear the scene and end the session.
|
// Clear the scene.
|
||||||
engineCommandManager.endSession()
|
engineCommandManager.clearScene()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
].filter(
|
].filter(
|
||||||
|
@ -88,7 +88,7 @@ export class KclManager {
|
|||||||
this._programMemoryCallBack(programMemory)
|
this._programMemoryCallBack(programMemory)
|
||||||
}
|
}
|
||||||
|
|
||||||
set execState(execState) {
|
private set execState(execState) {
|
||||||
this._execState = execState
|
this._execState = execState
|
||||||
this.programMemory = execState.memory
|
this.programMemory = execState.memory
|
||||||
}
|
}
|
||||||
@ -227,12 +227,6 @@ export class KclManager {
|
|||||||
this.addDiagnostics(complilationErrorsToDiagnostics(result.warnings))
|
this.addDiagnostics(complilationErrorsToDiagnostics(result.warnings))
|
||||||
if (result.errors.length > 0) {
|
if (result.errors.length > 0) {
|
||||||
this._hasErrors = true
|
this._hasErrors = true
|
||||||
// TODO: re-eval if session should end?
|
|
||||||
for (const e of result.errors)
|
|
||||||
if (e.message === 'file is empty') {
|
|
||||||
this.engineCommandManager?.endSession()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -276,12 +270,9 @@ export class KclManager {
|
|||||||
this._cancelTokens.set(currentExecutionId, false)
|
this._cancelTokens.set(currentExecutionId, false)
|
||||||
|
|
||||||
this.isExecuting = true
|
this.isExecuting = true
|
||||||
// Make sure we clear before starting again. End session will do this.
|
|
||||||
this.engineCommandManager?.endSession()
|
|
||||||
await this.ensureWasmInit()
|
await this.ensureWasmInit()
|
||||||
const { logs, errors, execState, isInterrupted } = await executeAst({
|
const { logs, errors, execState, isInterrupted } = await executeAst({
|
||||||
ast,
|
ast,
|
||||||
idGenerator: this.execState.idGenerator,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -331,8 +322,6 @@ export class KclManager {
|
|||||||
this.logs = logs
|
this.logs = logs
|
||||||
// Do not add the errors since the program was interrupted and the error is not a real KCL error
|
// Do not add the errors since the program was interrupted and the error is not a real KCL error
|
||||||
this.addDiagnostics(isInterrupted ? [] : kclErrorsToDiagnostics(errors))
|
this.addDiagnostics(isInterrupted ? [] : kclErrorsToDiagnostics(errors))
|
||||||
// Reset the next ID index so that we reuse the previous IDs next time.
|
|
||||||
execState.idGenerator.nextId = 0
|
|
||||||
this.execState = execState
|
this.execState = execState
|
||||||
if (!errors.length) {
|
if (!errors.length) {
|
||||||
this.lastSuccessfulProgramMemory = execState.memory
|
this.lastSuccessfulProgramMemory = execState.memory
|
||||||
@ -373,9 +362,9 @@ export class KclManager {
|
|||||||
|
|
||||||
const { logs, errors, execState } = await executeAst({
|
const { logs, errors, execState } = await executeAst({
|
||||||
ast: newAst,
|
ast: newAst,
|
||||||
idGenerator: this.execState.idGenerator,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
useFakeExecutor: true,
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
|
programMemoryOverride: ProgramMemory.empty(),
|
||||||
})
|
})
|
||||||
|
|
||||||
this._logs = logs
|
this._logs = logs
|
||||||
|
@ -2,7 +2,6 @@ import {
|
|||||||
Program,
|
Program,
|
||||||
_executor,
|
_executor,
|
||||||
ProgramMemory,
|
ProgramMemory,
|
||||||
programMemoryInit,
|
|
||||||
kclLint,
|
kclLint,
|
||||||
emptyExecState,
|
emptyExecState,
|
||||||
ExecState,
|
ExecState,
|
||||||
@ -11,7 +10,6 @@ import { enginelessExecutor } from 'lib/testHelpers'
|
|||||||
import { EngineCommandManager } from 'lang/std/engineConnection'
|
import { EngineCommandManager } from 'lang/std/engineConnection'
|
||||||
import { KCLError } from 'lang/errors'
|
import { KCLError } from 'lang/errors'
|
||||||
import { Diagnostic } from '@codemirror/lint'
|
import { Diagnostic } from '@codemirror/lint'
|
||||||
import { IdGenerator } from 'wasm-lib/kcl/bindings/IdGenerator'
|
|
||||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
export type ToolTip =
|
export type ToolTip =
|
||||||
@ -49,15 +47,13 @@ export const toolTips: Array<ToolTip> = [
|
|||||||
export async function executeAst({
|
export async function executeAst({
|
||||||
ast,
|
ast,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
useFakeExecutor = false,
|
// If you set programMemoryOverride we assume you mean mock mode. Since that
|
||||||
|
// is the only way to go about it.
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
idGenerator,
|
|
||||||
}: {
|
}: {
|
||||||
ast: Node<Program>
|
ast: Node<Program>
|
||||||
engineCommandManager: EngineCommandManager
|
engineCommandManager: EngineCommandManager
|
||||||
useFakeExecutor?: boolean
|
|
||||||
programMemoryOverride?: ProgramMemory
|
programMemoryOverride?: ProgramMemory
|
||||||
idGenerator?: IdGenerator
|
|
||||||
isInterrupted?: boolean
|
isInterrupted?: boolean
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
logs: string[]
|
logs: string[]
|
||||||
@ -66,22 +62,14 @@ export async function executeAst({
|
|||||||
isInterrupted: boolean
|
isInterrupted: boolean
|
||||||
}> {
|
}> {
|
||||||
try {
|
try {
|
||||||
if (!useFakeExecutor) {
|
const execState = await (programMemoryOverride
|
||||||
engineCommandManager.endSession()
|
? enginelessExecutor(ast, programMemoryOverride)
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
: _executor(ast, engineCommandManager))
|
||||||
engineCommandManager.startNewSession()
|
|
||||||
}
|
await engineCommandManager.waitForAllCommands(
|
||||||
const execState = await (useFakeExecutor
|
programMemoryOverride !== undefined
|
||||||
? enginelessExecutor(ast, programMemoryOverride || programMemoryInit())
|
)
|
||||||
: _executor(
|
|
||||||
ast,
|
|
||||||
programMemoryInit(),
|
|
||||||
idGenerator,
|
|
||||||
engineCommandManager,
|
|
||||||
false
|
|
||||||
))
|
|
||||||
|
|
||||||
await engineCommandManager.waitForAllCommands(useFakeExecutor)
|
|
||||||
return {
|
return {
|
||||||
logs: [],
|
logs: [],
|
||||||
errors: [],
|
errors: [],
|
||||||
|
@ -1879,7 +1879,7 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
}
|
}
|
||||||
return JSON.stringify(this.defaultPlanes)
|
return JSON.stringify(this.defaultPlanes)
|
||||||
}
|
}
|
||||||
endSession() {
|
clearScene(): void {
|
||||||
const deleteCmd: EngineCommand = {
|
const deleteCmd: EngineCommand = {
|
||||||
type: 'modeling_cmd_req',
|
type: 'modeling_cmd_req',
|
||||||
cmd_id: uuidv4(),
|
cmd_id: uuidv4(),
|
||||||
|
@ -35,7 +35,6 @@ 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 { IdGenerator } from 'wasm-lib/kcl/bindings/IdGenerator'
|
|
||||||
import { ExecState as RawExecState } from '../wasm-lib/kcl/bindings/ExecState'
|
import { ExecState as RawExecState } from '../wasm-lib/kcl/bindings/ExecState'
|
||||||
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'
|
||||||
@ -216,7 +215,6 @@ export const isPathToNodeNumber = (
|
|||||||
|
|
||||||
export interface ExecState {
|
export interface ExecState {
|
||||||
memory: ProgramMemory
|
memory: ProgramMemory
|
||||||
idGenerator: IdGenerator
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -226,21 +224,12 @@ export interface ExecState {
|
|||||||
export function emptyExecState(): ExecState {
|
export function emptyExecState(): ExecState {
|
||||||
return {
|
return {
|
||||||
memory: ProgramMemory.empty(),
|
memory: ProgramMemory.empty(),
|
||||||
idGenerator: defaultIdGenerator(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function execStateFromRaw(raw: RawExecState): ExecState {
|
function execStateFromRaw(raw: RawExecState): ExecState {
|
||||||
return {
|
return {
|
||||||
memory: ProgramMemory.fromRaw(raw.memory),
|
memory: ProgramMemory.fromRaw(raw.memory),
|
||||||
idGenerator: raw.idGenerator,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function defaultIdGenerator(): IdGenerator {
|
|
||||||
return {
|
|
||||||
nextId: 0,
|
|
||||||
ids: [],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,6 +243,19 @@ function emptyEnvironment(): Environment {
|
|||||||
return { bindings: {}, parent: null }
|
return { bindings: {}, parent: null }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function emptyRootEnvironment(): Environment {
|
||||||
|
return {
|
||||||
|
// This is dumb this is copied from rust.
|
||||||
|
bindings: {
|
||||||
|
ZERO: { type: 'Number', value: 0.0, __meta: [] },
|
||||||
|
QUARTER_TURN: { type: 'Number', value: 90.0, __meta: [] },
|
||||||
|
HALF_TURN: { type: 'Number', value: 180.0, __meta: [] },
|
||||||
|
THREE_QUARTER_TURN: { type: 'Number', value: 270.0, __meta: [] },
|
||||||
|
},
|
||||||
|
parent: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This duplicates logic in Rust. The hope is to keep ProgramMemory internals
|
* This duplicates logic in Rust. The hope is to keep ProgramMemory internals
|
||||||
* isolated from the rest of the TypeScript code so that we can move it to Rust
|
* isolated from the rest of the TypeScript code so that we can move it to Rust
|
||||||
@ -276,7 +278,7 @@ export class ProgramMemory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
environments: Environment[] = [emptyEnvironment()],
|
environments: Environment[] = [emptyRootEnvironment()],
|
||||||
currentEnv: EnvironmentRef = ROOT_ENVIRONMENT_REF,
|
currentEnv: EnvironmentRef = ROOT_ENVIRONMENT_REF,
|
||||||
returnVal: KclValue | null = null
|
returnVal: KclValue | null = null
|
||||||
) {
|
) {
|
||||||
@ -463,36 +465,31 @@ export function sketchFromKclValue(
|
|||||||
|
|
||||||
export const executor = async (
|
export const executor = async (
|
||||||
node: Node<Program>,
|
node: Node<Program>,
|
||||||
programMemory: ProgramMemory | Error = ProgramMemory.empty(),
|
|
||||||
idGenerator: IdGenerator = defaultIdGenerator(),
|
|
||||||
engineCommandManager: EngineCommandManager,
|
engineCommandManager: EngineCommandManager,
|
||||||
isMock: boolean = false
|
programMemoryOverride: ProgramMemory | Error | null = null
|
||||||
): Promise<ExecState> => {
|
): Promise<ExecState> => {
|
||||||
if (err(programMemory)) return Promise.reject(programMemory)
|
if (programMemoryOverride !== null && err(programMemoryOverride))
|
||||||
|
return Promise.reject(programMemoryOverride)
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
engineCommandManager.startNewSession()
|
engineCommandManager.startNewSession()
|
||||||
const _programMemory = await _executor(
|
const _programMemory = await _executor(
|
||||||
node,
|
node,
|
||||||
programMemory,
|
|
||||||
idGenerator,
|
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
isMock
|
programMemoryOverride
|
||||||
)
|
)
|
||||||
await engineCommandManager.waitForAllCommands()
|
await engineCommandManager.waitForAllCommands()
|
||||||
|
|
||||||
engineCommandManager.endSession()
|
|
||||||
return _programMemory
|
return _programMemory
|
||||||
}
|
}
|
||||||
|
|
||||||
export const _executor = async (
|
export const _executor = async (
|
||||||
node: Node<Program>,
|
node: Node<Program>,
|
||||||
programMemory: ProgramMemory | Error = ProgramMemory.empty(),
|
|
||||||
idGenerator: IdGenerator = defaultIdGenerator(),
|
|
||||||
engineCommandManager: EngineCommandManager,
|
engineCommandManager: EngineCommandManager,
|
||||||
isMock: boolean
|
programMemoryOverride: ProgramMemory | Error | null = null
|
||||||
): Promise<ExecState> => {
|
): Promise<ExecState> => {
|
||||||
if (err(programMemory)) return Promise.reject(programMemory)
|
if (programMemoryOverride !== null && err(programMemoryOverride))
|
||||||
|
return Promise.reject(programMemoryOverride)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let baseUnit = 'mm'
|
let baseUnit = 'mm'
|
||||||
@ -505,13 +502,10 @@ export const _executor = async (
|
|||||||
}
|
}
|
||||||
const execState: RawExecState = await execute_wasm(
|
const execState: RawExecState = await execute_wasm(
|
||||||
JSON.stringify(node),
|
JSON.stringify(node),
|
||||||
JSON.stringify(programMemory.toRaw()),
|
JSON.stringify(programMemoryOverride?.toRaw() || null),
|
||||||
JSON.stringify(idGenerator),
|
|
||||||
baseUnit,
|
baseUnit,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
fileSystemManager,
|
fileSystemManager
|
||||||
undefined,
|
|
||||||
isMock
|
|
||||||
)
|
)
|
||||||
return execStateFromRaw(execState)
|
return execStateFromRaw(execState)
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
|
@ -116,8 +116,8 @@ export async function createAndOpenNewTutorialProject({
|
|||||||
) => void
|
) => void
|
||||||
navigate: (path: string) => void
|
navigate: (path: string) => void
|
||||||
}) {
|
}) {
|
||||||
// Clear the scene and end the session.
|
// Clear the scene.
|
||||||
engineCommandManager.endSession()
|
engineCommandManager.clearScene()
|
||||||
|
|
||||||
// Create a new project with the onboarding project name
|
// Create a new project with the onboarding project name
|
||||||
const configuration = await readAppSettingsFile()
|
const configuration = await readAppSettingsFile()
|
||||||
|
@ -4,7 +4,6 @@ import {
|
|||||||
_executor,
|
_executor,
|
||||||
SourceRange,
|
SourceRange,
|
||||||
ExecState,
|
ExecState,
|
||||||
defaultIdGenerator,
|
|
||||||
} from '../lang/wasm'
|
} from '../lang/wasm'
|
||||||
import {
|
import {
|
||||||
EngineCommandManager,
|
EngineCommandManager,
|
||||||
@ -16,7 +15,6 @@ import { v4 as uuidv4 } from 'uuid'
|
|||||||
import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
|
import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
|
||||||
import { err, reportRejection } from 'lib/trap'
|
import { err, reportRejection } from 'lib/trap'
|
||||||
import { toSync } from './utils'
|
import { toSync } from './utils'
|
||||||
import { IdGenerator } from 'wasm-lib/kcl/bindings/IdGenerator'
|
|
||||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
type WebSocketResponse = Models['WebSocketResponse_type']
|
type WebSocketResponse = Models['WebSocketResponse_type']
|
||||||
@ -86,10 +84,9 @@ class MockEngineCommandManager {
|
|||||||
|
|
||||||
export async function enginelessExecutor(
|
export async function enginelessExecutor(
|
||||||
ast: Node<Program>,
|
ast: Node<Program>,
|
||||||
pm: ProgramMemory | Error = ProgramMemory.empty(),
|
pmo: ProgramMemory | Error = ProgramMemory.empty()
|
||||||
idGenerator: IdGenerator = defaultIdGenerator()
|
|
||||||
): Promise<ExecState> {
|
): Promise<ExecState> {
|
||||||
if (err(pm)) return Promise.reject(pm)
|
if (pmo !== null && err(pmo)) return Promise.reject(pmo)
|
||||||
|
|
||||||
const mockEngineCommandManager = new MockEngineCommandManager({
|
const mockEngineCommandManager = new MockEngineCommandManager({
|
||||||
setIsStreamReady: () => {},
|
setIsStreamReady: () => {},
|
||||||
@ -97,21 +94,14 @@ export async function enginelessExecutor(
|
|||||||
}) as any as EngineCommandManager
|
}) as any as EngineCommandManager
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
mockEngineCommandManager.startNewSession()
|
mockEngineCommandManager.startNewSession()
|
||||||
const execState = await _executor(
|
const execState = await _executor(ast, mockEngineCommandManager, pmo)
|
||||||
ast,
|
|
||||||
pm,
|
|
||||||
idGenerator,
|
|
||||||
mockEngineCommandManager,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
await mockEngineCommandManager.waitForAllCommands()
|
await mockEngineCommandManager.waitForAllCommands()
|
||||||
return execState
|
return execState
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function executor(
|
export async function executor(
|
||||||
ast: Node<Program>,
|
ast: Node<Program>,
|
||||||
pm: ProgramMemory = ProgramMemory.empty(),
|
pmo: ProgramMemory = ProgramMemory.empty()
|
||||||
idGenerator: IdGenerator = defaultIdGenerator()
|
|
||||||
): Promise<ExecState> {
|
): Promise<ExecState> {
|
||||||
const engineCommandManager = new EngineCommandManager()
|
const engineCommandManager = new EngineCommandManager()
|
||||||
engineCommandManager.start({
|
engineCommandManager.start({
|
||||||
@ -133,13 +123,7 @@ export async function executor(
|
|||||||
toSync(async () => {
|
toSync(async () => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
engineCommandManager.startNewSession()
|
engineCommandManager.startNewSession()
|
||||||
const execState = await _executor(
|
const execState = await _executor(ast, engineCommandManager, pmo)
|
||||||
ast,
|
|
||||||
pm,
|
|
||||||
idGenerator,
|
|
||||||
engineCommandManager,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
await engineCommandManager.waitForAllCommands()
|
await engineCommandManager.waitForAllCommands()
|
||||||
resolve(execState)
|
resolve(execState)
|
||||||
}, reportRejection)
|
}, reportRejection)
|
||||||
|
@ -103,9 +103,8 @@ export function useCalculateKclExpression({
|
|||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast,
|
ast,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
useFakeExecutor: true,
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
programMemoryOverride: kclManager.programMemory.clone(),
|
programMemoryOverride: kclManager.programMemory.clone(),
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
})
|
})
|
||||||
const resultDeclaration = ast.body.find(
|
const resultDeclaration = ast.body.find(
|
||||||
(a) =>
|
(a) =>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
PathToNode,
|
PathToNode,
|
||||||
|
ProgramMemory,
|
||||||
VariableDeclaration,
|
VariableDeclaration,
|
||||||
VariableDeclarator,
|
VariableDeclarator,
|
||||||
parse,
|
parse,
|
||||||
@ -730,9 +731,9 @@ export const modelingMachine = setup({
|
|||||||
|
|
||||||
const testExecute = await executeAst({
|
const testExecute = await executeAst({
|
||||||
ast: modifiedAst,
|
ast: modifiedAst,
|
||||||
idGenerator: kclManager.execState.idGenerator,
|
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
|
programMemoryOverride: ProgramMemory.empty(),
|
||||||
})
|
})
|
||||||
if (testExecute.errors.length) {
|
if (testExecute.errors.length) {
|
||||||
toast.error('Unable to delete part')
|
toast.error('Unable to delete part')
|
||||||
|
1
src/wasm-lib/Cargo.lock
generated
1
src/wasm-lib/Cargo.lock
generated
@ -4308,6 +4308,7 @@ dependencies = [
|
|||||||
"kcl-lib",
|
"kcl-lib",
|
||||||
"kittycad",
|
"kittycad",
|
||||||
"kittycad-modeling-cmds",
|
"kittycad-modeling-cmds",
|
||||||
|
"lazy_static",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
@ -15,6 +15,7 @@ data-encoding = "2.6.0"
|
|||||||
gloo-utils = "0.2.0"
|
gloo-utils = "0.2.0"
|
||||||
kcl-lib = { path = "kcl" }
|
kcl-lib = { path = "kcl" }
|
||||||
kittycad.workspace = true
|
kittycad.workspace = true
|
||||||
|
lazy_static = "1.5.0"
|
||||||
serde_json = "1.0.128"
|
serde_json = "1.0.128"
|
||||||
tokio = { version = "1.41.1", features = ["sync"] }
|
tokio = { version = "1.41.1", features = ["sync"] }
|
||||||
toml = "0.8.19"
|
toml = "0.8.19"
|
||||||
|
@ -778,7 +778,7 @@ fn generate_code_block_test(fn_name: &str, code_block: &str, index: usize) -> pr
|
|||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx.run(&program, &mut crate::ExecState::default()).await.unwrap();
|
ctx.run(program.into(), &mut crate::ExecState::default()).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||||
|
@ -14,7 +14,7 @@ mod test_examples_someFn {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, &mut crate::ExecState::default())
|
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ mod test_examples_someFn {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, &mut crate::ExecState::default())
|
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ mod test_examples_show {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, &mut crate::ExecState::default())
|
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
@ -49,7 +49,7 @@ mod test_examples_show {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, &mut crate::ExecState::default())
|
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ mod test_examples_show {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, &mut crate::ExecState::default())
|
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ mod test_examples_my_func {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, &mut crate::ExecState::default())
|
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
@ -50,7 +50,7 @@ mod test_examples_my_func {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, &mut crate::ExecState::default())
|
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ mod test_examples_line_to {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, &mut crate::ExecState::default())
|
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
@ -50,7 +50,7 @@ mod test_examples_line_to {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, &mut crate::ExecState::default())
|
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ mod test_examples_min {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, &mut crate::ExecState::default())
|
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
@ -49,7 +49,7 @@ mod test_examples_min {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, &mut crate::ExecState::default())
|
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ mod test_examples_show {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, &mut crate::ExecState::default())
|
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ mod test_examples_import {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, &mut crate::ExecState::default())
|
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ mod test_examples_import {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, &mut crate::ExecState::default())
|
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ mod test_examples_import {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, &mut crate::ExecState::default())
|
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ mod test_examples_show {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, &mut crate::ExecState::default())
|
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ mod test_examples_some_function {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, &mut crate::ExecState::default())
|
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use kcl_lib::{
|
use kcl_lib::{
|
||||||
@ -11,10 +16,6 @@ use kittycad_modeling_cmds::{
|
|||||||
shared::PathSegment::{self, *},
|
shared::PathSegment::{self, *},
|
||||||
websocket::{ModelingBatch, ModelingCmdReq, OkWebSocketResponseData, WebSocketRequest, WebSocketResponse},
|
websocket::{ModelingBatch, ModelingCmdReq, OkWebSocketResponseData, WebSocketRequest, WebSocketResponse},
|
||||||
};
|
};
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
};
|
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
const CPP_PREFIX: &str = "const double scaleFactor = 100;\n";
|
const CPP_PREFIX: &str = "const double scaleFactor = 100;\n";
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use kcl_lib::{ExecState, ExecutorContext};
|
use kcl_lib::{ExecState, ExecutorContext};
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
mod conn_mock_core;
|
mod conn_mock_core;
|
||||||
@ -15,7 +16,7 @@ pub async fn kcl_to_engine_core(code: &str) -> Result<String> {
|
|||||||
let ctx = ExecutorContext::new_forwarded_mock(Arc::new(Box::new(
|
let ctx = ExecutorContext::new_forwarded_mock(Arc::new(Box::new(
|
||||||
crate::conn_mock_core::EngineConnection::new(ref_result).await?,
|
crate::conn_mock_core::EngineConnection::new(ref_result).await?,
|
||||||
)));
|
)));
|
||||||
ctx.run(&program, &mut ExecState::default()).await?;
|
ctx.run(program.into(), &mut ExecState::default()).await?;
|
||||||
|
|
||||||
let result = result.lock().expect("mutex lock").clone();
|
let result = result.lock().expect("mutex lock").clone();
|
||||||
Ok(result)
|
Ok(result)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use kcl_to_core::*;
|
|
||||||
use std::{env, fs};
|
use std::{env, fs};
|
||||||
|
|
||||||
|
use kcl_to_core::*;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
|
3
src/wasm-lib/kcl/src/ast/mod.rs
Normal file
3
src/wasm-lib/kcl/src/ast/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub mod cache;
|
||||||
|
pub mod modify;
|
||||||
|
pub mod types;
|
@ -25,7 +25,10 @@ use crate::{
|
|||||||
engine::{EngineManager, ExecutionKind},
|
engine::{EngineManager, ExecutionKind},
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
fs::{FileManager, FileSystem},
|
fs::{FileManager, FileSystem},
|
||||||
parsing::ast::types::{BodyItem, Expr, FunctionExpression, ItemVisibility, Node, NodeRef, TagDeclarator, TagNode},
|
parsing::ast::{
|
||||||
|
cache::{get_changed_program, CacheInformation},
|
||||||
|
types::{BodyItem, Expr, FunctionExpression, ItemVisibility, Node, NodeRef, TagDeclarator, TagNode},
|
||||||
|
},
|
||||||
settings::types::UnitLength,
|
settings::types::UnitLength,
|
||||||
source_range::{ModuleId, SourceRange},
|
source_range::{ModuleId, SourceRange},
|
||||||
std::{args::Arg, StdLib},
|
std::{args::Arg, StdLib},
|
||||||
@ -55,9 +58,6 @@ pub struct ExecState {
|
|||||||
pub path_to_source_id: IndexMap<std::path::PathBuf, ModuleId>,
|
pub path_to_source_id: IndexMap<std::path::PathBuf, ModuleId>,
|
||||||
/// Map from module ID to module info.
|
/// Map from module ID to module info.
|
||||||
pub module_infos: IndexMap<ModuleId, ModuleInfo>,
|
pub module_infos: IndexMap<ModuleId, ModuleInfo>,
|
||||||
/// The directory of the current project. This is used for resolving import
|
|
||||||
/// paths. If None is given, the current working directory is used.
|
|
||||||
pub project_directory: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExecState {
|
impl ExecState {
|
||||||
@ -1484,7 +1484,8 @@ pub struct ExecutorContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The executor settings.
|
/// The executor settings.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
|
#[ts(export)]
|
||||||
pub struct ExecutorSettings {
|
pub struct ExecutorSettings {
|
||||||
/// The unit to use in modeling dimensions.
|
/// The unit to use in modeling dimensions.
|
||||||
pub units: UnitLength,
|
pub units: UnitLength,
|
||||||
@ -1785,18 +1786,21 @@ impl ExecutorContext {
|
|||||||
&self,
|
&self,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
source_range: crate::executor::SourceRange,
|
source_range: crate::executor::SourceRange,
|
||||||
) -> Result<()> {
|
) -> Result<(), KclError> {
|
||||||
self.engine
|
self.engine
|
||||||
.clear_scene(&mut exec_state.id_generator, source_range)
|
.clear_scene(&mut exec_state.id_generator, source_range)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
// We do not create the planes here as the post hook in wasm will do that
|
||||||
|
// AND if we aren't in wasm it doesn't really matter.
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform the execution of a program.
|
/// Perform the execution of a program.
|
||||||
/// You can optionally pass in some initialization memory.
|
/// You can optionally pass in some initialization memory.
|
||||||
/// Kurt uses this for partial execution.
|
/// Kurt uses this for partial execution.
|
||||||
pub async fn run(&self, program: &Program, exec_state: &mut ExecState) -> Result<(), KclError> {
|
pub async fn run(&self, cache_info: CacheInformation, exec_state: &mut ExecState) -> Result<(), KclError> {
|
||||||
self.run_with_session_data(program, exec_state).await?;
|
self.run_with_session_data(cache_info, exec_state).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1805,10 +1809,27 @@ impl ExecutorContext {
|
|||||||
/// Kurt uses this for partial execution.
|
/// Kurt uses this for partial execution.
|
||||||
pub async fn run_with_session_data(
|
pub async fn run_with_session_data(
|
||||||
&self,
|
&self,
|
||||||
program: &Program,
|
cache_info: CacheInformation,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
) -> Result<Option<ModelingSessionData>, KclError> {
|
) -> Result<Option<ModelingSessionData>, KclError> {
|
||||||
let _stats = crate::log::LogPerfStats::new("Interpretation");
|
let _stats = crate::log::LogPerfStats::new("Interpretation");
|
||||||
|
|
||||||
|
// Get the program that actually changed from the old and new information.
|
||||||
|
let cache_result = get_changed_program(cache_info.clone(), &self.settings);
|
||||||
|
|
||||||
|
// Check if we don't need to re-execute.
|
||||||
|
let Some(cache_result) = cache_result else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
if cache_result.clear_scene && !self.is_mock() {
|
||||||
|
// 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.
|
||||||
|
self.reset_scene(exec_state, Default::default()).await?;
|
||||||
|
// Pop the execution state, since we are starting fresh.
|
||||||
|
*exec_state = Default::default();
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Use the top-level file's path.
|
// TODO: Use the top-level file's path.
|
||||||
exec_state.add_module(std::path::PathBuf::from(""));
|
exec_state.add_module(std::path::PathBuf::from(""));
|
||||||
// Before we even start executing the program, set the units.
|
// Before we even start executing the program, set the units.
|
||||||
@ -1829,7 +1850,7 @@ impl ExecutorContext {
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
self.inner_execute(&program.ast, exec_state, crate::executor::BodyType::Root)
|
self.inner_execute(&cache_result.program, exec_state, crate::executor::BodyType::Root)
|
||||||
.await?;
|
.await?;
|
||||||
let session_data = self.engine.get_session_data();
|
let session_data = self.engine.get_session_data();
|
||||||
Ok(session_data)
|
Ok(session_data)
|
||||||
@ -1857,11 +1878,7 @@ impl ExecutorContext {
|
|||||||
source_ranges: vec![source_range],
|
source_ranges: vec![source_range],
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
let resolved_path = if let Some(project_dir) = &exec_state.project_directory {
|
let resolved_path = std::path::PathBuf::from(&path);
|
||||||
std::path::PathBuf::from(project_dir).join(&path)
|
|
||||||
} else {
|
|
||||||
std::path::PathBuf::from(&path)
|
|
||||||
};
|
|
||||||
if exec_state.import_stack.contains(&resolved_path) {
|
if exec_state.import_stack.contains(&resolved_path) {
|
||||||
return Err(KclError::ImportCycle(KclErrorDetails {
|
return Err(KclError::ImportCycle(KclErrorDetails {
|
||||||
message: format!(
|
message: format!(
|
||||||
@ -2097,7 +2114,7 @@ impl ExecutorContext {
|
|||||||
program: &Program,
|
program: &Program,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
) -> std::result::Result<TakeSnapshot, ExecError> {
|
) -> std::result::Result<TakeSnapshot, ExecError> {
|
||||||
self.run(program, exec_state).await?;
|
self.run(program.clone().into(), exec_state).await?;
|
||||||
|
|
||||||
// Zoom to fit.
|
// Zoom to fit.
|
||||||
self.engine
|
self.engine
|
||||||
@ -2243,7 +2260,7 @@ mod tests {
|
|||||||
context_type: ContextType::Mock,
|
context_type: ContextType::Mock,
|
||||||
};
|
};
|
||||||
let mut exec_state = ExecState::default();
|
let mut exec_state = ExecState::default();
|
||||||
ctx.run(&program, &mut exec_state).await?;
|
ctx.run(program.into(), &mut exec_state).await?;
|
||||||
|
|
||||||
Ok(exec_state.memory)
|
Ok(exec_state.memory)
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,11 @@ pub use lsp::{
|
|||||||
copilot::Backend as CopilotLspBackend,
|
copilot::Backend as CopilotLspBackend,
|
||||||
kcl::{Backend as KclLspBackend, Server as KclLspServerSubCommand},
|
kcl::{Backend as KclLspBackend, Server as KclLspServerSubCommand},
|
||||||
};
|
};
|
||||||
pub use parsing::ast::{modify::modify_ast_for_sketch, types::FormatOptions};
|
pub use parsing::ast::{
|
||||||
|
cache::{CacheInformation, OldAstState},
|
||||||
|
modify::modify_ast_for_sketch,
|
||||||
|
types::FormatOptions,
|
||||||
|
};
|
||||||
pub use settings::types::{project::ProjectConfiguration, Configuration, UnitLength};
|
pub use settings::types::{project::ProjectConfiguration, Configuration, UnitLength};
|
||||||
pub use source_range::{ModuleId, SourceRange};
|
pub use source_range::{ModuleId, SourceRange};
|
||||||
|
|
||||||
@ -125,7 +129,7 @@ use crate::log::{log, logln};
|
|||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Program {
|
pub struct Program {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
ast: parsing::ast::types::Node<parsing::ast::types::Program>,
|
pub ast: parsing::ast::types::Node<parsing::ast::types::Program>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(test, feature = "lsp-test-util"))]
|
#[cfg(any(test, feature = "lsp-test-util"))]
|
||||||
|
@ -45,11 +45,14 @@ use crate::{
|
|||||||
errors::Suggestion,
|
errors::Suggestion,
|
||||||
lsp::{backend::Backend as _, util::IntoDiagnostic},
|
lsp::{backend::Backend as _, util::IntoDiagnostic},
|
||||||
parsing::{
|
parsing::{
|
||||||
ast::types::{Expr, Node, VariableKind},
|
ast::{
|
||||||
|
cache::{CacheInformation, OldAstState},
|
||||||
|
types::{Expr, Node, VariableKind},
|
||||||
|
},
|
||||||
token::TokenType,
|
token::TokenType,
|
||||||
PIPE_OPERATOR,
|
PIPE_OPERATOR,
|
||||||
},
|
},
|
||||||
ExecState, ModuleId, Program, SourceRange,
|
ModuleId, Program, SourceRange,
|
||||||
};
|
};
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
@ -105,6 +108,12 @@ pub struct Backend {
|
|||||||
pub token_map: DashMap<String, Vec<crate::parsing::token::Token>>,
|
pub token_map: DashMap<String, Vec<crate::parsing::token::Token>>,
|
||||||
/// AST maps.
|
/// AST maps.
|
||||||
pub ast_map: DashMap<String, Node<crate::parsing::ast::types::Program>>,
|
pub ast_map: DashMap<String, Node<crate::parsing::ast::types::Program>>,
|
||||||
|
/// Last successful execution.
|
||||||
|
/// This gets set to None when execution errors, or we want to bust the cache on purpose to
|
||||||
|
/// force a re-execution.
|
||||||
|
/// We do not need to manually bust the cache for changed units, that's handled by the cache
|
||||||
|
/// information.
|
||||||
|
pub last_successful_ast_state: Arc<RwLock<Option<OldAstState>>>,
|
||||||
/// Memory maps.
|
/// Memory maps.
|
||||||
pub memory_map: DashMap<String, crate::executor::ProgramMemory>,
|
pub memory_map: DashMap<String, crate::executor::ProgramMemory>,
|
||||||
/// Current code.
|
/// Current code.
|
||||||
@ -189,6 +198,7 @@ impl Backend {
|
|||||||
diagnostics_map: Default::default(),
|
diagnostics_map: Default::default(),
|
||||||
symbols_map: Default::default(),
|
symbols_map: Default::default(),
|
||||||
semantic_tokens_map: Default::default(),
|
semantic_tokens_map: Default::default(),
|
||||||
|
last_successful_ast_state: Default::default(),
|
||||||
is_initialized: Default::default(),
|
is_initialized: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -262,6 +272,13 @@ impl crate::lsp::backend::Backend for Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn inner_on_change(&self, params: TextDocumentItem, force: bool) {
|
async fn inner_on_change(&self, params: TextDocumentItem, force: bool) {
|
||||||
|
if force {
|
||||||
|
// Bust the execution cache.
|
||||||
|
let mut old_ast_state = self.last_successful_ast_state.write().await;
|
||||||
|
*old_ast_state = None;
|
||||||
|
drop(old_ast_state);
|
||||||
|
}
|
||||||
|
|
||||||
let filename = params.uri.to_string();
|
let filename = params.uri.to_string();
|
||||||
// We already updated the code map in the shared backend.
|
// We already updated the code map in the shared backend.
|
||||||
|
|
||||||
@ -674,23 +691,43 @@ impl Backend {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut exec_state = ExecState::default();
|
let mut last_successful_ast_state = self.last_successful_ast_state.write().await;
|
||||||
|
|
||||||
// Clear the scene, before we execute so it's not fugly as shit.
|
let mut exec_state = if let Some(last_successful_ast_state) = last_successful_ast_state.clone() {
|
||||||
executor_ctx
|
last_successful_ast_state.exec_state
|
||||||
.engine
|
} else {
|
||||||
.clear_scene(&mut exec_state.id_generator, SourceRange::default())
|
Default::default()
|
||||||
.await?;
|
};
|
||||||
|
|
||||||
if let Err(err) = executor_ctx.run(ast, &mut exec_state).await {
|
if let Err(err) = executor_ctx
|
||||||
|
.run(
|
||||||
|
CacheInformation {
|
||||||
|
old: last_successful_ast_state.clone(),
|
||||||
|
new_ast: ast.ast.clone(),
|
||||||
|
},
|
||||||
|
&mut exec_state,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
self.memory_map.remove(params.uri.as_str());
|
self.memory_map.remove(params.uri.as_str());
|
||||||
self.add_to_diagnostics(params, &[err], false).await;
|
self.add_to_diagnostics(params, &[err], false).await;
|
||||||
|
|
||||||
|
// Update the last successful ast state to be None.
|
||||||
|
*last_successful_ast_state = None;
|
||||||
|
|
||||||
// Since we already published the diagnostics we don't really care about the error
|
// Since we already published the diagnostics we don't really care about the error
|
||||||
// string.
|
// string.
|
||||||
return Err(anyhow::anyhow!("failed to execute code"));
|
return Err(anyhow::anyhow!("failed to execute code"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the last successful ast state.
|
||||||
|
*last_successful_ast_state = Some(OldAstState {
|
||||||
|
ast: ast.ast.clone(),
|
||||||
|
exec_state: exec_state.clone(),
|
||||||
|
settings: executor_ctx.settings.clone(),
|
||||||
|
});
|
||||||
|
drop(last_successful_ast_state);
|
||||||
|
|
||||||
self.memory_map
|
self.memory_map
|
||||||
.insert(params.uri.to_string(), exec_state.memory.clone());
|
.insert(params.uri.to_string(), exec_state.memory.clone());
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ pub async fn kcl_lsp_server(execute: bool) -> Result<crate::lsp::kcl::Backend> {
|
|||||||
can_send_telemetry: true,
|
can_send_telemetry: true,
|
||||||
executor_ctx: Arc::new(tokio::sync::RwLock::new(executor_ctx)),
|
executor_ctx: Arc::new(tokio::sync::RwLock::new(executor_ctx)),
|
||||||
can_execute: Arc::new(tokio::sync::RwLock::new(can_execute)),
|
can_execute: Arc::new(tokio::sync::RwLock::new(can_execute)),
|
||||||
|
last_successful_ast_state: Default::default(),
|
||||||
is_initialized: Default::default(),
|
is_initialized: Default::default(),
|
||||||
})
|
})
|
||||||
.custom_method("kcl/updateUnits", crate::lsp::kcl::Backend::update_units)
|
.custom_method("kcl/updateUnits", crate::lsp::kcl::Backend::update_units)
|
||||||
|
373
src/wasm-lib/kcl/src/parsing/ast/cache.rs
Normal file
373
src/wasm-lib/kcl/src/parsing/ast/cache.rs
Normal file
@ -0,0 +1,373 @@
|
|||||||
|
//! Functions for helping with caching an ast and finding the parts the changed.
|
||||||
|
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
executor::ExecState,
|
||||||
|
parsing::ast::types::{Node, Program},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Information for the caching an AST and smartly re-executing it if we can.
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct CacheInformation {
|
||||||
|
/// The old information.
|
||||||
|
pub old: Option<OldAstState>,
|
||||||
|
/// The new ast to executed.
|
||||||
|
pub new_ast: Node<Program>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The old ast and program memory.
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct OldAstState {
|
||||||
|
/// The ast.
|
||||||
|
pub ast: Node<Program>,
|
||||||
|
/// The exec state.
|
||||||
|
pub exec_state: ExecState,
|
||||||
|
/// The last settings used for execution.
|
||||||
|
pub settings: crate::executor::ExecutorSettings,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::Program> for CacheInformation {
|
||||||
|
fn from(program: crate::Program) -> Self {
|
||||||
|
CacheInformation {
|
||||||
|
old: None,
|
||||||
|
new_ast: program.ast,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The result of a cache check.
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct CacheResult {
|
||||||
|
/// Should we clear the scene and start over?
|
||||||
|
pub clear_scene: bool,
|
||||||
|
/// The program that needs to be executed.
|
||||||
|
pub program: Node<Program>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given an old ast, old program memory and new ast, find the parts of the code that need to be
|
||||||
|
// re-executed.
|
||||||
|
// This function should never error, because in the case of any internal error, we should just pop
|
||||||
|
// the cache.
|
||||||
|
pub fn get_changed_program(
|
||||||
|
info: CacheInformation,
|
||||||
|
new_settings: &crate::executor::ExecutorSettings,
|
||||||
|
) -> Option<CacheResult> {
|
||||||
|
let Some(old) = info.old else {
|
||||||
|
// We have no old info, we need to re-execute the whole thing.
|
||||||
|
return Some(CacheResult {
|
||||||
|
clear_scene: true,
|
||||||
|
program: info.new_ast,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// If the settings are different we need to bust the cache.
|
||||||
|
// We specifically do this before checking if they are the exact same.
|
||||||
|
if old.settings != *new_settings {
|
||||||
|
return Some(CacheResult {
|
||||||
|
clear_scene: true,
|
||||||
|
program: info.new_ast,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the ASTs are the EXACT same we return None.
|
||||||
|
// We don't even need to waste time computing the digests.
|
||||||
|
if old.ast == info.new_ast {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut old_ast = old.ast.inner;
|
||||||
|
old_ast.compute_digest();
|
||||||
|
let mut new_ast = info.new_ast.inner.clone();
|
||||||
|
new_ast.compute_digest();
|
||||||
|
|
||||||
|
// Check if the digest is the same.
|
||||||
|
if old_ast.digest == new_ast.digest {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the changes were only to Non-code areas, like comments or whitespace.
|
||||||
|
|
||||||
|
// For any unhandled cases just re-execute the whole thing.
|
||||||
|
Some(CacheResult {
|
||||||
|
clear_scene: true,
|
||||||
|
program: info.new_ast,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
async fn execute(program: &crate::Program) -> Result<ExecState> {
|
||||||
|
let ctx = crate::executor::ExecutorContext {
|
||||||
|
engine: Arc::new(Box::new(crate::engine::conn_mock::EngineConnection::new().await?)),
|
||||||
|
fs: Arc::new(crate::fs::FileManager::new()),
|
||||||
|
stdlib: Arc::new(crate::std::StdLib::new()),
|
||||||
|
settings: Default::default(),
|
||||||
|
context_type: crate::executor::ContextType::Mock,
|
||||||
|
};
|
||||||
|
let mut exec_state = crate::executor::ExecState::default();
|
||||||
|
ctx.run(program.clone().into(), &mut exec_state).await?;
|
||||||
|
|
||||||
|
Ok(exec_state)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Easy case where we have no old ast and memory.
|
||||||
|
// We need to re-execute everything.
|
||||||
|
#[test]
|
||||||
|
fn test_get_changed_program_no_old_information() {
|
||||||
|
let new = r#"// Remove the end face for the extrusion.
|
||||||
|
firstSketch = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-12, 12], %)
|
||||||
|
|> line([24, 0], %)
|
||||||
|
|> line([0, -24], %)
|
||||||
|
|> line([-24, 0], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(6, %)
|
||||||
|
|
||||||
|
// Remove the end face for the extrusion.
|
||||||
|
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||||
|
let program = crate::Program::parse_no_errs(new).unwrap().ast;
|
||||||
|
|
||||||
|
let result = get_changed_program(
|
||||||
|
CacheInformation {
|
||||||
|
old: None,
|
||||||
|
new_ast: program.clone(),
|
||||||
|
},
|
||||||
|
&Default::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(result.is_some());
|
||||||
|
|
||||||
|
let result = result.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(result.program, program);
|
||||||
|
assert!(result.clear_scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn test_get_changed_program_same_code() {
|
||||||
|
let new = r#"// Remove the end face for the extrusion.
|
||||||
|
firstSketch = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-12, 12], %)
|
||||||
|
|> line([24, 0], %)
|
||||||
|
|> line([0, -24], %)
|
||||||
|
|> line([-24, 0], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(6, %)
|
||||||
|
|
||||||
|
// Remove the end face for the extrusion.
|
||||||
|
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||||
|
let program = crate::Program::parse_no_errs(new).unwrap();
|
||||||
|
|
||||||
|
let executed = execute(&program).await.unwrap();
|
||||||
|
|
||||||
|
let result = get_changed_program(
|
||||||
|
CacheInformation {
|
||||||
|
old: Some(OldAstState {
|
||||||
|
ast: program.ast.clone(),
|
||||||
|
exec_state: executed,
|
||||||
|
settings: Default::default(),
|
||||||
|
}),
|
||||||
|
new_ast: program.ast.clone(),
|
||||||
|
},
|
||||||
|
&Default::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(result, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn test_get_changed_program_same_code_changed_whitespace() {
|
||||||
|
let old = r#" // Remove the end face for the extrusion.
|
||||||
|
firstSketch = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-12, 12], %)
|
||||||
|
|> line([24, 0], %)
|
||||||
|
|> line([0, -24], %)
|
||||||
|
|> line([-24, 0], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(6, %)
|
||||||
|
|
||||||
|
// Remove the end face for the extrusion.
|
||||||
|
shell({ faces = ['end'], thickness = 0.25 }, firstSketch) "#;
|
||||||
|
|
||||||
|
let new = r#"// Remove the end face for the extrusion.
|
||||||
|
firstSketch = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-12, 12], %)
|
||||||
|
|> line([24, 0], %)
|
||||||
|
|> line([0, -24], %)
|
||||||
|
|> line([-24, 0], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(6, %)
|
||||||
|
|
||||||
|
// Remove the end face for the extrusion.
|
||||||
|
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||||
|
let program_old = crate::Program::parse_no_errs(old).unwrap();
|
||||||
|
|
||||||
|
let executed = execute(&program_old).await.unwrap();
|
||||||
|
|
||||||
|
let program_new = crate::Program::parse_no_errs(new).unwrap();
|
||||||
|
|
||||||
|
let result = get_changed_program(
|
||||||
|
CacheInformation {
|
||||||
|
old: Some(OldAstState {
|
||||||
|
ast: program_old.ast.clone(),
|
||||||
|
exec_state: executed,
|
||||||
|
settings: Default::default(),
|
||||||
|
}),
|
||||||
|
new_ast: program_new.ast.clone(),
|
||||||
|
},
|
||||||
|
&Default::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(result, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn test_get_changed_program_same_code_changed_code_comment_start_of_program() {
|
||||||
|
let old = r#" // Removed the end face for the extrusion.
|
||||||
|
firstSketch = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-12, 12], %)
|
||||||
|
|> line([24, 0], %)
|
||||||
|
|> line([0, -24], %)
|
||||||
|
|> line([-24, 0], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(6, %)
|
||||||
|
|
||||||
|
// Remove the end face for the extrusion.
|
||||||
|
shell({ faces = ['end'], thickness = 0.25 }, firstSketch) "#;
|
||||||
|
|
||||||
|
let new = r#"// Remove the end face for the extrusion.
|
||||||
|
firstSketch = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-12, 12], %)
|
||||||
|
|> line([24, 0], %)
|
||||||
|
|> line([0, -24], %)
|
||||||
|
|> line([-24, 0], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(6, %)
|
||||||
|
|
||||||
|
// Remove the end face for the extrusion.
|
||||||
|
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||||
|
let program_old = crate::Program::parse_no_errs(old).unwrap();
|
||||||
|
|
||||||
|
let executed = execute(&program_old).await.unwrap();
|
||||||
|
|
||||||
|
let program_new = crate::Program::parse_no_errs(new).unwrap();
|
||||||
|
|
||||||
|
let result = get_changed_program(
|
||||||
|
CacheInformation {
|
||||||
|
old: Some(OldAstState {
|
||||||
|
ast: program_old.ast.clone(),
|
||||||
|
exec_state: executed,
|
||||||
|
settings: Default::default(),
|
||||||
|
}),
|
||||||
|
new_ast: program_new.ast.clone(),
|
||||||
|
},
|
||||||
|
&Default::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(result, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn test_get_changed_program_same_code_changed_code_comments() {
|
||||||
|
let old = r#" // Removed the end face for the extrusion.
|
||||||
|
firstSketch = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-12, 12], %)
|
||||||
|
|> line([24, 0], %)
|
||||||
|
|> line([0, -24], %)
|
||||||
|
|> line([-24, 0], %) // my thing
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(6, %)
|
||||||
|
|
||||||
|
// Remove the end face for the extrusion.
|
||||||
|
shell({ faces = ['end'], thickness = 0.25 }, firstSketch) "#;
|
||||||
|
|
||||||
|
let new = r#"// Remove the end face for the extrusion.
|
||||||
|
firstSketch = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-12, 12], %)
|
||||||
|
|> line([24, 0], %)
|
||||||
|
|> line([0, -24], %)
|
||||||
|
|> line([-24, 0], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(6, %)
|
||||||
|
|
||||||
|
// Remove the end face for the extrusion.
|
||||||
|
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||||
|
let program_old = crate::Program::parse_no_errs(old).unwrap();
|
||||||
|
|
||||||
|
let executed = execute(&program_old).await.unwrap();
|
||||||
|
|
||||||
|
let program_new = crate::Program::parse_no_errs(new).unwrap();
|
||||||
|
|
||||||
|
let result = get_changed_program(
|
||||||
|
CacheInformation {
|
||||||
|
old: Some(OldAstState {
|
||||||
|
ast: program_old.ast.clone(),
|
||||||
|
exec_state: executed,
|
||||||
|
settings: Default::default(),
|
||||||
|
}),
|
||||||
|
new_ast: program_new.ast.clone(),
|
||||||
|
},
|
||||||
|
&Default::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(result.is_some());
|
||||||
|
|
||||||
|
let result = result.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(result.program, program_new.ast);
|
||||||
|
assert!(result.clear_scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Changing the units with the exact same file should bust the cache.
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn test_get_changed_program_same_code_but_different_units() {
|
||||||
|
let new = r#"// Remove the end face for the extrusion.
|
||||||
|
firstSketch = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-12, 12], %)
|
||||||
|
|> line([24, 0], %)
|
||||||
|
|> line([0, -24], %)
|
||||||
|
|> line([-24, 0], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(6, %)
|
||||||
|
|
||||||
|
// Remove the end face for the extrusion.
|
||||||
|
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||||
|
let program = crate::Program::parse_no_errs(new).unwrap();
|
||||||
|
|
||||||
|
let executed = execute(&program).await.unwrap();
|
||||||
|
|
||||||
|
let result = get_changed_program(
|
||||||
|
CacheInformation {
|
||||||
|
old: Some(OldAstState {
|
||||||
|
ast: program.ast.clone(),
|
||||||
|
exec_state: executed,
|
||||||
|
settings: Default::default(),
|
||||||
|
}),
|
||||||
|
new_ast: program.ast.clone(),
|
||||||
|
},
|
||||||
|
&crate::ExecutorSettings {
|
||||||
|
units: crate::UnitLength::Cm,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(result.is_some());
|
||||||
|
|
||||||
|
let result = result.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(result.program, program.ast);
|
||||||
|
assert!(result.clear_scene);
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
pub(crate) mod cache;
|
||||||
pub(crate) mod digest;
|
pub(crate) mod digest;
|
||||||
pub(crate) mod execute;
|
pub(crate) mod execute;
|
||||||
pub mod modify;
|
pub mod modify;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use crate::parsing::ast::types::{BinaryPart, BodyItem, Expr, LiteralIdentifier, MemberObject};
|
use crate::{
|
||||||
use crate::source_range::ModuleId;
|
parsing::ast::types::{BinaryPart, BodyItem, Expr, LiteralIdentifier, MemberObject},
|
||||||
|
source_range::ModuleId,
|
||||||
|
};
|
||||||
|
|
||||||
impl BodyItem {
|
impl BodyItem {
|
||||||
pub fn module_id(&self) -> ModuleId {
|
pub fn module_id(&self) -> ModuleId {
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
// TODO optimise size of CompilationError
|
// TODO optimise size of CompilationError
|
||||||
#![allow(clippy::result_large_err)]
|
#![allow(clippy::result_large_err)]
|
||||||
|
|
||||||
|
use super::CompilationError;
|
||||||
use crate::{
|
use crate::{
|
||||||
parsing::ast::types::{BinaryExpression, BinaryOperator, BinaryPart, Node},
|
parsing::ast::types::{BinaryExpression, BinaryOperator, BinaryPart, Node},
|
||||||
SourceRange,
|
SourceRange,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::CompilationError;
|
|
||||||
|
|
||||||
/// Parses a list of tokens (in infix order, i.e. as the user typed them)
|
/// Parses a list of tokens (in infix order, i.e. as the user typed them)
|
||||||
/// into a binary expression tree.
|
/// into a binary expression tree.
|
||||||
pub fn parse(infix_tokens: Vec<BinaryExpressionToken>) -> Result<Node<BinaryExpression>, CompilationError> {
|
pub fn parse(infix_tokens: Vec<BinaryExpressionToken>) -> Result<Node<BinaryExpression>, CompilationError> {
|
||||||
@ -127,8 +126,7 @@ impl From<BinaryOperator> for BinaryExpressionToken {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::parsing::ast::types::Literal;
|
use crate::{parsing::ast::types::Literal, source_range::ModuleId};
|
||||||
use crate::source_range::ModuleId;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_and_evaluate() {
|
fn parse_and_evaluate() {
|
||||||
|
@ -4,27 +4,42 @@ use std::{str::FromStr, sync::Arc};
|
|||||||
|
|
||||||
use futures::stream::TryStreamExt;
|
use futures::stream::TryStreamExt;
|
||||||
use gloo_utils::format::JsValueSerdeExt;
|
use gloo_utils::format::JsValueSerdeExt;
|
||||||
use kcl_lib::{CoreDump, EngineManager, ExecState, ModuleId, Program};
|
use kcl_lib::{CacheInformation, CoreDump, EngineManager, ExecState, ModuleId, OldAstState, Program};
|
||||||
|
use tokio::sync::RwLock;
|
||||||
use tower_lsp::{LspService, Server};
|
use tower_lsp::{LspService, Server};
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
/// A static mutable lock for updating the last successful execution state for the cache.
|
||||||
|
static ref OLD_AST_MEMORY: Arc<RwLock<Option<OldAstState>>> = Default::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the old ast memory from the lock, this should never fail since
|
||||||
|
// in failure scenarios we should just bust the cache and send back None as the previous
|
||||||
|
// state.
|
||||||
|
async fn read_old_ast_memory() -> Option<OldAstState> {
|
||||||
|
let lock = OLD_AST_MEMORY.read().await;
|
||||||
|
lock.clone()
|
||||||
|
}
|
||||||
|
|
||||||
// wasm_bindgen wrapper for execute
|
// wasm_bindgen wrapper for execute
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub async fn execute_wasm(
|
pub async fn execute_wasm(
|
||||||
program_str: &str,
|
program_str: &str,
|
||||||
memory_str: &str,
|
program_memory_override_str: &str,
|
||||||
id_generator_str: &str,
|
|
||||||
units: &str,
|
units: &str,
|
||||||
engine_manager: kcl_lib::wasm_engine::EngineCommandManager,
|
engine_manager: kcl_lib::wasm_engine::EngineCommandManager,
|
||||||
fs_manager: kcl_lib::wasm_engine::FileSystemManager,
|
fs_manager: kcl_lib::wasm_engine::FileSystemManager,
|
||||||
project_directory: Option<String>,
|
|
||||||
is_mock: bool,
|
|
||||||
) -> Result<JsValue, String> {
|
) -> Result<JsValue, String> {
|
||||||
console_error_panic_hook::set_once();
|
console_error_panic_hook::set_once();
|
||||||
|
|
||||||
let program: Program = serde_json::from_str(program_str).map_err(|e| e.to_string())?;
|
let program: Program = serde_json::from_str(program_str).map_err(|e| e.to_string())?;
|
||||||
let memory: kcl_lib::exec::ProgramMemory = serde_json::from_str(memory_str).map_err(|e| e.to_string())?;
|
let program_memory_override: Option<kcl_lib::exec::ProgramMemory> =
|
||||||
let id_generator: kcl_lib::exec::IdGenerator = serde_json::from_str(id_generator_str).map_err(|e| e.to_string())?;
|
serde_json::from_str(program_memory_override_str).map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
// If we have a program memory override, assume we are in mock mode.
|
||||||
|
// You cannot override the memory in non-mock mode.
|
||||||
|
let is_mock = program_memory_override.is_some();
|
||||||
|
|
||||||
let units = kcl_lib::UnitLength::from_str(units).map_err(|e| e.to_string())?;
|
let units = kcl_lib::UnitLength::from_str(units).map_err(|e| e.to_string())?;
|
||||||
let ctx = if is_mock {
|
let ctx = if is_mock {
|
||||||
@ -33,14 +48,55 @@ pub async fn execute_wasm(
|
|||||||
kcl_lib::ExecutorContext::new(engine_manager, fs_manager, units).await?
|
kcl_lib::ExecutorContext::new(engine_manager, fs_manager, units).await?
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut exec_state = ExecState {
|
let mut exec_state = ExecState { ..ExecState::default() };
|
||||||
memory,
|
|
||||||
id_generator,
|
|
||||||
project_directory,
|
|
||||||
..ExecState::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
ctx.run(&program, &mut exec_state).await.map_err(String::from)?;
|
let mut old_ast_memory = None;
|
||||||
|
|
||||||
|
// Populate from the old exec state if it exists.
|
||||||
|
if let Some(program_memory_override) = program_memory_override {
|
||||||
|
exec_state.memory = program_memory_override;
|
||||||
|
} else {
|
||||||
|
// If we are in mock mode, we don't want to use any cache.
|
||||||
|
if let Some(old) = read_old_ast_memory().await {
|
||||||
|
exec_state = old.exec_state.clone();
|
||||||
|
old_ast_memory = Some(old);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(err) = ctx
|
||||||
|
.run(
|
||||||
|
CacheInformation {
|
||||||
|
old: old_ast_memory,
|
||||||
|
new_ast: program.ast.clone(),
|
||||||
|
},
|
||||||
|
&mut exec_state,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(String::from)
|
||||||
|
{
|
||||||
|
if !is_mock {
|
||||||
|
// We don't use the cache in mock mode.
|
||||||
|
let mut current_cache = OLD_AST_MEMORY.write().await;
|
||||||
|
// Set the cache to None.
|
||||||
|
*current_cache = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Throw the error.
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !is_mock {
|
||||||
|
// We don't use the cache in mock mode.
|
||||||
|
let mut current_cache = OLD_AST_MEMORY.write().await;
|
||||||
|
|
||||||
|
// If we aren't in mock mode, save this as the last successful execution to the cache.
|
||||||
|
*current_cache = Some(OldAstState {
|
||||||
|
ast: program.ast.clone(),
|
||||||
|
exec_state: exec_state.clone(),
|
||||||
|
settings: ctx.settings.clone(),
|
||||||
|
});
|
||||||
|
drop(current_cache);
|
||||||
|
}
|
||||||
|
|
||||||
// The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
|
// The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
|
||||||
// gloo-serialize crate instead.
|
// gloo-serialize crate instead.
|
||||||
|
@ -11,7 +11,7 @@ async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, Modu
|
|||||||
let program = Program::parse_no_errs(code)?;
|
let program = Program::parse_no_errs(code)?;
|
||||||
let ctx = kcl_lib::ExecutorContext::new_with_default_client(Default::default()).await?;
|
let ctx = kcl_lib::ExecutorContext::new_with_default_client(Default::default()).await?;
|
||||||
let mut exec_state = ExecState::default();
|
let mut exec_state = ExecState::default();
|
||||||
ctx.run(&program, &mut exec_state).await?;
|
ctx.run(program.clone().into(), &mut exec_state).await?;
|
||||||
|
|
||||||
// We need to get the sketch ID.
|
// We need to get the sketch ID.
|
||||||
// Get the sketch ID from memory.
|
// Get the sketch ID from memory.
|
||||||
|
Reference in New Issue
Block a user