Skip to content

Commit 605f0dd

Browse files
authored
Support providing args right from the upload screen. (#383)
1 parent 3bdd92d commit 605f0dd

File tree

9 files changed

+98
-37
lines changed

9 files changed

+98
-37
lines changed

src/components/DebuggerSettings/Content.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ export const DebuggerSettingsContent = () => {
114114
</span>
115115
<Input
116116
className={commonClass}
117+
placeholder="0x-prefixed, encoded operands"
117118
onChange={(e) => {
118119
const value = e.target?.value;
119120
dispatch(setSpiArgs(value));

src/components/ProgramLoader/Assembly.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ export const Assembly = ({
118118
try {
119119
const programJson = compile_assembly(input);
120120
const newProgram = JSON.parse(programJson);
121-
const output = mapUploadFileInputToOutput(newProgram);
121+
const output = mapUploadFileInputToOutput(newProgram, "assembly");
122122
output.name = programName;
123123
// avoid re-rendering when the code & state is the same.
124124
if (isArrayEqual(program, output.program)) {

src/components/ProgramLoader/Examples.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ export const Examples = ({ onProgramLoad }: { onProgramLoad: (val: ProgramUpload
2323
memory: program.memory,
2424
gas: program.gas,
2525
},
26+
isSpi: false,
27+
kind: "Example",
2628
program: programs[key].program,
2729
name: program.name,
2830
exampleName: key,

src/components/ProgramLoader/Loader.tsx

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,24 @@ import { useState, useCallback, useEffect } from "react";
44
import { ProgramUploadFileOutput } from "./types";
55
import { useDebuggerActions } from "@/hooks/useDebuggerActions";
66
import { useAppDispatch, useAppSelector } from "@/store/hooks.ts";
7-
import { setIsProgramEditMode } from "@/store/debugger/debuggerSlice.ts";
7+
import { setIsProgramEditMode, setSpiArgs } from "@/store/debugger/debuggerSlice.ts";
88
import { selectIsAnyWorkerLoading } from "@/store/workers/workersSlice";
99
import { isSerializedError } from "@/store/utils";
1010
import { ProgramFileUpload } from "@/components/ProgramLoader/ProgramFileUpload.tsx";
1111
import { useNavigate } from "react-router";
1212
import { Links } from "./Links";
1313
import { Separator } from "../ui/separator";
1414
import { TriangleAlert } from "lucide-react";
15+
import { WithHelp } from "../WithHelp/WithHelp";
16+
import { Input } from "../ui/input";
1517

1618
export const Loader = ({ setIsDialogOpen }: { setIsDialogOpen?: (val: boolean) => void }) => {
1719
const dispatch = useAppDispatch();
1820
const [programLoad, setProgramLoad] = useState<ProgramUploadFileOutput>();
1921
const [error, setError] = useState<string>();
20-
const [isSubmitted, setIsSubmitted] = useState(false);
2122
const debuggerActions = useDebuggerActions();
2223
const isLoading = useAppSelector(selectIsAnyWorkerLoading);
24+
const debuggerState = useAppSelector((state) => state.debugger);
2325
const navigate = useNavigate();
2426

2527
useEffect(() => {
@@ -28,8 +30,6 @@ export const Loader = ({ setIsDialogOpen }: { setIsDialogOpen?: (val: boolean) =
2830

2931
const handleLoad = useCallback(
3032
async (program?: ProgramUploadFileOutput) => {
31-
setIsSubmitted(true);
32-
3333
if (!programLoad && !program) return;
3434

3535
dispatch(setIsProgramEditMode(false));
@@ -60,26 +60,63 @@ export const Loader = ({ setIsDialogOpen }: { setIsDialogOpen?: (val: boolean) =
6060
<Examples
6161
onProgramLoad={(val) => {
6262
setProgramLoad(val);
63-
setIsSubmitted(false);
6463
handleLoad(val);
6564
}}
6665
/>
6766

6867
<div className="my-10">
69-
<ProgramFileUpload
70-
onFileUpload={(val) => {
71-
setProgramLoad(val);
72-
setIsSubmitted(false);
73-
// handleLoad(val);
74-
}}
75-
/>
68+
<ProgramFileUpload onFileUpload={setProgramLoad} isError={error !== undefined} setError={setError} />
7669
</div>
77-
<Links />
78-
{error && isSubmitted && (
79-
<p className="flex items-center text-destructive-foreground mt-10 text-[11px] whitespace-pre-line">
70+
{error && (
71+
<p className="flex items-top text-destructive-foreground h-[145px] overflow-auto text-[11px] whitespace-pre-line">
8072
<TriangleAlert className="mr-2" height="18px" /> {error}
8173
</p>
8274
)}
75+
{!error && programLoad && (
76+
<div className="h-[145px] overflow-auto text-xs">
77+
<div className="mt-2 flex justify-between items-center">
78+
<span className="block text-xs font-bold min-w-[150px]">Detected:</span>
79+
<code className="flex-1 ml-2"> {programLoad.kind}</code>
80+
</div>
81+
<div className="mt-2 flex justify-between items-center">
82+
<span className="block text-xs font-bold min-w-[150px]">Name:</span>
83+
<code className="flex-1 ml-2">{programLoad.name}</code>
84+
</div>
85+
<div className="mt-2 flex items-center">
86+
<span className="block text-xs font-bold min-w-[150px]">Initial state:</span>
87+
<details open={false} className="flex-1 ml-2">
88+
<summary>view</summary>
89+
<pre>{JSON.stringify(programLoad.initial, null, 2)}</pre>
90+
</details>
91+
</div>
92+
{programLoad.isSpi && (
93+
<>
94+
<div className="mt-2 flex justify-between items-center">
95+
<span className="block text-xs font-bold min-w-[150px]">
96+
<WithHelp help="Hex-encoded JAM SPI arguments written to the heap">Arguments</WithHelp>
97+
</span>
98+
<Input
99+
size={2}
100+
className="text-xs m-2"
101+
placeholder="0x-prefixed, encoded operands"
102+
onChange={(e) => {
103+
const value = e.target?.value;
104+
dispatch(setSpiArgs(value));
105+
}}
106+
value={debuggerState.spiArgs ?? ""}
107+
/>
108+
</div>
109+
<div className="mt-2 flex justify-between items-center">
110+
<span className="block text-xs font-bold min-w-[150px]">
111+
<WithHelp help="JSON containing instructions how to handle host calls">Host Calls Trace</WithHelp>
112+
</span>
113+
<p className="flex-1 ml-2">(coming soon)</p>
114+
</div>
115+
</>
116+
)}
117+
</div>
118+
)}
119+
{!error && !programLoad && <Links />}
83120
</div>
84121
<div className="px-5 mt-[30px]">
85122
<Separator />

src/components/ProgramLoader/ProgramFileUpload.tsx

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@ import { SafeParseReturnType, z } from "zod";
77
import { useAppSelector } from "@/store/hooks";
88
import { selectInitialState } from "@/store/debugger/debuggerSlice";
99
import { cn, getAsChunks, getAsPageMap } from "@/lib/utils.ts";
10-
import { TriangleAlert, UploadCloud } from "lucide-react";
10+
import { UploadCloud } from "lucide-react";
1111
import { Button } from "../ui/button";
1212
import { useCallback, useEffect, useMemo, useState } from "react";
1313
import { Input } from "../ui/input";
1414
import { decodeSpiWithMetadata } from "@/utils/spi";
1515

1616
type ProgramFileUploadProps = {
1717
onFileUpload: (val: ProgramUploadFileOutput) => void;
18+
setError: (e?: string) => void;
19+
isError: boolean;
1820
close?: () => void;
1921
};
2022

@@ -70,6 +72,9 @@ function loadFileFromUint8Array(
7072
onFileUpload: (d: ProgramUploadFileOutput) => void,
7173
initialState: ExpectedState,
7274
) {
75+
// reset error state
76+
setError(undefined);
77+
7378
// Try to parse file as a JSON first
7479
let jsonFile = null;
7580
let stringContent = "";
@@ -104,7 +109,7 @@ function loadFileFromUint8Array(
104109
return;
105110
}
106111

107-
onFileUpload(mapUploadFileInputToOutput(jsonFile));
112+
onFileUpload(mapUploadFileInputToOutput(jsonFile, "JSON test"));
108113
return;
109114
}
110115

@@ -123,6 +128,8 @@ function loadFileFromUint8Array(
123128
onFileUpload({
124129
program: Array.from(code),
125130
name: `${loadedFileName} [spi]`,
131+
isSpi: true,
132+
kind: "JAM SPI",
126133
initial: {
127134
regs: Array.from(registers).map((x) => BigInt(x as number | bigint)) as RegistersArray,
128135
pc: 0,
@@ -146,20 +153,21 @@ function loadFileFromUint8Array(
146153
onFileUpload({
147154
program: Array.from(uint8Array),
148155
name: `${loadedFileName} [generic]`,
156+
isSpi: false,
157+
kind: "Generic PVM",
149158
initial: initialState,
150159
});
151160
} else {
152161
setError("Unrecognized program format (see console).");
153162
}
154163
}
155164

156-
export const ProgramFileUpload: React.FC<ProgramFileUploadProps> = ({ onFileUpload, close }) => {
165+
export const ProgramFileUpload: React.FC<ProgramFileUploadProps> = ({ isError, onFileUpload, close, setError }) => {
157166
const initialState = useAppSelector(selectInitialState);
158167
const spiArgs = useAppSelector((state) => state.debugger.spiArgs);
159168

160169
const [isUpload, setIsUpload] = useState(true);
161170
const [manualInput, setManualInput] = useState("");
162-
const [error, setError] = useState<string>();
163171
const [loadedFileName, setLoadedFileName] = useState<string | undefined>(undefined);
164172

165173
const spiArgsAsBytes = useMemo(() => {
@@ -173,18 +181,17 @@ export const ProgramFileUpload: React.FC<ProgramFileUploadProps> = ({ onFileUplo
173181
useEffect(() => {
174182
// reset the state of upload
175183
if (isUpload) {
176-
setLoadedFileName(undefined);
177-
setError(undefined);
178184
return;
179185
}
186+
180187
if (manualInput === "") {
181188
setError(undefined);
182189
return;
183190
}
184191

185192
const buffer = new TextEncoder().encode(manualInput);
186193
loadFileFromUint8Array("pasted", buffer, spiArgsAsBytes, setError, onFileUpload, initialState);
187-
}, [manualInput, isUpload, initialState, onFileUpload, spiArgsAsBytes]);
194+
}, [manualInput, isUpload, initialState, onFileUpload, spiArgsAsBytes, setError]);
188195

189196
const handleFileRead = (e: ProgressEvent<FileReader>) => {
190197
setError(undefined);
@@ -227,9 +234,11 @@ export const ProgramFileUpload: React.FC<ProgramFileUploadProps> = ({ onFileUplo
227234
open();
228235
}, [open]);
229236

230-
const toggleUpload = useCallback(() => {
231-
setIsUpload((upload) => !upload);
232-
}, []);
237+
const setNoUpload = useCallback(() => {
238+
setIsUpload(false);
239+
setLoadedFileName(undefined);
240+
setError(undefined);
241+
}, [setError]);
233242

234243
const isLoaded = loadedFileName !== undefined;
235244

@@ -253,14 +262,14 @@ export const ProgramFileUpload: React.FC<ProgramFileUploadProps> = ({ onFileUplo
253262
value={manualInput}
254263
onChange={(e) => setManualInput(e.target.value)}
255264
className={cn("flex-auto text-xs", {
256-
"focus-visible:ring-red-500 ring-red-500 ring-2": error,
265+
"focus-visible:ring-red-500 ring-red-500 ring-2": isError,
257266
})}
258267
/>
259268
)}
260269
</div>
261270
<div className="flex space-x-2">
262271
{isUpload && (
263-
<Button className="text-[10px] py-1 h-9" variant="ghost" onClick={toggleUpload}>
272+
<Button className="text-[10px] py-1 h-9" variant="ghost" onClick={setNoUpload}>
264273
Paste manually
265274
</Button>
266275
)}
@@ -270,12 +279,6 @@ export const ProgramFileUpload: React.FC<ProgramFileUploadProps> = ({ onFileUplo
270279
</div>
271280
<input {...getInputProps()} className="hidden" />
272281
</div>
273-
274-
{error && (
275-
<p className="flex items-center text-destructive-foreground mt-3 text-[11px] whitespace-pre-line">
276-
<TriangleAlert className="mr-2" height="18px" /> {error}
277-
</p>
278-
)}
279282
</div>
280283
);
281284
};

src/components/ProgramLoader/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ export type ProgramUploadFileOutput = {
55
initial: InitialState;
66
program: number[];
77
exampleName?: string;
8+
kind: string;
9+
isSpi: boolean;
810
};
911

1012
export type ProgramUploadFileInput = {

src/components/ProgramLoader/utils.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { mapKeys, camelCase, pickBy } from "lodash";
22
import { ProgramUploadFileInput, ProgramUploadFileOutput } from "./types";
33
import { RegistersArray } from "@/types/pvm";
44

5-
export function mapUploadFileInputToOutput(data: ProgramUploadFileInput): ProgramUploadFileOutput {
5+
export function mapUploadFileInputToOutput(data: ProgramUploadFileInput, kind: string): ProgramUploadFileOutput {
66
const camelCasedData = mapKeys(data, (_value: unknown, key: string) => camelCase(key));
77

88
const initial = pickBy(camelCasedData, (_value: unknown, key) => key.startsWith("initial"));
@@ -14,9 +14,15 @@ export function mapUploadFileInputToOutput(data: ProgramUploadFileInput): Progra
1414
...(mapKeys(initial, (_value: unknown, key) =>
1515
camelCase(key.replace("initial", "")),
1616
) as ProgramUploadFileOutput["initial"]),
17-
// TODO is-writable has wrong case
18-
// pageMap: data["initial-page-map"].map((val) => ({ address: val.address, length: val.length, isWritable: val["is-writable"] })),
17+
// TODO [ToDr] is this okay?
18+
pageMap: data["initial-page-map"].map((val) => ({
19+
address: val.address,
20+
length: val.length,
21+
["is-writable"]: val["is-writable"],
22+
})),
1923
},
24+
kind,
25+
isSpi: false,
2026
program: data.program,
2127
// expected: mapKeys(expected, (_value: unknown, key) => camelCase(key.replace("expected", ""))) as unknown as ProgramUploadFileOutput["expected"],
2228
};

src/pages/DebuggerContent.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,13 +167,17 @@ const DebuggerContent = () => {
167167
program: Array.from(code) || [],
168168
initial: { ...initialState, regs, pageMap, memory },
169169
name: `${programNameWithoutSuffix} (SPI)`,
170+
isSpi: true,
171+
kind: "JAM SPI",
170172
};
171173
debuggerActions.handleProgramLoad(program);
172174
} else {
173175
debuggerActions.handleProgramLoad({
174176
initial: initialState,
175177
program: program || [],
176178
name: `${programName} (generic)`,
179+
isSpi: false,
180+
kind: "Generic PVM",
177181
});
178182
}
179183
}

src/pages/ProgramLoader.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ const ProgramLoader = () => {
4343
await debuggerActions.handleProgramLoad({
4444
program: program.program,
4545
name: program.name,
46+
isSpi: false,
47+
kind: "Example",
4648
initial: {
4749
regs: program.regs.map((x) => BigInt(x)) as RegistersArray,
4850
pc: program.pc,
@@ -75,6 +77,8 @@ const ProgramLoader = () => {
7577
await debuggerActions.handleProgramLoad({
7678
program: Array.from(code),
7779
name: "loaded-from-url [SPI]",
80+
isSpi: true,
81+
kind: "JAM SPI",
7882
initial: {
7983
regs: Array.from(registers).map((x) => BigInt(x as number | bigint)) as RegistersArray,
8084
pc: 0,
@@ -93,6 +97,8 @@ const ProgramLoader = () => {
9397
program: parsedBlobArray,
9498
name: "loaded-from-url [generic]",
9599
initial: initialState,
100+
isSpi: false,
101+
kind: "Generic PVM",
96102
});
97103

98104
navigate("/", { replace: true });

0 commit comments

Comments
 (0)