Prepare TS flatbuffer support

This commit is contained in:
momo5502
2025-04-28 11:11:00 +02:00
parent bc26ebf1ba
commit 9ab282c2cf
15 changed files with 393 additions and 26 deletions

4
deps/CMakeLists.txt vendored
View File

@@ -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)

View File

@@ -1,2 +1,3 @@
# Ignore artifacts:
dist
src/fb/

View File

@@ -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",

View File

@@ -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",

View File

@@ -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);
}
}

8
page/src/fb/debugger.ts Normal file
View File

@@ -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';

View File

@@ -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<DebugEventT> {
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<T extends flatbuffers.Table>(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
);
}
}

View File

@@ -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;
}
}

View File

@@ -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<PauseEventT> {
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);
}
}

View File

@@ -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<RunEventT> {
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
);
}
}

5
page/src/fb/events.ts Normal file
View File

@@ -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';

View File

@@ -17,8 +17,8 @@
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
"baseUrl": ".",

View File

@@ -25,7 +25,7 @@ set_property(GLOBAL PROPERTY VS_STARTUP_PROJECT debugger)
momo_strip_target(debugger)
add_custom_target(gen_fbs
DEPENDS flatc
COMMAND "$<TARGET_FILE:flatc>" --gen-mutable --gen-object-api --filename-ext hxx --cpp -o "${CMAKE_CURRENT_LIST_DIR}" "${CMAKE_CURRENT_LIST_DIR}/events.fbs"
COMMAND "$<TARGET_FILE:flatc>" --gen-mutable --gen-object-api --ts -o "${PROJECT_SOURCE_DIR}/page/src/fb" "${CMAKE_CURRENT_LIST_DIR}/events.fbs"
)
add_dependencies(gen_fbs flatc)

View File

@@ -3,7 +3,7 @@ namespace Debugger;
table PauseEvent {}
table RunEvent {
singleStep: bool;
single_step: bool;
}
union Event {

View File

@@ -176,24 +176,24 @@ inline ::flatbuffers::Offset<PauseEvent> 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<uint8_t>(VT_SINGLESTEP, 0) != 0;
bool single_step() const {
return GetField<uint8_t>(VT_SINGLE_STEP, 0) != 0;
}
bool mutate_singleStep(bool _singleStep = 0) {
return SetField<uint8_t>(VT_SINGLESTEP, static_cast<uint8_t>(_singleStep), 0);
bool mutate_single_step(bool _single_step = 0) {
return SetField<uint8_t>(VT_SINGLE_STEP, static_cast<uint8_t>(_single_step), 0);
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<uint8_t>(verifier, VT_SINGLESTEP, 1) &&
VerifyField<uint8_t>(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<uint8_t>(RunEvent::VT_SINGLESTEP, static_cast<uint8_t>(singleStep), 0);
void add_single_step(bool single_step) {
fbb_.AddElement<uint8_t>(RunEvent::VT_SINGLE_STEP, static_cast<uint8_t>(single_step), 0);
}
explicit RunEventBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
@@ -221,9 +221,9 @@ struct RunEventBuilder {
inline ::flatbuffers::Offset<RunEvent> 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> RunEvent::Pack(::flatbuffers::FlatBufferBuilder &_fbb, const RunEventT* _o, const ::flatbuffers::rehasher_function_t *_rehasher) {
@@ -353,10 +353,10 @@ inline ::flatbuffers::Offset<RunEvent> 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 {