refactor(lite): extract owned-string core, drop dead bridge-runtime scaffold
lite_bridge_runtime.{cpp,h} was ~25k lines of dry-dispatch / dynamic-loader
scaffolding that the shipping wallet never used: 0 of its 122 public types
reached the app binary. The only live code on the bridge path was the
owned-string memory-safety helper — LiteClientBridge::linkedSdxl() already
loads the backend via direct litelib_* externs in lite_client_bridge.cpp.
- Extract LiteBridgeOwnedString + liteBridgeRuntimeTakeOwnedString into
src/wallet/lite_owned_string.{h,cpp} (the copy-before-free / free-once /
wipe / "Error:"-classify boundary), with the runtime-friend coupling removed.
- Point lite_client_bridge.cpp at the new header.
- Delete lite_bridge_runtime.{cpp,h} and the 16 runtime-only tests +
their fixtures/aliases in test_phase4.cpp; keep the 5 owned-string tests
(retargeted) and restore testGeneratedResourceBehavior, which had been
caught in the runtime-test line range.
- Swap the CMake source/header references.
Both variants build; full test suite passes; source-hygiene check clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -391,7 +391,7 @@ set(APP_SOURCES
|
|||||||
src/chat/chat_protocol.cpp
|
src/chat/chat_protocol.cpp
|
||||||
src/wallet/lite_backend_artifact_contract.cpp
|
src/wallet/lite_backend_artifact_contract.cpp
|
||||||
src/wallet/lite_backend_artifact_resolver.cpp
|
src/wallet/lite_backend_artifact_resolver.cpp
|
||||||
src/wallet/lite_bridge_runtime.cpp
|
src/wallet/lite_owned_string.cpp
|
||||||
src/wallet/lite_client_bridge.cpp
|
src/wallet/lite_client_bridge.cpp
|
||||||
src/wallet/lite_connection_service.cpp
|
src/wallet/lite_connection_service.cpp
|
||||||
src/wallet/lite_wallet_controller.cpp
|
src/wallet/lite_wallet_controller.cpp
|
||||||
@@ -507,7 +507,7 @@ set(APP_HEADERS
|
|||||||
src/wallet/wallet_backend.h
|
src/wallet/wallet_backend.h
|
||||||
src/wallet/lite_backend_artifact_contract.h
|
src/wallet/lite_backend_artifact_contract.h
|
||||||
src/wallet/lite_backend_artifact_resolver.h
|
src/wallet/lite_backend_artifact_resolver.h
|
||||||
src/wallet/lite_bridge_runtime.h
|
src/wallet/lite_owned_string.h
|
||||||
src/wallet/lite_client_bridge.h
|
src/wallet/lite_client_bridge.h
|
||||||
src/wallet/lite_connection_service.h
|
src/wallet/lite_connection_service.h
|
||||||
src/wallet/lite_result_parsers.h
|
src/wallet/lite_result_parsers.h
|
||||||
@@ -692,7 +692,7 @@ if(DRAGONX_LITE_BACKEND_READY)
|
|||||||
add_executable(lite_smoke
|
add_executable(lite_smoke
|
||||||
tools/lite_smoke.cpp
|
tools/lite_smoke.cpp
|
||||||
src/wallet/lite_client_bridge.cpp
|
src/wallet/lite_client_bridge.cpp
|
||||||
src/wallet/lite_bridge_runtime.cpp
|
src/wallet/lite_owned_string.cpp
|
||||||
src/wallet/lite_backend_artifact_contract.cpp
|
src/wallet/lite_backend_artifact_contract.cpp
|
||||||
src/wallet/lite_backend_artifact_resolver.cpp
|
src/wallet/lite_backend_artifact_resolver.cpp
|
||||||
src/wallet/lite_connection_service.cpp
|
src/wallet/lite_connection_service.cpp
|
||||||
@@ -941,7 +941,7 @@ if(BUILD_TESTING)
|
|||||||
src/chat/chat_protocol.cpp
|
src/chat/chat_protocol.cpp
|
||||||
src/wallet/lite_backend_artifact_contract.cpp
|
src/wallet/lite_backend_artifact_contract.cpp
|
||||||
src/wallet/lite_backend_artifact_resolver.cpp
|
src/wallet/lite_backend_artifact_resolver.cpp
|
||||||
src/wallet/lite_bridge_runtime.cpp
|
src/wallet/lite_owned_string.cpp
|
||||||
src/wallet/lite_client_bridge.cpp
|
src/wallet/lite_client_bridge.cpp
|
||||||
src/wallet/lite_connection_service.cpp
|
src/wallet/lite_connection_service.cpp
|
||||||
src/wallet/lite_wallet_controller.cpp
|
src/wallet/lite_wallet_controller.cpp
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@
|
|||||||
// Released under the GPLv3
|
// Released under the GPLv3
|
||||||
|
|
||||||
#include "lite_client_bridge.h"
|
#include "lite_client_bridge.h"
|
||||||
#include "lite_bridge_runtime.h"
|
#include "lite_owned_string.h"
|
||||||
|
|
||||||
#ifndef DRAGONX_ENABLE_LITE_BACKEND
|
#ifndef DRAGONX_ENABLE_LITE_BACKEND
|
||||||
#define DRAGONX_ENABLE_LITE_BACKEND 0
|
#define DRAGONX_ENABLE_LITE_BACKEND 0
|
||||||
|
|||||||
123
src/wallet/lite_owned_string.cpp
Normal file
123
src/wallet/lite_owned_string.cpp
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
// DragonX Wallet - ImGui Edition
|
||||||
|
// Copyright 2024-2026 The Hush Developers
|
||||||
|
// Released under the GPLv3
|
||||||
|
|
||||||
|
#include "lite_owned_string.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace dragonx {
|
||||||
|
namespace wallet {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// A backend response is a failure when it is prefixed with "Error:" (the litelib_* convention).
|
||||||
|
bool looksLikeError(const std::string& value)
|
||||||
|
{
|
||||||
|
return value.rfind("Error:", 0) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Best-effort scrub of a copied secret-ish payload before the string is released.
|
||||||
|
void wipeString(std::string& value)
|
||||||
|
{
|
||||||
|
std::fill(value.begin(), value.end(), '\0');
|
||||||
|
value.clear();
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
LiteBridgeOwnedString::LiteBridgeOwnedString(char* rawValue, FreeStringFn freeString)
|
||||||
|
: rawValue_(rawValue),
|
||||||
|
freeString_(freeString),
|
||||||
|
rawPointerReceived_(rawValue != nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LiteBridgeOwnedString::LiteBridgeOwnedString(LiteBridgeOwnedString&& other) noexcept
|
||||||
|
{
|
||||||
|
moveFrom(std::move(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
LiteBridgeOwnedString& LiteBridgeOwnedString::operator=(LiteBridgeOwnedString&& other) noexcept
|
||||||
|
{
|
||||||
|
if (this == &other) return *this;
|
||||||
|
copyValueBeforeFree();
|
||||||
|
releaseRawValue();
|
||||||
|
wipeString(copiedValue_);
|
||||||
|
moveFrom(std::move(other));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
LiteBridgeOwnedString::~LiteBridgeOwnedString()
|
||||||
|
{
|
||||||
|
copyValueBeforeFree();
|
||||||
|
releaseRawValue();
|
||||||
|
wipeString(copiedValue_);
|
||||||
|
}
|
||||||
|
|
||||||
|
LiteBridgeStringResult LiteBridgeOwnedString::intoResult()
|
||||||
|
{
|
||||||
|
if (resultTaken_) return {false, {}, "lite bridge string result already consumed"};
|
||||||
|
resultTaken_ = true;
|
||||||
|
|
||||||
|
if (!rawPointerReceived_) return {false, {}, "lite bridge returned a null string"};
|
||||||
|
if (!rawValue_) return {false, {}, "lite bridge string was already released"};
|
||||||
|
if (!freeString_) {
|
||||||
|
rawValue_ = nullptr;
|
||||||
|
return {false, {}, "lite bridge freeString function is unavailable"};
|
||||||
|
}
|
||||||
|
|
||||||
|
copyValueBeforeFree();
|
||||||
|
releaseRawValue();
|
||||||
|
|
||||||
|
const std::string resultValue = copiedValue_;
|
||||||
|
wipeString(copiedValue_);
|
||||||
|
if (looksLikeError(resultValue)) return {false, {}, resultValue};
|
||||||
|
return {true, resultValue, {}};
|
||||||
|
}
|
||||||
|
|
||||||
|
void LiteBridgeOwnedString::copyValueBeforeFree()
|
||||||
|
{
|
||||||
|
if (!rawValue_ || copiedBeforeFree_) return;
|
||||||
|
copiedValue_ = rawValue_;
|
||||||
|
copiedBeforeFree_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LiteBridgeOwnedString::releaseRawValue()
|
||||||
|
{
|
||||||
|
if (!rawValue_) return;
|
||||||
|
if (freeString_) {
|
||||||
|
freeString_(rawValue_);
|
||||||
|
freed_ = true;
|
||||||
|
}
|
||||||
|
rawValue_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LiteBridgeOwnedString::moveFrom(LiteBridgeOwnedString&& other) noexcept
|
||||||
|
{
|
||||||
|
rawValue_ = other.rawValue_;
|
||||||
|
freeString_ = other.freeString_;
|
||||||
|
rawPointerReceived_ = other.rawPointerReceived_;
|
||||||
|
copiedBeforeFree_ = other.copiedBeforeFree_;
|
||||||
|
freed_ = other.freed_;
|
||||||
|
resultTaken_ = other.resultTaken_;
|
||||||
|
copiedValue_ = std::move(other.copiedValue_);
|
||||||
|
|
||||||
|
other.rawValue_ = nullptr;
|
||||||
|
other.freeString_ = nullptr;
|
||||||
|
other.rawPointerReceived_ = false;
|
||||||
|
other.copiedBeforeFree_ = false;
|
||||||
|
other.freed_ = false;
|
||||||
|
other.resultTaken_ = true;
|
||||||
|
wipeString(other.copiedValue_);
|
||||||
|
}
|
||||||
|
|
||||||
|
LiteBridgeStringResult liteBridgeRuntimeTakeOwnedString(
|
||||||
|
char* rawValue,
|
||||||
|
LiteClientBridgeApi::FreeStringFn freeString)
|
||||||
|
{
|
||||||
|
LiteBridgeOwnedString ownedString(rawValue, freeString);
|
||||||
|
return ownedString.intoResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace wallet
|
||||||
|
} // namespace dragonx
|
||||||
65
src/wallet/lite_owned_string.h
Normal file
65
src/wallet/lite_owned_string.h
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
// DragonX Wallet - ImGui Edition
|
||||||
|
// Copyright 2024-2026 The Hush Developers
|
||||||
|
// Released under the GPLv3
|
||||||
|
//
|
||||||
|
// LiteBridgeOwnedString — RAII wrapper for a Rust-allocated C string returned by the litelib_*
|
||||||
|
// backend. It is the memory-safety boundary for owned strings: copy the value before freeing,
|
||||||
|
// free it exactly once via the backend's freeString function, wipe the copy, and convert to a
|
||||||
|
// LiteBridgeStringResult (classifying "Error:"-prefixed payloads as failures).
|
||||||
|
//
|
||||||
|
// Extracted from the former lite_bridge_runtime.{cpp,h} — a ~25k-line dry-dispatch /
|
||||||
|
// dynamic-loader scaffold the shipping wallet never used. Only this owned-string helper was on
|
||||||
|
// the live path: LiteClientBridge::linkedSdxl() loads the backend via direct litelib_* externs
|
||||||
|
// in lite_client_bridge.cpp, and lite_client_bridge.cpp converts each owned string through
|
||||||
|
// liteBridgeRuntimeTakeOwnedString() below.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "lite_client_bridge.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace dragonx::wallet {
|
||||||
|
|
||||||
|
class LiteBridgeOwnedString {
|
||||||
|
public:
|
||||||
|
using FreeStringFn = LiteClientBridgeApi::FreeStringFn;
|
||||||
|
|
||||||
|
LiteBridgeOwnedString() = default;
|
||||||
|
LiteBridgeOwnedString(char* rawValue, FreeStringFn freeString);
|
||||||
|
LiteBridgeOwnedString(const LiteBridgeOwnedString&) = delete;
|
||||||
|
LiteBridgeOwnedString& operator=(const LiteBridgeOwnedString&) = delete;
|
||||||
|
LiteBridgeOwnedString(LiteBridgeOwnedString&& other) noexcept;
|
||||||
|
LiteBridgeOwnedString& operator=(LiteBridgeOwnedString&& other) noexcept;
|
||||||
|
~LiteBridgeOwnedString();
|
||||||
|
|
||||||
|
// Consume the owned string into a result (copy-before-free, free-once). Idempotent: a second
|
||||||
|
// call returns an "already consumed" failure.
|
||||||
|
LiteBridgeStringResult intoResult();
|
||||||
|
|
||||||
|
bool rawPointerReceived() const { return rawPointerReceived_; }
|
||||||
|
bool copiedBeforeFree() const { return copiedBeforeFree_; }
|
||||||
|
bool freed() const { return freed_; }
|
||||||
|
bool rawPointerEscaped() const { return false; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void copyValueBeforeFree();
|
||||||
|
void releaseRawValue();
|
||||||
|
void moveFrom(LiteBridgeOwnedString&& other) noexcept;
|
||||||
|
|
||||||
|
char* rawValue_ = nullptr;
|
||||||
|
FreeStringFn freeString_ = nullptr;
|
||||||
|
bool rawPointerReceived_ = false;
|
||||||
|
bool copiedBeforeFree_ = false;
|
||||||
|
bool freed_ = false;
|
||||||
|
bool resultTaken_ = false;
|
||||||
|
std::string copiedValue_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Take ownership of a backend-returned raw string and convert it to a result (copy-before-free,
|
||||||
|
// free-once) using the backend's deallocator. The single live entry point used by the bridge.
|
||||||
|
LiteBridgeStringResult liteBridgeRuntimeTakeOwnedString(
|
||||||
|
char* rawValue,
|
||||||
|
LiteClientBridgeApi::FreeStringFn freeString);
|
||||||
|
|
||||||
|
} // namespace dragonx::wallet
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user