Add stepping and breakpoints to debugger

This commit is contained in:
Nilay Majorwar 2021-12-15 22:08:30 +05:30
parent 38247f03c8
commit 7d9fb457ff
6 changed files with 58 additions and 1 deletions

View File

@ -134,6 +134,16 @@ class ExecutionController<RS> {
return this._result;
}
/**
* Run a single step of execution
* @returns Result of execution
*/
executeStep(): StepExecutionResult<RS> {
this._result = this._engine.executeStep();
this._result.signal = "paused";
return this._result;
}
/** Asynchronously sleep for a period of time */
private async sleep(millis: number) {
return new Promise<void>((resolve) => setTimeout(resolve, millis));

View File

@ -22,6 +22,10 @@ export type WorkerRequestData =
type: "Execute";
params: { interval?: number };
}
| {
type: "ExecuteStep";
params?: null;
}
| {
type: "Pause";
params?: null;

View File

@ -64,7 +64,6 @@ const updateBreakpoints = (points: number[]) => {
* and return result of execution.
*/
const execute = (interval?: number) => {
console.info(`Executing at interval ${interval}`);
_controller!.executeAll({
interval,
onResult: (res) => postMessage(resultMessage(res)),
@ -79,12 +78,21 @@ const pauseExecution = async () => {
postMessage(ackMessage("pause"));
};
/**
* Run a single execution step
*/
const executeStep = () => {
const result = _controller!.executeStep();
postMessage(resultMessage(result));
};
addEventListener("message", async (ev: MessageEvent<WorkerRequestData>) => {
if (ev.data.type === "Init") return initController();
if (ev.data.type === "Reset") return resetController();
if (ev.data.type === "Prepare") return prepare(ev.data.params);
if (ev.data.type === "Execute") return execute(ev.data.params.interval);
if (ev.data.type === "Pause") return await pauseExecution();
if (ev.data.type === "ExecuteStep") return executeStep();
if (ev.data.type === "UpdateBreakpoints")
return updateBreakpoints(ev.data.params.points);
throw new Error("Invalid worker message type");

View File

@ -57,6 +57,21 @@ export const Mainframe = () => {
await execController.pauseExecution();
};
/** Run a single step of execution */
const executeStep = async () => {
// Check if controller is paused
if (execController.state !== "paused") {
console.error("Controller not paused");
return;
}
// Run and update execution states
const result = await execController.executeStep();
setRendererState(result.rendererState);
setCodeHighlights(result.nextStepLocation || undefined);
setOutput((o) => (o || "") + (result.output || ""));
};
/** Resume the currently paused execution */
const resumeExecution = async () => {
// Check if controller is indeed paused
@ -125,6 +140,7 @@ export const Mainframe = () => {
onRun={runProgram}
onPause={pauseExecution}
onResume={resumeExecution}
onStep={executeStep}
onStop={stopExecution}
/>
)}

View File

@ -24,6 +24,7 @@ const DebugControls = (props: {
paused: boolean;
onPause: () => void;
onResume: () => void;
onStep: () => void;
onStop: () => void;
}) => {
return (
@ -37,6 +38,7 @@ const DebugControls = (props: {
<Button
small
title="Step"
onClick={props.onStep}
disabled={!props.paused}
icon={<Icon icon="step-forward" intent="warning" />}
/>
@ -55,6 +57,7 @@ type Props = {
onRun: () => void;
onPause: () => void;
onResume: () => void;
onStep: () => void;
onStop: () => void;
};
@ -68,6 +71,7 @@ export const ExecutionControls = (props: Props) => {
paused={props.state === "paused"}
onPause={props.onPause}
onResume={props.onResume}
onStep={props.onStep}
onStop={props.onStop}
/>
)}

View File

@ -140,6 +140,20 @@ export const useExecController = <RS>() => {
});
}, []);
/**
* Run a single step of execution
* @return Execution result
*/
const executeStep = React.useCallback(async () => {
const res = await requestWorker(
{ type: "ExecuteStep" },
(res) => res.type !== "result"
);
if (res.type !== "result") throw new Error("Something unexpected happened");
if (!res.data.nextStepLocation) setWorkerState("done");
return res.data;
}, []);
/**
* Execute the code loaded into the engine
* @param onResult Callback used when an execution result is received
@ -172,6 +186,7 @@ export const useExecController = <RS>() => {
prepare,
pauseExecution,
execute,
executeStep,
updateBreakpoints,
};
};