Skip to content
Open
44 changes: 24 additions & 20 deletions codegen/lib/templates/cpp/method_forward.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,32 @@
method = locals[:method]
arguments = locals[:arguments] || ['instance'] + WasmCppHelper.arguments(method.parameters.drop(1))
call = render('cpp/method_call.erb', { method: method, arguments: arguments })
data_params = method.parameters.select { |p| p.type.name == :data }
needs_wipe = !data_params.empty?

# Method returns data
if should_return_data(method)
if method.return_type.is_nullable -%>
return TWDataToVal(<%= call %>);
<% else -%>
return TWDataToVal(<%= call %>);
<% end
# Method returns a string
return_expr = "TWDataToVal(#{call})"
elsif should_return_string(method)
if method.return_type.is_nullable -%>
return TWStringToStd(<%= call %>);
<% else -%>
return TWStringToStd(<%= call %>);
<% end
# Method returns a class or struct
return_expr = "TWStringToVal(#{call})"
elsif method.return_type.is_class || method.return_type.is_struct
if method.return_type.is_nullable -%>
return new Wasm<%= method.return_type.name %>(<%= call %>);
return_expr = "new Wasm#{method.return_type.name}(#{call})"
else
return_expr = call
end
-%>
<% if needs_wipe -%>
<% if method.return_type.name == :void -%>
<%= return_expr %>;
<% data_params.each do |p| -%>
if (!<%= p.name %>Data.empty()) { memzero(<%= p.name %>Data.data(), <%= p.name %>Data.size()); }
<% end -%>
<% else -%>
auto result = <%= return_expr %>;
<% data_params.each do |p| -%>
if (!<%= p.name %>Data.empty()) { memzero(<%= p.name %>Data.data(), <%= p.name %>Data.size()); }
<% end -%>
return result;
Comment thread
sergei-boiko-trustwallet marked this conversation as resolved.
<% end -%>
<% else -%>
return new Wasm<%= method.return_type.name %>(<%= call %>);
<% end
else -%>
return <%= call %>;
<%end -%>
return <%= return_expr %>;
<% end -%>
4 changes: 2 additions & 2 deletions wasm/src/AnySigner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class AnySigner {
static auto sign(const std::string& string, TWCoinType coin) {
Data out;
TW::anyCoinSign(coin, TW::data(string), out);
return DataToVal(out);
return DataToVal(std::move(out));
}

static auto supportsJSON(TWCoinType coin) {
Expand All @@ -28,7 +28,7 @@ class AnySigner {
static auto plan(const std::string& string, TWCoinType coin) {
Data out;
TW::anyCoinPlan(coin, TW::data(string), out);
return DataToVal(out);
return DataToVal(std::move(out));
}
};

Expand Down
12 changes: 6 additions & 6 deletions wasm/src/CoinTypeExt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ namespace TW::Wasm {
return TWCoinTypeStaticPrefix(coin);
}
auto CoinTypeExt::chainId(TWCoinType coin) {
return TWStringToStd(TWCoinTypeChainId(coin));
return TWStringToVal(TWCoinTypeChainId(coin));
}
auto CoinTypeExt::slip44Id(TWCoinType coin) {
return TWCoinTypeSlip44Id(coin);
Expand All @@ -59,19 +59,19 @@ namespace TW::Wasm {
return TWCoinTypeValidate(coin, &address);
}
auto CoinTypeExt::derivationPath(TWCoinType coin) {
return TWStringToStd(TWCoinTypeDerivationPath(coin));
return TWStringToVal(TWCoinTypeDerivationPath(coin));
}
auto CoinTypeExt::derivationPathWithDerivation(TWCoinType coin, TWDerivation derivation) {
return TWStringToStd(TWCoinTypeDerivationPathWithDerivation(coin, derivation));
return TWStringToVal(TWCoinTypeDerivationPathWithDerivation(coin, derivation));
}
auto CoinTypeExt::deriveAddress(TWCoinType coin, WasmPrivateKey* privateKey) {
return TWStringToStd(TWCoinTypeDeriveAddress(coin, privateKey->instance));
return TWStringToVal(TWCoinTypeDeriveAddress(coin, privateKey->instance));
}
auto CoinTypeExt::deriveAddressFromPublicKey(TWCoinType coin, WasmPublicKey* publicKey) {
return TWStringToStd(TWCoinTypeDeriveAddressFromPublicKey(coin, publicKey->instance));
return TWStringToVal(TWCoinTypeDeriveAddressFromPublicKey(coin, publicKey->instance));
}
auto CoinTypeExt::deriveAddressFromPublicKeyAndDerivation(TWCoinType coin, WasmPublicKey* publicKey, TWDerivation derivation) {
return TWStringToStd(TWCoinTypeDeriveAddressFromPublicKeyAndDerivation(coin, publicKey->instance, derivation));
return TWStringToVal(TWCoinTypeDeriveAddressFromPublicKeyAndDerivation(coin, publicKey->instance, derivation));
}

EMSCRIPTEN_BINDINGS(Wasm_CoinTypeExt) {
Expand Down
3 changes: 2 additions & 1 deletion wasm/src/HexCoding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ namespace TW::Wasm {
class HexCoding {
public:
static auto parseHex(const std::string& string) {
return DataToVal(TW::parse_hex(string, true));
auto data = TW::parse_hex(string, true);
return DataToVal(std::move(data));
}

static auto hexEncoded(const std::string& string) {
Expand Down
17 changes: 12 additions & 5 deletions wasm/src/WasmData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,19 @@
#include "WasmData.h"
#include "Defer.h"

#include <TrezorCrypto/memzero.h>

using namespace emscripten;

namespace TW::Wasm {

auto DataToVal(Data data) -> val {
auto view = val(typed_memory_view(data.size(), data.data()));
auto jsArray = val::global("Uint8Array").new_(data.size());
auto DataToVal(Data&& data) -> val {
Data local = std::move(data); // take ownership; caller's object is now empty
defer {
if (!local.empty()) { memzero(local.data(), local.size()); }
};
auto view = val(typed_memory_view(local.size(), local.data()));
auto jsArray = val::global("Uint8Array").new_(local.size());
jsArray.call<void>("set", view);
return jsArray;
}
Expand All @@ -21,8 +27,9 @@ auto TWDataToVal(TWData* _Nonnull data) -> val {
defer {
TWDataDelete(data);
};
auto* v = reinterpret_cast<const Data*>(data);
return DataToVal(*v);
auto* v = const_cast<Data*>(reinterpret_cast<const Data*>(data));
Data local = std::move(*v); // *v is now genuinely empty; local owns the buffer
return DataToVal(std::move(local));
}

} // namespace TW::Wasm
4 changes: 3 additions & 1 deletion wasm/src/WasmData.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
#pragma once

#include <TrustWalletCore/TWData.h>
#include <TrezorCrypto/memzero.h>
#include <emscripten/val.h>

#include "Data.h"

namespace TW::Wasm {

auto DataToVal(Data data) -> emscripten::val;
/// Takes ownership of data, copies to a JS Uint8Array, then zeroes the WASM-heap buffer.
auto DataToVal(Data&& data) -> emscripten::val;
Comment thread
sergei-boiko-trustwallet marked this conversation as resolved.

/// Converts a TWData * to Uint8Array, deleting the TWData * when done.
auto TWDataToVal(TWData *_Nonnull data) -> emscripten::val;
Expand Down
16 changes: 14 additions & 2 deletions wasm/src/WasmString.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,26 @@
#include "WasmString.h"
#include "Defer.h"

#include <emscripten/val.h>

using namespace emscripten;

namespace TW::Wasm {

auto TWStringToStd(TWString *_Nonnull string) -> std::string {
auto TWStringToVal(TWString *_Nonnull string) -> val {
defer {
TWStringDelete(string);
};
auto* s = reinterpret_cast<const std::string*>(string);
auto result = *s;
// View the original bytes in-place — no WASM-heap copy.
auto view = val(typed_memory_view(s->size(), reinterpret_cast<const uint8_t*>(s->data())));
// Copy once into a JS-heap Uint8Array (not in WASM linear memory).
auto jsArr = val::global("Uint8Array").new_(view);
// Decode to a JS string.
auto result = val::global("TextDecoder").new_().call<val>("decode", jsArr);
// Zero the JS-heap intermediate byte copy.
jsArr.call<void>("fill", val(0));
// defer calls TWStringDelete, which calls memzero on the buffer before freeing.
return result;
}

Expand Down
7 changes: 5 additions & 2 deletions wasm/src/WasmString.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
#pragma once

#include <TrustWalletCore/TWString.h>
#include <emscripten/val.h>
#include <string>

namespace TW::Wasm {

/// Converts a TWString * to std::string, deleting the TWString * when done.
auto TWStringToStd(TWString *_Nonnull string) -> std::string;
/// Converts a TWString * to a JS string val.
/// Zeroes the WASM-heap buffer via TWStringDelete (which calls memzero internally),
/// and explicitly zeroes the intermediate JS-heap Uint8Array before returning.
auto TWStringToVal(TWString *_Nonnull string) -> emscripten::val;

} // namespace TW::Wasm
Loading