diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 86da80dd..ff723c69 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -12,6 +12,10 @@ option(FLATBUFFERS_BUILD_TESTS "" OFF) option(FLATBUFFERS_INSTALL "" OFF) add_subdirectory(flatbuffers) +if(MSVC) +target_compile_options(flatc PRIVATE /MD) +endif() + ########################################## add_library(reflect INTERFACE) diff --git a/page/.prettierignore b/page/.prettierignore index e17b023c..f16c6412 100644 --- a/page/.prettierignore +++ b/page/.prettierignore @@ -1,2 +1,3 @@ # Ignore artifacts: dist +src/fb/ \ No newline at end of file diff --git a/page/package-lock.json b/page/package-lock.json index 5510ce20..845dbe87 100644 --- a/page/package-lock.json +++ b/page/package-lock.json @@ -22,6 +22,7 @@ "@types/react-window": "^1.8.8", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "flatbuffers": "^25.2.10", "jszip": "^3.10.1", "lucide-react": "^0.503.0", "react": "^19.0.0", @@ -3595,6 +3596,12 @@ "node": ">=16" } }, + "node_modules/flatbuffers": { + "version": "25.2.10", + "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-25.2.10.tgz", + "integrity": "sha512-7JlN9ZvLDG1McO3kbX0k4v+SUAg48L1rIwEvN6ZQl/eCtgJz9UylTMzE9wrmYrcorgxm3CX/3T/w5VAub99UUw==", + "license": "Apache-2.0" + }, "node_modules/flatted": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", diff --git a/page/package.json b/page/package.json index 25100305..13ae4aca 100644 --- a/page/package.json +++ b/page/package.json @@ -24,6 +24,7 @@ "@types/react-window": "^1.8.8", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "flatbuffers": "^25.2.10", "jszip": "^3.10.1", "lucide-react": "^0.503.0", "react": "^19.0.0", diff --git a/page/src/emulator.ts b/page/src/emulator.ts index ce38da34..0ea8fc5b 100644 --- a/page/src/emulator.ts +++ b/page/src/emulator.ts @@ -1,6 +1,10 @@ import { createDefaultSettings, Settings, translateSettings } from "./settings"; import { FileEntry } from "./zip-file"; +import * as flatbuffers from "flatbuffers"; +import * as fbDebugger from "@/fb/debugger"; +import * as fbDebuggerEvent from "@/fb/debugger/event"; + type LogHandler = (lines: string[]) => void; export interface UserFile { @@ -8,6 +12,35 @@ export interface UserFile { data: ArrayBuffer; } +function base64Encode(uint8Array: Uint8Array): string { + let binaryString = ""; + for (let i = 0; i < uint8Array.byteLength; i++) { + binaryString += String.fromCharCode(uint8Array[i]); + } + + return btoa(binaryString); +} + +function base64Decode(data: string) { + const binaryString = atob(data); + + const len = binaryString.length; + const bytes = new Uint8Array(len); + + for (let i = 0; i < len; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + + return bytes; +} + +function decodeEvent(data: string) { + const array = base64Decode(data); + const buffer = new flatbuffers.ByteBuffer(array); + const event = fbDebugger.DebugEvent.getRootAsDebugEvent(buffer); + return event.unpack(); +} + export class Emulator { filesystem: FileEntry[]; logHandler: LogHandler; @@ -30,13 +63,7 @@ export class Emulator { /*new URL('./emulator-worker.js', import.meta.url)*/ "./emulator-worker.js", ); - this.worker.onmessage = (event: MessageEvent) => { - if (event.data.message == "log") { - this.logHandler(event.data.data); - } else if (event.data.message == "end") { - this.terminateResolve(0); - } - }; + this.worker.onmessage = this._onMessage.bind(this); } start( @@ -72,4 +99,30 @@ export class Emulator { onTerminate() { return this.terminatePromise; } + + sendEvent(event: fbDebugger.DebugEventT) { + const builder = new flatbuffers.Builder(1024); + fbDebugger.DebugEvent.finishDebugEventBuffer(builder, event.pack(builder)); + + const message = base64Encode(builder.asUint8Array()); + + this.worker.postMessage({ + message: "event", + data: message, + }); + } + + _onMessage(event: MessageEvent) { + if (event.data.message == "log") { + this.logHandler(event.data.data); + } else if (event.data.message == "event") { + this._onEvent(decodeEvent(event.data.data)); + } else if (event.data.message == "end") { + this.terminateResolve(0); + } + } + + _onEvent(event: fbDebugger.DebugEventT) { + console.log(event); + } } diff --git a/page/src/fb/debugger.ts b/page/src/fb/debugger.ts new file mode 100644 index 00000000..347b04f6 --- /dev/null +++ b/page/src/fb/debugger.ts @@ -0,0 +1,8 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ + +export { DebugEvent, DebugEventT } from './debugger/debug-event.js'; +export { Event } from './debugger/event.js'; +export { PauseEvent, PauseEventT } from './debugger/pause-event.js'; +export { RunEvent, RunEventT } from './debugger/run-event.js'; diff --git a/page/src/fb/debugger/debug-event.ts b/page/src/fb/debugger/debug-event.ts new file mode 100644 index 00000000..a34e10e9 --- /dev/null +++ b/page/src/fb/debugger/debug-event.ts @@ -0,0 +1,109 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ + +import * as flatbuffers from 'flatbuffers'; + +import { Event, unionToEvent, unionListToEvent } from '../debugger/event.js'; +import { PauseEvent, PauseEventT } from '../debugger/pause-event.js'; +import { RunEvent, RunEventT } from '../debugger/run-event.js'; + + +export class DebugEvent implements flatbuffers.IUnpackableObject { + bb: flatbuffers.ByteBuffer|null = null; + bb_pos = 0; + __init(i:number, bb:flatbuffers.ByteBuffer):DebugEvent { + this.bb_pos = i; + this.bb = bb; + return this; +} + +static getRootAsDebugEvent(bb:flatbuffers.ByteBuffer, obj?:DebugEvent):DebugEvent { + return (obj || new DebugEvent()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +static getSizePrefixedRootAsDebugEvent(bb:flatbuffers.ByteBuffer, obj?:DebugEvent):DebugEvent { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new DebugEvent()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +eventType():Event { + const offset = this.bb!.__offset(this.bb_pos, 4); + return offset ? this.bb!.readUint8(this.bb_pos + offset) : Event.NONE; +} + +event(obj:any):any|null { + const offset = this.bb!.__offset(this.bb_pos, 6); + return offset ? this.bb!.__union(obj, this.bb_pos + offset) : null; +} + +static startDebugEvent(builder:flatbuffers.Builder) { + builder.startObject(2); +} + +static addEventType(builder:flatbuffers.Builder, eventType:Event) { + builder.addFieldInt8(0, eventType, Event.NONE); +} + +static addEvent(builder:flatbuffers.Builder, eventOffset:flatbuffers.Offset) { + builder.addFieldOffset(1, eventOffset, 0); +} + +static endDebugEvent(builder:flatbuffers.Builder):flatbuffers.Offset { + const offset = builder.endObject(); + return offset; +} + +static finishDebugEventBuffer(builder:flatbuffers.Builder, offset:flatbuffers.Offset) { + builder.finish(offset); +} + +static finishSizePrefixedDebugEventBuffer(builder:flatbuffers.Builder, offset:flatbuffers.Offset) { + builder.finish(offset, undefined, true); +} + +static createDebugEvent(builder:flatbuffers.Builder, eventType:Event, eventOffset:flatbuffers.Offset):flatbuffers.Offset { + DebugEvent.startDebugEvent(builder); + DebugEvent.addEventType(builder, eventType); + DebugEvent.addEvent(builder, eventOffset); + return DebugEvent.endDebugEvent(builder); +} + +unpack(): DebugEventT { + return new DebugEventT( + this.eventType(), + (() => { + const temp = unionToEvent(this.eventType(), this.event.bind(this)); + if(temp === null) { return null; } + return temp.unpack() + })() + ); +} + + +unpackTo(_o: DebugEventT): void { + _o.eventType = this.eventType(); + _o.event = (() => { + const temp = unionToEvent(this.eventType(), this.event.bind(this)); + if(temp === null) { return null; } + return temp.unpack() + })(); +} +} + +export class DebugEventT implements flatbuffers.IGeneratedObject { +constructor( + public eventType: Event = Event.NONE, + public event: PauseEventT|RunEventT|null = null +){} + + +pack(builder:flatbuffers.Builder): flatbuffers.Offset { + const event = builder.createObjectOffset(this.event); + + return DebugEvent.createDebugEvent(builder, + this.eventType, + event + ); +} +} diff --git a/page/src/fb/debugger/event.ts b/page/src/fb/debugger/event.ts new file mode 100644 index 00000000..1949c08e --- /dev/null +++ b/page/src/fb/debugger/event.ts @@ -0,0 +1,38 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ + +import { PauseEvent, PauseEventT } from '../debugger/pause-event.js'; +import { RunEvent, RunEventT } from '../debugger/run-event.js'; + + +export enum Event { + NONE = 0, + PauseEvent = 1, + RunEvent = 2 +} + +export function unionToEvent( + type: Event, + accessor: (obj:PauseEvent|RunEvent) => PauseEvent|RunEvent|null +): PauseEvent|RunEvent|null { + switch(Event[type]) { + case 'NONE': return null; + case 'PauseEvent': return accessor(new PauseEvent())! as PauseEvent; + case 'RunEvent': return accessor(new RunEvent())! as RunEvent; + default: return null; + } +} + +export function unionListToEvent( + type: Event, + accessor: (index: number, obj:PauseEvent|RunEvent) => PauseEvent|RunEvent|null, + index: number +): PauseEvent|RunEvent|null { + switch(Event[type]) { + case 'NONE': return null; + case 'PauseEvent': return accessor(index, new PauseEvent())! as PauseEvent; + case 'RunEvent': return accessor(index, new RunEvent())! as RunEvent; + default: return null; + } +} diff --git a/page/src/fb/debugger/pause-event.ts b/page/src/fb/debugger/pause-event.ts new file mode 100644 index 00000000..ea56222b --- /dev/null +++ b/page/src/fb/debugger/pause-event.ts @@ -0,0 +1,56 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ + +import * as flatbuffers from 'flatbuffers'; + + + +export class PauseEvent implements flatbuffers.IUnpackableObject { + bb: flatbuffers.ByteBuffer|null = null; + bb_pos = 0; + __init(i:number, bb:flatbuffers.ByteBuffer):PauseEvent { + this.bb_pos = i; + this.bb = bb; + return this; +} + +static getRootAsPauseEvent(bb:flatbuffers.ByteBuffer, obj?:PauseEvent):PauseEvent { + return (obj || new PauseEvent()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +static getSizePrefixedRootAsPauseEvent(bb:flatbuffers.ByteBuffer, obj?:PauseEvent):PauseEvent { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new PauseEvent()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +static startPauseEvent(builder:flatbuffers.Builder) { + builder.startObject(0); +} + +static endPauseEvent(builder:flatbuffers.Builder):flatbuffers.Offset { + const offset = builder.endObject(); + return offset; +} + +static createPauseEvent(builder:flatbuffers.Builder):flatbuffers.Offset { + PauseEvent.startPauseEvent(builder); + return PauseEvent.endPauseEvent(builder); +} + +unpack(): PauseEventT { + return new PauseEventT(); +} + + +unpackTo(_o: PauseEventT): void {} +} + +export class PauseEventT implements flatbuffers.IGeneratedObject { +constructor(){} + + +pack(builder:flatbuffers.Builder): flatbuffers.Offset { + return PauseEvent.createPauseEvent(builder); +} +} diff --git a/page/src/fb/debugger/run-event.ts b/page/src/fb/debugger/run-event.ts new file mode 100644 index 00000000..9502e4d5 --- /dev/null +++ b/page/src/fb/debugger/run-event.ts @@ -0,0 +1,85 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ + +import * as flatbuffers from 'flatbuffers'; + + + +export class RunEvent implements flatbuffers.IUnpackableObject { + bb: flatbuffers.ByteBuffer|null = null; + bb_pos = 0; + __init(i:number, bb:flatbuffers.ByteBuffer):RunEvent { + this.bb_pos = i; + this.bb = bb; + return this; +} + +static getRootAsRunEvent(bb:flatbuffers.ByteBuffer, obj?:RunEvent):RunEvent { + return (obj || new RunEvent()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +static getSizePrefixedRootAsRunEvent(bb:flatbuffers.ByteBuffer, obj?:RunEvent):RunEvent { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new RunEvent()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +singleStep():boolean { + const offset = this.bb!.__offset(this.bb_pos, 4); + return offset ? !!this.bb!.readInt8(this.bb_pos + offset) : false; +} + +mutate_single_step(value:boolean):boolean { + const offset = this.bb!.__offset(this.bb_pos, 4); + + if (offset === 0) { + return false; + } + + this.bb!.writeInt8(this.bb_pos + offset, +value); + return true; +} + +static startRunEvent(builder:flatbuffers.Builder) { + builder.startObject(1); +} + +static addSingleStep(builder:flatbuffers.Builder, singleStep:boolean) { + builder.addFieldInt8(0, +singleStep, +false); +} + +static endRunEvent(builder:flatbuffers.Builder):flatbuffers.Offset { + const offset = builder.endObject(); + return offset; +} + +static createRunEvent(builder:flatbuffers.Builder, singleStep:boolean):flatbuffers.Offset { + RunEvent.startRunEvent(builder); + RunEvent.addSingleStep(builder, singleStep); + return RunEvent.endRunEvent(builder); +} + +unpack(): RunEventT { + return new RunEventT( + this.singleStep() + ); +} + + +unpackTo(_o: RunEventT): void { + _o.singleStep = this.singleStep(); +} +} + +export class RunEventT implements flatbuffers.IGeneratedObject { +constructor( + public singleStep: boolean = false +){} + + +pack(builder:flatbuffers.Builder): flatbuffers.Offset { + return RunEvent.createRunEvent(builder, + this.singleStep + ); +} +} diff --git a/page/src/fb/events.ts b/page/src/fb/events.ts new file mode 100644 index 00000000..f21dacb2 --- /dev/null +++ b/page/src/fb/events.ts @@ -0,0 +1,5 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ + +export * as Debugger from './debugger.js'; diff --git a/page/tsconfig.app.json b/page/tsconfig.app.json index 9f0a5124..0f468d74 100644 --- a/page/tsconfig.app.json +++ b/page/tsconfig.app.json @@ -17,8 +17,8 @@ /* Linting */ "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, + "noUnusedLocals": false, + "noUnusedParameters": false, "noFallthroughCasesInSwitch": true, "noUncheckedSideEffectImports": true, "baseUrl": ".", diff --git a/src/debugger/CMakeLists.txt b/src/debugger/CMakeLists.txt index 12711131..a0cf3db5 100644 --- a/src/debugger/CMakeLists.txt +++ b/src/debugger/CMakeLists.txt @@ -25,7 +25,7 @@ set_property(GLOBAL PROPERTY VS_STARTUP_PROJECT debugger) momo_strip_target(debugger) add_custom_target(gen_fbs + DEPENDS flatc COMMAND "$" --gen-mutable --gen-object-api --filename-ext hxx --cpp -o "${CMAKE_CURRENT_LIST_DIR}" "${CMAKE_CURRENT_LIST_DIR}/events.fbs" + COMMAND "$" --gen-mutable --gen-object-api --ts -o "${PROJECT_SOURCE_DIR}/page/src/fb" "${CMAKE_CURRENT_LIST_DIR}/events.fbs" ) - -add_dependencies(gen_fbs flatc) \ No newline at end of file diff --git a/src/debugger/events.fbs b/src/debugger/events.fbs index a0456ceb..15095c10 100644 --- a/src/debugger/events.fbs +++ b/src/debugger/events.fbs @@ -3,7 +3,7 @@ namespace Debugger; table PauseEvent {} table RunEvent { - singleStep: bool; + single_step: bool; } union Event { diff --git a/src/debugger/events_generated.hxx b/src/debugger/events_generated.hxx index 337dff47..3e9be171 100644 --- a/src/debugger/events_generated.hxx +++ b/src/debugger/events_generated.hxx @@ -176,24 +176,24 @@ inline ::flatbuffers::Offset CreatePauseEvent( struct RunEventT : public ::flatbuffers::NativeTable { typedef RunEvent TableType; - bool singleStep = false; + bool single_step = false; }; struct RunEvent FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef RunEventT NativeTableType; typedef RunEventBuilder Builder; enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_SINGLESTEP = 4 + VT_SINGLE_STEP = 4 }; - bool singleStep() const { - return GetField(VT_SINGLESTEP, 0) != 0; + bool single_step() const { + return GetField(VT_SINGLE_STEP, 0) != 0; } - bool mutate_singleStep(bool _singleStep = 0) { - return SetField(VT_SINGLESTEP, static_cast(_singleStep), 0); + bool mutate_single_step(bool _single_step = 0) { + return SetField(VT_SINGLE_STEP, static_cast(_single_step), 0); } bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && - VerifyField(verifier, VT_SINGLESTEP, 1) && + VerifyField(verifier, VT_SINGLE_STEP, 1) && verifier.EndTable(); } RunEventT *UnPack(const ::flatbuffers::resolver_function_t *_resolver = nullptr) const; @@ -205,8 +205,8 @@ struct RunEventBuilder { typedef RunEvent Table; ::flatbuffers::FlatBufferBuilder &fbb_; ::flatbuffers::uoffset_t start_; - void add_singleStep(bool singleStep) { - fbb_.AddElement(RunEvent::VT_SINGLESTEP, static_cast(singleStep), 0); + void add_single_step(bool single_step) { + fbb_.AddElement(RunEvent::VT_SINGLE_STEP, static_cast(single_step), 0); } explicit RunEventBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { @@ -221,9 +221,9 @@ struct RunEventBuilder { inline ::flatbuffers::Offset CreateRunEvent( ::flatbuffers::FlatBufferBuilder &_fbb, - bool singleStep = false) { + bool single_step = false) { RunEventBuilder builder_(_fbb); - builder_.add_singleStep(singleStep); + builder_.add_single_step(single_step); return builder_.Finish(); } @@ -342,7 +342,7 @@ inline RunEventT *RunEvent::UnPack(const ::flatbuffers::resolver_function_t *_re inline void RunEvent::UnPackTo(RunEventT *_o, const ::flatbuffers::resolver_function_t *_resolver) const { (void)_o; (void)_resolver; - { auto _e = singleStep(); _o->singleStep = _e; } + { auto _e = single_step(); _o->single_step = _e; } } inline ::flatbuffers::Offset RunEvent::Pack(::flatbuffers::FlatBufferBuilder &_fbb, const RunEventT* _o, const ::flatbuffers::rehasher_function_t *_rehasher) { @@ -353,10 +353,10 @@ inline ::flatbuffers::Offset CreateRunEvent(::flatbuffers::FlatBufferB (void)_rehasher; (void)_o; struct _VectorArgs { ::flatbuffers::FlatBufferBuilder *__fbb; const RunEventT* __o; const ::flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; - auto _singleStep = _o->singleStep; + auto _single_step = _o->single_step; return Debugger::CreateRunEvent( _fbb, - _singleStep); + _single_step); } inline DebugEventT *DebugEvent::UnPack(const ::flatbuffers::resolver_function_t *_resolver) const {