diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 78174f6..a9846fa 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -7,5 +7,6 @@ module.exports = { plugins: ["react-refresh"], rules: { "react-refresh/only-export-components": ["warn", { allowConstantExport: true }], + "no-console": ["warn", { allow: ["warn", "error"] }], }, }; diff --git a/package-lock.json b/package-lock.json index ee4960c..c626235 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,6 +39,7 @@ "react-json-view-compare": "^2.0.2", "react-katex": "^3.0.1", "react-redux": "^9.1.2", + "react-toastify": "^10.0.6", "scale-codec": "^0.13.0", "tailwind-merge": "^2.4.0", "tailwindcss-animate": "^1.0.7", @@ -6370,6 +6371,18 @@ } } }, + "node_modules/react-toastify": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.6.tgz", + "integrity": "sha512-yYjp+omCDf9lhZcrZHKbSq7YMuK0zcYkDFTzfRFgTXkTFHZ1ToxwAonzA4JI5CxA91JpjFLmwEsZEgfYfOqI1A==", + "dependencies": { + "clsx": "^2.1.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", diff --git a/package.json b/package.json index 2bacc46..ec7d436 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "react-json-view-compare": "^2.0.2", "react-katex": "^3.0.1", "react-redux": "^9.1.2", + "react-toastify": "^10.0.6", "scale-codec": "^0.13.0", "tailwind-merge": "^2.4.0", "tailwindcss-animate": "^1.0.7", diff --git a/src/App.tsx b/src/App.tsx index 418aeb9..cb7f935 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,7 @@ import "./App.css"; +import { ToastContainer } from "react-toastify"; +import "react-toastify/dist/ReactToastify.css"; + import { Button } from "@/components/ui/button"; import { useCallback, useEffect, useRef } from "react"; import { Instructions } from "./components/Instructions"; @@ -51,6 +54,7 @@ import { setPvmInitialized, } from "@/store/debugger/debuggerSlice.ts"; import { MemoryPreview } from "@/components/MemoryPreview"; +import { logger } from "./utils/loggerService"; function App() { const { @@ -131,12 +135,12 @@ function App() { try { const result = disassemblify(new Uint8Array(newProgram)); - console.info("Disassembly result:", result); + logger.info("Disassembly result:", result); dispatch(setProgramPreviewResult(result)); dispatch(setAllWorkersCurrentInstruction(result?.[0])); dispatch(setPvmInitialized(true)); } catch (e) { - console.log("Error disassembling program", e); + console.error("Error disassembling program", e); } }, [dispatch], @@ -206,7 +210,7 @@ function App() { }; const handlePvmTypeChange = async (selectedPvms: SelectedPvmWithPayload[]) => { - console.log("selectedPvms vs workers ", selectedPvms, workers); + logger.debug("selectedPvms vs workers ", selectedPvms, workers); await Promise.all( workers.map((worker: WorkerState) => { @@ -216,13 +220,13 @@ function App() { await Promise.all( selectedPvms.map(async ({ id, type, params }) => { - console.log("Selected PVM type", id, type, params); + logger.info("Selected PVM type", id, type, params); if (workers.find((worker: WorkerState) => worker.id === id)) { - console.log("Worker already initialized"); + logger.info("Worker already initialized"); // TODO: for now just initialize the worker one more time } - console.log("Worker not initialized"); + logger.info("Worker not initialized"); if (id === AvailablePvms.POLKAVM) { await dispatch(createWorker(AvailablePvms.POLKAVM)).unwrap(); @@ -246,7 +250,6 @@ function App() { }), ).unwrap(); } else if (type === AvailablePvms.WASM_FILE) { - console.log("go wasm file!", id, type, params); await dispatch(createWorker(id)).unwrap(); await dispatch( loadWorker({ @@ -419,6 +422,7 @@ function App() { + ); } diff --git a/src/components/KnowledgeBase/index.tsx b/src/components/KnowledgeBase/index.tsx index b6c768d..50bca03 100644 --- a/src/components/KnowledgeBase/index.tsx +++ b/src/components/KnowledgeBase/index.tsx @@ -48,7 +48,7 @@ export const KnowledgeBase = ({ currentInstruction }: { currentInstruction: Curr

{instruction?.name}

- +

{currentInstructionFromKnowledgeBase?.description}

diff --git a/src/components/ProgramLoader/Assembly.tsx b/src/components/ProgramLoader/Assembly.tsx index 6e8a671..57e2e36 100644 --- a/src/components/ProgramLoader/Assembly.tsx +++ b/src/components/ProgramLoader/Assembly.tsx @@ -144,7 +144,7 @@ export const Assembly = ({ return; } } - console.log(e); + console.error(e); onProgramLoad(undefined); setError(`${e}`); } diff --git a/src/components/ProgramTextLoader/index.tsx b/src/components/ProgramTextLoader/index.tsx index 5e48361..83baf5b 100644 --- a/src/components/ProgramTextLoader/index.tsx +++ b/src/components/ProgramTextLoader/index.tsx @@ -27,7 +27,7 @@ export const ProgramTextLoader = ({ setProgram(Array.prototype.slice.call(parsedBlob.buffer)); } catch (e) { console.warn(e); - console.log("wrong binary file"); + console.error("wrong binary file"); setIsInvalidProgram(true); } } else { @@ -36,7 +36,8 @@ export const ProgramTextLoader = ({ setProgram(JSON.parse(newInput)); setIsInvalidProgram(false); } catch (e) { - console.log("wrong json"); + // TODO only validate on submit + console.error("wrong json"); setIsInvalidProgram(true); setProgram(); } diff --git a/src/components/PvmSelect/index.tsx b/src/components/PvmSelect/index.tsx index 99331f6..472e242 100644 --- a/src/components/PvmSelect/index.tsx +++ b/src/components/PvmSelect/index.tsx @@ -45,7 +45,7 @@ const fetchWasmMetadata = async (url: string): Promise alert("Invalid URL"); } } catch (error) { - console.log(error); + console.error(error); alert("Invalid URL"); } return; diff --git a/src/context/NumeralSystemProvider.tsx b/src/context/NumeralSystemProvider.tsx index d318b09..0587a92 100644 --- a/src/context/NumeralSystemProvider.tsx +++ b/src/context/NumeralSystemProvider.tsx @@ -3,9 +3,8 @@ import { NumeralSystem } from "./NumeralSystem"; export const NumeralSystemContext = createContext({ numeralSystem: NumeralSystem.DECIMAL, - setNumeralSystem: (numeralSystem: NumeralSystem) => { - console.log(numeralSystem); - }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + setNumeralSystem: (_: NumeralSystem) => {}, }); export const NumeralSystemProvider = ({ children }: { children: ReactNode }) => { diff --git a/src/globals.css b/src/globals.css index d0863a9..1e610ae 100644 --- a/src/globals.css +++ b/src/globals.css @@ -29,6 +29,8 @@ --chart-3: 197 37% 24%; --chart-4: 43 74% 66%; --chart-5: 27 87% 67%; + + --toastify-toast-width: 350px; } .dark { diff --git a/src/packages/web-worker/command-handlers/index.ts b/src/packages/web-worker/command-handlers/index.ts new file mode 100644 index 0000000..5440aed --- /dev/null +++ b/src/packages/web-worker/command-handlers/index.ts @@ -0,0 +1,9 @@ +import { runInit } from "./init"; +import { runLoad } from "./load"; +import { runStep } from "./step"; + +export default { + runInit, + runLoad, + runStep, +}; diff --git a/src/packages/web-worker/command-handlers/init.ts b/src/packages/web-worker/command-handlers/init.ts new file mode 100644 index 0000000..be157d3 --- /dev/null +++ b/src/packages/web-worker/command-handlers/init.ts @@ -0,0 +1,59 @@ +import { InitialState } from "@/types/pvm"; +import { initPvm } from "../pvm"; +import { getState, isInternalPvm, regsAsUint8 } from "../utils"; +import { CommandStatus, PvmApiInterface } from "../worker"; +import { Pvm as InternalPvmInstance } from "@typeberry/pvm-debugger-adapter"; +import { logger } from "@/utils/loggerService"; + +export type InitParams = { + pvm: PvmApiInterface | null; + program: Uint8Array; + initialState: InitialState; +}; +export type InitResponse = { + initialState: InitialState; + status: CommandStatus; + error?: unknown; +}; + +const init = async ({ pvm, program, initialState }: InitParams) => { + if (!pvm) { + throw new Error("PVM is uninitialized."); + } + if (isInternalPvm(pvm)) { + logger.info("PVM init internal", pvm); + // TODO Fix type guards + initPvm(pvm as InternalPvmInstance, program, initialState); + } else { + logger.info("PVM init external", pvm); + const gas = initialState.gas || 10000; + pvm.reset(program, regsAsUint8(initialState.regs), BigInt(gas)); + pvm.setNextProgramCounter(initialState.pc ?? 0); + pvm.setGasLeft(BigInt(gas)); + } +}; + +export const runInit = async ({ pvm, program, initialState }: InitParams): Promise => { + if (program.length === 0) { + console.warn("Skipping init, no program yet."); + return { + status: CommandStatus.SUCCESS, + initialState: {}, + }; + } + + try { + await init({ pvm, program, initialState }); + + return { + status: CommandStatus.SUCCESS, + initialState: pvm ? getState(pvm) : {}, + }; + } catch (error) { + return { + status: CommandStatus.ERROR, + error, + initialState: pvm ? getState(pvm) : {}, + }; + } +}; diff --git a/src/packages/web-worker/command-handlers/load.ts b/src/packages/web-worker/command-handlers/load.ts new file mode 100644 index 0000000..2487b00 --- /dev/null +++ b/src/packages/web-worker/command-handlers/load.ts @@ -0,0 +1,50 @@ +import { logger } from "@/utils/loggerService"; +import { loadArrayBufferAsWasm, SupportedLangs } from "../utils"; +import { CommandStatus, PvmApiInterface, PvmTypes } from "../worker"; +import { Pvm as InternalPvmInstance } from "@typeberry/pvm-debugger-adapter"; + +export type LoadParams = { type: PvmTypes; params: { url?: string; file?: Blob; lang?: SupportedLangs } }; +export type LoadResponse = { pvm: PvmApiInterface | null; status: CommandStatus; error?: unknown }; + +const load = async (args: LoadParams): Promise => { + if (args.type === PvmTypes.BUILT_IN) { + return new InternalPvmInstance(); + } else if (args.type === PvmTypes.WASM_FILE) { + const file = args.params.file; + if (!file) { + throw new Error("No PVM file"); + } + + logger.info("Load WASM from file", file); + const bytes = await file.arrayBuffer(); + return await loadArrayBufferAsWasm(bytes); + } else if (args.type === PvmTypes.WASM_URL) { + const url = args.params.url ?? ""; + const isValidUrl = Boolean(new URL(url)); + + if (!isValidUrl) { + throw new Error("Invalid PVM URL"); + } + + logger.info("Load WASM from URL", url); + const response = await fetch(url); + const bytes = await response.arrayBuffer(); + + return await loadArrayBufferAsWasm(bytes, args.params.lang); + } + + return null; +}; + +export const runLoad = async (args: LoadParams): Promise => { + try { + const pvm = await load(args); + if (pvm) { + return { pvm, status: CommandStatus.SUCCESS }; + } + } catch (error) { + return { pvm: null, status: CommandStatus.ERROR, error }; + } + + return { pvm: null, status: CommandStatus.ERROR, error: new Error("Unknown PVM type") }; +}; diff --git a/src/packages/web-worker/command-handlers/step.ts b/src/packages/web-worker/command-handlers/step.ts new file mode 100644 index 0000000..37f4b41 --- /dev/null +++ b/src/packages/web-worker/command-handlers/step.ts @@ -0,0 +1,40 @@ +import { CurrentInstruction, ExpectedState, Status } from "@/types/pvm"; +import { nextInstruction } from "../pvm"; +import { isInternalPvm, getState } from "../utils"; +import { CommandStatus, PvmApiInterface } from "../worker"; + +export type StepParams = { program: number[]; pvm: PvmApiInterface | null }; +export type StepResponse = { + status: CommandStatus; + error?: unknown; + result: CurrentInstruction | object; + state: ExpectedState; + isFinished: boolean; +}; + +const step = ({ pvm, program }: StepParams) => { + if (!pvm) { + throw new Error("PVM is uninitialized."); + } + + let isFinished: boolean; + if (isInternalPvm(pvm)) { + isFinished = pvm.nextStep() !== Status.OK; + } else { + isFinished = !pvm.nextStep(); + } + + const state = getState(pvm); + const result = nextInstruction(state.pc ?? 0, program) as unknown as CurrentInstruction; + + return { result, state, isFinished }; +}; + +export const runStep = ({ pvm, program }: StepParams): StepResponse => { + try { + const data = step({ pvm, program }); + return { status: CommandStatus.SUCCESS, ...data }; + } catch (error) { + return { status: CommandStatus.ERROR, error, isFinished: true, result: {}, state: {} }; + } +}; diff --git a/src/packages/web-worker/pvm.ts b/src/packages/web-worker/pvm.ts index 9f0bf21..f3282b9 100644 --- a/src/packages/web-worker/pvm.ts +++ b/src/packages/web-worker/pvm.ts @@ -3,7 +3,7 @@ import { createResults, instructionArgumentTypeMap, ProgramDecoder } from "@type import { ArgsDecoder, Registers } from "@typeberry/pvm-debugger-adapter"; import { byteToOpCodeMap } from "../../packages/pvm/pvm/assemblify"; import { Pvm as InternalPvmInstance, MemoryBuilder as InternalPvmMemoryBuilder } from "@typeberry/pvm-debugger-adapter"; -export const initPvm = (pvm: InternalPvmInstance, program: number[], initialState: InitialState) => { +export const initPvm = (pvm: InternalPvmInstance, program: Uint8Array, initialState: InitialState) => { const initialMemory = initialState.memory ?? []; const pageMap = initialState.pageMap ?? []; @@ -32,7 +32,6 @@ export const initPvm = (pvm: InternalPvmInstance, program: number[], initialStat const registers = new Registers(); registers.copyFrom(new Uint32Array(initialState.regs!)); pvm.reset(new Uint8Array(program), initialState.pc ?? 0, initialState.gas ?? 0, registers, memory); - return pvm; }; export const runAllInstructions = (pvm: InternalPvm, program: number[]) => { diff --git a/src/packages/web-worker/utils.ts b/src/packages/web-worker/utils.ts index 9f93588..d8b556a 100644 --- a/src/packages/web-worker/utils.ts +++ b/src/packages/web-worker/utils.ts @@ -5,6 +5,7 @@ import { createWasmPvmShell } from "@/packages/web-worker/wasmPvmShell.ts"; import "./goWasmExec.js"; import "./goWasmExec.d.ts"; import { createGoWasmPvmShell } from "@/packages/web-worker/wasmGoPvmShell.ts"; +import { logger } from "@/utils/loggerService.tsx"; export enum SupportedLangs { Go = "Go", @@ -66,13 +67,13 @@ export async function loadArrayBufferAsWasm(bytes: ArrayBuffer, lang?: Supported const go = new Go(); const wasmModule = await WebAssembly.instantiate(bytes, go.importObject); go.run(wasmModule.instance); - console.log("Go WASM module loaded", wasmModule.instance.exports); + logger.info("Go WASM module loaded", wasmModule.instance.exports); const wasmPvmShell = createGoWasmPvmShell(); wasmPvmShell.__wbg_set_wasm(wasmModule.instance.exports); return wasmPvmShell; } else { const wasmModule = await WebAssembly.instantiate(bytes, {}); - console.log("Rust WASM module loaded", wasmModule.instance.exports); + logger.info("Rust WASM module loaded", wasmModule.instance.exports); const wasmPvmShell = createWasmPvmShell(); wasmPvmShell.__wbg_set_wasm(wasmModule.instance.exports); return wasmPvmShell; @@ -80,7 +81,6 @@ export async function loadArrayBufferAsWasm(bytes: ArrayBuffer, lang?: Supported } export function getMemoryPage(pageNumber: number, pvm: PvmApiInterface | null) { - console.log("getMemoryPage", pageNumber, pvm); if (!pvm) { return []; } @@ -88,6 +88,5 @@ export function getMemoryPage(pageNumber: number, pvm: PvmApiInterface | null) { if (isInternalPvm(pvm)) { return pvm.getMemoryPage(pageNumber) || []; } - console.log("getpagedump", pageNumber, pvm.getPageDump(pageNumber)); return pvm.getPageDump(pageNumber) || []; } diff --git a/src/packages/web-worker/worker.ts b/src/packages/web-worker/worker.ts index 192a305..a668e92 100644 --- a/src/packages/web-worker/worker.ts +++ b/src/packages/web-worker/worker.ts @@ -1,15 +1,8 @@ -import { CurrentInstruction, ExpectedState, InitialState, Pvm as InternalPvm, Status } from "@/types/pvm"; -import { initPvm, nextInstruction } from "./pvm"; -import { - getMemoryPage, - getState, - isInternalPvm, - loadArrayBufferAsWasm, - regsAsUint8, - SupportedLangs, -} from "@/packages/web-worker/utils.ts"; +import { CurrentInstruction, ExpectedState, InitialState, Pvm as InternalPvm } from "@/types/pvm"; +import { getMemoryPage, SupportedLangs } from "@/packages/web-worker/utils.ts"; import { WasmPvmShellInterface } from "@/packages/web-worker/wasmPvmShell.ts"; -import { Pvm as InternalPvmInstance } from "@typeberry/pvm-debugger-adapter"; +import commandHandlers from "./command-handlers"; +import { logger } from "@/utils/loggerService"; export enum Commands { LOAD = "load", @@ -28,7 +21,7 @@ export enum PvmTypes { WASM_FILE = "wasm-file", } -export enum CommandResult { +export enum CommandStatus { SUCCESS = "success", ERROR = "error", } @@ -39,11 +32,13 @@ let pvm: PvmApiInterface | null = null; let isRunMode = false; export type TargetOnMessageParams = - | { command: Commands.LOAD; result: CommandResult } - | { command: Commands.INIT; payload: { initialState: InitialState } } + | { command: Commands.LOAD; status: CommandStatus; error?: unknown } + | { command: Commands.INIT; status: CommandStatus; error?: unknown; payload: { initialState: InitialState } } | { command: Commands.STEP; - payload: { state: ExpectedState; result: CurrentInstruction; isFinished: boolean; isRunMode: boolean }; + status: CommandStatus; + error?: unknown; + payload: { state: ExpectedState; result: CurrentInstruction | object; isFinished: boolean; isRunMode: boolean }; } | { command: Commands.RUN; payload: { state: ExpectedState; isFinished: boolean; isRunMode: boolean } } | { command: Commands.STOP; payload: { isRunMode: boolean } } @@ -56,7 +51,7 @@ export type WorkerOnMessageParams = command: Commands.LOAD; payload: { type: PvmTypes; params: { url?: string; file?: Blob; lang?: SupportedLangs } }; } - | { command: Commands.INIT; payload: { program: number[]; initialState: InitialState } } + | { command: Commands.INIT; payload: { program: Uint8Array; initialState: InitialState } } | { command: Commands.STEP; payload: { program: number[] } } | { command: Commands.RUN } | { command: Commands.STOP } @@ -64,7 +59,7 @@ export type WorkerOnMessageParams = | { command: Commands.MEMORY_RANGE; payload: { start: number; end: number } } | { command: Commands.MEMORY_SIZE }; -function postTypedMessage(msg: TargetOnMessageParams) { +export function postTypedMessage(msg: TargetOnMessageParams) { postMessage(msg); } @@ -72,107 +67,38 @@ onmessage = async (e: MessageEvent) => { if (!e.data?.command) { return; } + logger.info("Worker received message", e.data); - let result; let state; // let program; let isFinished; if (e.data.command === Commands.LOAD) { - if (e.data.payload.type === PvmTypes.BUILT_IN) { - pvm = new InternalPvmInstance(); - postMessage({ command: Commands.LOAD, result: CommandResult.SUCCESS }); - } - if (e.data.payload.type === PvmTypes.WASM_FILE) { - try { - const file = e.data.payload.params.file; - if (!file) { - throw new Error("No PVM file"); - } - - console.log("Load WASM from file", file); - const bytes = await file.arrayBuffer(); - pvm = await loadArrayBufferAsWasm(bytes, e.data.payload.params.lang); - - postTypedMessage({ command: Commands.LOAD, result: CommandResult.SUCCESS }); - } catch (error) { - console.error(error); - postTypedMessage({ command: Commands.LOAD, result: CommandResult.ERROR }); - } - } - if (e.data.payload.type === PvmTypes.WASM_URL) { - try { - const url = e.data.payload.params.url ?? ""; - const isValidUrl = Boolean(new URL(url)); - - if (!isValidUrl) { - throw new Error("Invalid PVM URL"); - } - - console.log("Load WASM from URL", url); - const response = await fetch(url); - const bytes = await response.arrayBuffer(); - pvm = await loadArrayBufferAsWasm(bytes); - - postTypedMessage({ command: Commands.LOAD, result: CommandResult.SUCCESS }); - } catch (error) { - console.error(error); - postTypedMessage({ command: Commands.LOAD, result: CommandResult.ERROR }); - } - } + const data = await commandHandlers.runLoad(e.data.payload); + pvm = data.pvm; + postTypedMessage({ command: Commands.LOAD, status: data.status, error: data.error }); } else if (e.data.command === Commands.INIT) { - if (e.data.payload.program.length === 0) { - console.warn("Skipping init, no program yet."); - postTypedMessage({ - command: Commands.INIT, - payload: { - initialState: {}, - }, - }); - } else { - if (!pvm) { - throw new Error("PVM is uninitialized."); - } - if (isInternalPvm(pvm)) { - console.log("PVM init", pvm); - pvm = initPvm(pvm as InternalPvmInstance, e.data.payload.program, e.data.payload.initialState); - } else { - console.log("PVM reset", pvm); - const gas = e.data.payload.initialState.gas || 10000; - pvm.reset( - // TODO: check root cause of this type error - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - e.data.payload.program, - regsAsUint8(e.data.payload.initialState.regs), - BigInt(gas), - ); - pvm.setNextProgramCounter(e.data.payload.initialState.pc ?? 0); - pvm.setGasLeft(BigInt(gas)); - } + const data = await commandHandlers.runInit({ + pvm, + program: e.data.payload.program, + initialState: e.data.payload.initialState, + }); - postTypedMessage({ - command: Commands.INIT, - payload: { - initialState: pvm ? getState(pvm) : {}, - }, - }); - } + postTypedMessage({ + command: Commands.INIT, + status: data.status, + error: data.error, + payload: { + initialState: data.initialState, + }, + }); } else if (e.data.command === Commands.STEP) { - if (!pvm) { - throw new Error("PVM is uninitialized."); - } - if (isInternalPvm(pvm)) { - isFinished = pvm.nextStep() !== Status.OK; - } else { - isFinished = !pvm.nextStep(); - } - if (isFinished) { - isRunMode = false; - } - state = getState(pvm); - result = nextInstruction(state.pc ?? 0, e.data.payload.program) as unknown as CurrentInstruction; + const { result, state, isFinished, status, error } = commandHandlers.runStep({ + pvm, + program: e.data.payload.program, + }); + isRunMode = !isFinished; - postTypedMessage({ command: Commands.STEP, payload: { result, state, isFinished, isRunMode } }); + postTypedMessage({ command: Commands.STEP, status, error, payload: { result, state, isFinished, isRunMode } }); } else if (e.data.command === Commands.RUN) { isRunMode = true; postTypedMessage({ command: Commands.RUN, payload: { isRunMode, isFinished: true, state: state ?? {} } }); @@ -197,14 +123,13 @@ onmessage = async (e: MessageEvent) => { // Get first page to check the memory size const memoryPage = getMemoryPage(0, pvm); - console.log("memoryPage", memoryPage); postMessage({ command: Commands.MEMORY_SIZE, // TODO fix types payload: { pageNumber: 0, memorySize: (memoryPage as unknown as Array)?.length }, }); } - // TODO uncomennet and finish implementation + // TODO uncomment and finish implementation // else if (e.data.command === Commands.MEMORY_RANGE) { // const memoryRange = Object.values(memory).flat().slice(e.data.payload.start, e.data.payload.end); // postMessage({ diff --git a/src/store/workers/workersSlice.ts b/src/store/workers/workersSlice.ts index d26bd35..5651190 100644 --- a/src/store/workers/workersSlice.ts +++ b/src/store/workers/workersSlice.ts @@ -6,6 +6,7 @@ import { Commands, PvmTypes, TargetOnMessageParams } from "@/packages/web-worker import PvmWorker from "@/packages/web-worker/worker?worker&inline"; import { SupportedLangs } from "@/packages/web-worker/utils.ts"; import { virtualTrapInstruction } from "@/utils/virtualTrapInstruction.ts"; +import { logger } from "@/utils/loggerService"; // TODO: remove this when found a workaround for BigInt support in JSON.stringify // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -71,10 +72,20 @@ export const loadWorker = createAsyncThunk( } return new Promise((resolve) => { - const messageHandler = (event: MessageEvent) => { + const messageHandler = (event: MessageEvent) => { + if ("status" in event.data && event.data.status === "error") { + logger.error(`An error occured on command ${event.data.command}`, { error: event.data.error }); + } + if (event.data.command === Commands.LOAD) { - resolve(true); - worker.worker.removeEventListener("message", messageHandler); + if (event.data.status === "success") { + resolve(true); + worker.worker.removeEventListener("message", messageHandler); + } else if (event.data.status === "error") { + resolve(false); + logger.error("Error loading PVM worker", { error: event.data.error }); + worker.worker.removeEventListener("message", messageHandler); + } } }; @@ -104,6 +115,10 @@ export const initAllWorkers = createAsyncThunk("workers/initAllWorkers", async ( }); globalMessageHandlers[worker.id] = (event: MessageEvent) => { + if ("status" in event.data && event.data.status === "error") { + logger.error(`An error occured on command ${event.data.command}`, { error: event.data.error }); + } + if (event.data.command === Commands.STEP) { const { state, isFinished } = event.data.payload; @@ -131,6 +146,7 @@ export const initAllWorkers = createAsyncThunk("workers/initAllWorkers", async ( id: worker.id, pageNumber: event.data.payload.pageNumber, data: event.data.payload.memoryPage, + isLoading: false, }), ); @@ -142,6 +158,14 @@ export const initAllWorkers = createAsyncThunk("workers/initAllWorkers", async ( worker.worker.addEventListener("message", globalMessageHandlers[worker.id]); + worker.worker.postMessage({ + command: Commands.INIT, + payload: { + initialState: debuggerState.initialState, + program: debuggerState.program, + }, + }); + worker.worker.postMessage({ command: Commands.MEMORY_SIZE, }); @@ -207,6 +231,10 @@ export const continueAllWorkers = createAsyncThunk("workers/continueAllWorkers", }) => void, ) => { const messageHandler = (event: MessageEvent) => { + if ("status" in event.data && event.data.status === "error") { + logger.error(`An error occured on command ${event.data.command}`, { error: event.data.error }); + } + if (event.data.command === Commands.STEP) { const { state, isRunMode, isFinished } = event.data.payload; const currentState = getState() as RootState; @@ -227,7 +255,7 @@ export const continueAllWorkers = createAsyncThunk("workers/continueAllWorkers", isBreakpoint: debuggerState.breakpointAddresses.includes(state.pc), }); - console.log("Response from worker:", { + logger.info("Response from worker:", { isFinished, state, isRunMode, @@ -492,7 +520,7 @@ const workers = createSlice({ }, extraReducers: (builder) => { builder.addCase(createWorker.fulfilled, (state, action) => { - console.log("Worker created", action.payload); + logger.info("Worker created", action.payload); state.push({ worker: action.payload.worker, id: action.payload.id, diff --git a/src/utils/loggerService.tsx b/src/utils/loggerService.tsx new file mode 100644 index 0000000..1c3706c --- /dev/null +++ b/src/utils/loggerService.tsx @@ -0,0 +1,40 @@ +import { throttle } from "lodash"; +import { toast } from "react-toastify"; + +const errorToast = throttle((msg: string) => { + toast.error( +
+ {msg} +
+ Check console for more information +
, + { autoClose: 3000 }, + ); +}, 6000); +class Logger { + error(msg: string, { error, hideToast }: { error: unknown; hideToast?: boolean }) { + if (!hideToast) { + errorToast(msg); + } + + console.error("‼️‼️‼️‼️Catched error:", error); + } + + warn(...msg: unknown[]) { + console.warn("⚠️⚠️⚠️⚠️⚠️⚠️", ...msg); + } + + info(...msg: unknown[]) { + // eslint-disable-next-line no-console + console.info("🪵🪵🪵🪵🪵", ...msg); + } + + debug(...msg: unknown[]) { + if (process.env.NODE_ENV === "development") { + // eslint-disable-next-line no-console + console.debug("💻💻💻💻💻 DEV LOG: \n", ...msg); + } + } +} + +export const logger = new Logger();