From e9b590b0ae4a2fa88a7c2c3c8995a5d336248450 Mon Sep 17 00:00:00 2001 From: Randalphwa <38287198+Randalphwa@users.noreply.github.com> Date: Fri, 12 Jun 2026 19:36:49 -0700 Subject: [PATCH 1/2] Add GenerateHelpers() Currently this generates conversions for wxString --- src/lang/lang_typescript.cpp | 92 +++++++++++++++++++++++++----------- src/lang/lang_typescript.h | 3 ++ 2 files changed, 67 insertions(+), 28 deletions(-) diff --git a/src/lang/lang_typescript.cpp b/src/lang/lang_typescript.cpp index 7aabf47..bd0fb12 100644 --- a/src/lang/lang_typescript.cpp +++ b/src/lang/lang_typescript.cpp @@ -170,40 +170,19 @@ struct WrapParam { return "Deno.PointerValue"; } - if (func.return_type == "int" || func.return_type == "TArrayLen" || - func.return_type == "TByteStringLen") - { - return "number"; - } - if (func.return_type == "long" || func.return_type == "time_t") - { - return "number"; - } - if (func.return_type == "unsigned" || func.return_type == "unsigned int") - { - return "number"; - } - if (func.return_type == "unsigned long" || func.return_type == "wxUIntPtr") + if (func.return_type == "void*" || func.return_type.find('*') != std::string::npos) { - return "number"; + return "Deno.PointerValue"; } if (func.return_type == "uintptr_t" || func.return_type == "size_t") { return "bigint"; } - if (func.return_type == "double" || func.return_type == "float") - { - return "number"; - } - if (func.return_type == "TChar" || func.return_type == "TUInt8") - { - return "number"; - } - if (func.return_type == "void*" || func.return_type.find('*') != std::string::npos) - { - return "Deno.PointerValue"; - } - return "number"; // fallback + + // All remaining numeric types (int, long, unsigned, unsigned long, double, + // float, TArrayLen, TByteStringLen, TChar, TUInt8, time_t, wxUIntPtr, etc.) + // map to the Deno FFI "number" type. + return "number"; } // Emit the return statement for a method wrapper, applying any needed conversions. @@ -296,6 +275,7 @@ void TypeScriptEmitter::Generate(const ParsedFFI& ffi, const fs::path& out_dir) GenerateFfi(ffi, out_dir); GenerateConstants(ffi, out_dir); + GenerateHelpers(out_dir); GenerateFreeFunctions(ffi, out_dir); GenerateClassFiles(ffi, out_dir); GenerateIndex(ffi, out_dir); @@ -378,6 +358,11 @@ void TypeScriptEmitter::GenerateFfi(const ParsedFFI& ffi, const fs::path& out_di << " },\n"; } + // Always-available kwxFFI symbols (string buffer utilities) + output << " kwxUtf8Buffer_Create: { parameters: [\"pointer\"], result: \"pointer\" },\n"; + output << " kwxUtf8Buffer_Data: { parameters: [\"pointer\"], result: \"pointer\" },\n"; + output << " kwxUtf8Buffer_Delete: { parameters: [\"pointer\"], result: \"void\" },\n"; + output << "} as const);\n"; std::println(stderr, @@ -387,6 +372,55 @@ void TypeScriptEmitter::GenerateFfi(const ParsedFFI& ffi, const fs::path& out_di ffi.constants.size()); } +// ------------------------------------------------------------------------- +// kwx_helpers_gen.ts — wxString conversion utilities for generated code +// ------------------------------------------------------------------------- + +void TypeScriptEmitter::GenerateHelpers(const fs::path& out_dir) +{ + const fs::path file_path = out_dir / "kwx_helpers_gen.ts"; + ConditionalFileWriter output(file_path); + + WriteGeneratedHeader(output, "//"); + output << + R"TS(import { lib } from "./kwx_ffi_gen.ts"; +import { wxString } from "./wxString_gen.ts"; + +// --- Helper: create a wxString from a JS string --- +export function createWxString(s: string): wxString +{ + const buf = new TextEncoder().encode(s + "\0"); + const ptr = Deno.UnsafePointer.of(buf); + const result = wxString.CreateUTF8(ptr); + if (!result) + { + throw new Error(`Failed to create wxString for: ${s}`); + } + return result; +} + +// --- Helper: read a JS string from a wxString* --- +export function readWxString(wxStrPtr: Deno.PointerValue): string +{ + const buf = lib.symbols.kwxUtf8Buffer_Create(wxStrPtr); + const data = lib.symbols.kwxUtf8Buffer_Data(buf); + const result = data ? Deno.UnsafePointerView.getCString(data) : ""; + lib.symbols.kwxUtf8Buffer_Delete(buf); + return result; +} + +// --- Helper: read and delete a wxString* returned from a getter --- +export function readAndDeleteWxString(wxStrPtr: Deno.PointerValue): string +{ + const result = readWxString(wxStrPtr); + lib.symbols.wxString_Delete(wxStrPtr); + return result; +} +)TS"; + + std::println(stderr, " kwx_helpers_gen.ts: wxString conversion helpers"); +} + // ------------------------------------------------------------------------- // kwx_constants_gen.ts — eagerly-evaluated constants, events, and keys // ------------------------------------------------------------------------- @@ -812,6 +846,8 @@ void TypeScriptEmitter::GenerateIndex(const ParsedFFI& ffi, const fs::path& out_ output << "export * from \"./kwx_free_functions_gen.ts\";\n"; } + output << "export * from \"./kwx_helpers_gen.ts\";\n"; + // Sort class names for deterministic output std::vector class_names; for (const auto& cls: ffi.classes) diff --git a/src/lang/lang_typescript.h b/src/lang/lang_typescript.h index 06c8d40..b30977e 100644 --- a/src/lang/lang_typescript.h +++ b/src/lang/lang_typescript.h @@ -35,4 +35,7 @@ class TypeScriptEmitter : public LanguageEmitter // Emits the body of a single TypeScript class wrapper file. static void EmitClassFile(std::ostream& output, const ClassInfo& cls, const ParsedFFI& ffi); + + // Emits kwx_helpers_gen.ts — wxString conversion utilities for generated code. + static void GenerateHelpers(const std::filesystem::path& out_dir); }; From 65734b6a952f636f712d9dae3b5ff21d0e5088a9 Mon Sep 17 00:00:00 2001 From: Randalphwa <38287198+Randalphwa@users.noreply.github.com> Date: Sun, 14 Jun 2026 12:57:43 -0700 Subject: [PATCH 2/2] Ensure we don't write duplicate functions --- src/lang/lang_typescript.cpp | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/lang/lang_typescript.cpp b/src/lang/lang_typescript.cpp index bd0fb12..2e2af11 100644 --- a/src/lang/lang_typescript.cpp +++ b/src/lang/lang_typescript.cpp @@ -313,7 +313,9 @@ void TypeScriptEmitter::GenerateFfi(const ParsedFFI& ffi, const fs::path& out_di output << "export const lib = Deno.dlopen(_libName, {\n"; // Class methods + std::unordered_set emitted_symbols; size_t method_count = 0; + size_t free_count = 0; for (const auto& cls: ffi.classes) { for (const auto& func: cls.methods) @@ -322,6 +324,11 @@ void TypeScriptEmitter::GenerateFfi(const ParsedFFI& ffi, const fs::path& out_di { continue; } + const std::string func_name = CFuncName(func); + if (!emitted_symbols.insert(func_name).second) + { + continue; + } EmitSymbolDef(output, func); ++method_count; } @@ -334,7 +341,13 @@ void TypeScriptEmitter::GenerateFfi(const ParsedFFI& ffi, const fs::path& out_di { continue; } + const std::string func_name = CFuncName(func); + if (!emitted_symbols.insert(func_name).second) + { + continue; + } EmitSymbolDef(output, func); + ++free_count; } // Event accessor symbols (no parameters, return i32) @@ -359,16 +372,25 @@ void TypeScriptEmitter::GenerateFfi(const ParsedFFI& ffi, const fs::path& out_di } // Always-available kwxFFI symbols (string buffer utilities) - output << " kwxUtf8Buffer_Create: { parameters: [\"pointer\"], result: \"pointer\" },\n"; - output << " kwxUtf8Buffer_Data: { parameters: [\"pointer\"], result: \"pointer\" },\n"; - output << " kwxUtf8Buffer_Delete: { parameters: [\"pointer\"], result: \"void\" },\n"; + if (emitted_symbols.insert("kwxUtf8Buffer_Create").second) + { + output << " kwxUtf8Buffer_Create: { parameters: [\"pointer\"], result: \"pointer\" },\n"; + } + if (emitted_symbols.insert("kwxUtf8Buffer_Data").second) + { + output << " kwxUtf8Buffer_Data: { parameters: [\"pointer\"], result: \"pointer\" },\n"; + } + if (emitted_symbols.insert("kwxUtf8Buffer_Delete").second) + { + output << " kwxUtf8Buffer_Delete: { parameters: [\"pointer\"], result: \"void\" },\n"; + } output << "} as const);\n"; std::println(stderr, " kwx_ffi_gen.ts: {} class methods, {} free functions, " "{} events, {} keys, {} constants", - method_count, ffi.free_functions.size(), ffi.events.size(), ffi.keys.size(), + method_count, free_count, ffi.events.size(), ffi.keys.size(), ffi.constants.size()); }