Skip to content

Commit 7753bd1

Browse files
fix: not existing error type, add comments to JSI code
1 parent 962f1c3 commit 7753bd1

3 files changed

Lines changed: 60 additions & 73 deletions

File tree

packages/react-native-executorch/common/rnexecutorch/host_objects/ModelHostObject.h

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,6 @@ template <typename Model> class ModelHostObject : public JsiHostObject {
4646
"getInputShape"));
4747
}
4848

49-
if constexpr (meta::HasGenerateFromString<Model>) {
50-
addFunctions(
51-
JSI_EXPORT_FUNCTION(ModelHostObject<Model>,
52-
promiseHostFunction<&Model::generateFromString>,
53-
"generateFromString"));
54-
}
55-
5649
if constexpr (meta::HasEncode<Model>) {
5750
addFunctions(JSI_EXPORT_FUNCTION(ModelHostObject<Model>,
5851
promiseHostFunction<&Model::encode>,
@@ -172,6 +165,13 @@ template <typename Model> class ModelHostObject : public JsiHostObject {
172165
"stream"));
173166
}
174167

168+
if constexpr (meta::HasGenerateFromString<Model>) {
169+
addFunctions(
170+
JSI_EXPORT_FUNCTION(ModelHostObject<Model>,
171+
promiseHostFunction<&Model::generateFromString>,
172+
"generateFromString"));
173+
}
174+
175175
if constexpr (meta::HasGenerateFromFrame<Model>) {
176176
addFunctions(JSI_EXPORT_FUNCTION(
177177
ModelHostObject<Model>, visionHostFunction<&Model::generateFromFrame>,
@@ -181,7 +181,7 @@ template <typename Model> class ModelHostObject : public JsiHostObject {
181181
if constexpr (meta::HasGenerateFromPixels<Model>) {
182182
addFunctions(
183183
JSI_EXPORT_FUNCTION(ModelHostObject<Model>,
184-
visionHostFunction<&Model::generateFromPixels>,
184+
promiseHostFunction<&Model::generateFromPixels>,
185185
"generateFromPixels"));
186186
}
187187
}
@@ -233,47 +233,49 @@ template <typename Model> class ModelHostObject : public JsiHostObject {
233233
}
234234
}
235235

236+
/**
237+
* Unlike promiseHostFunction, this runs synchronously on the JS thread,
238+
* which is required for VisionCamera worklet frame processors.
239+
*
240+
* The key challenge is argument mapping: the C++ function takes
241+
* (Runtime, frameData, Rest...) but from the JS side, Runtime is injected
242+
* automatically and frameData is JS args[0]. The remaining args (Rest...)
243+
* map to JS args[1..N].
244+
*
245+
* This is achieved via TailSignature: it extracts the Rest... parameter pack
246+
* from the function pointer type, creates a dummy free function with only
247+
* those types, then uses createArgsTupleFromJsi on that dummy to convert
248+
* args[1..N] — bypassing the manually-handled frameData at args[0].
249+
*
250+
* Argument mapping:
251+
* C++ params: (Runtime&, frameData, Rest[0], Rest[1], ...)
252+
* JS args: ( args[0], args[1], args[2], ...)
253+
* JS arg count = C++ arity - 1 (Runtime is injected, not counted)
254+
*
255+
*/
236256
template <auto FnPtr> JSI_HOST_FUNCTION(visionHostFunction) {
237-
// 1. Check Argument Count
238-
// (We rely on our new FunctionTraits)
239257
constexpr std::size_t cppArgCount =
240258
meta::FunctionTraits<decltype(FnPtr)>::arity;
241-
242-
// We expect JS args = (Total C++ Args) - (2 injected args: Runtime + Value)
243259
constexpr std::size_t expectedJsArgs = cppArgCount - 1;
244-
log(LOG_LEVEL::Debug, cppArgCount, count);
260+
245261
if (count != expectedJsArgs) {
246262
throw jsi::JSError(runtime, "Argument count mismatch in vision function");
247263
}
248264

249265
try {
250-
// 2. The Magic Trick
251-
// We get a pointer to a dummy function: void dummy(Rest...) {}
252-
// This function has exactly the signature of the arguments we want to
253-
// parse.
254266
auto dummyFuncPtr = &meta::TailSignature<decltype(FnPtr)>::dummy;
255-
256-
// 3. Let existing helpers do the work
257-
// We pass the dummy pointer. The helper inspects its arguments (Rest...)
258-
// and converts args[0]...args[N] accordingly.
259-
// Note: We pass (args + 1) because JS args[0] is the PixelData, which we
260-
// handle manually. Note: We use expectedJsArgs - 1 because we skipped one
261-
// JS arg.
262267
auto tailArgsTuple =
263268
meta::createArgsTupleFromJsi(dummyFuncPtr, args + 1, runtime);
264269

265-
// 4. Invoke
266270
using ReturnType =
267271
typename meta::FunctionTraits<decltype(FnPtr)>::return_type;
268272

269273
if constexpr (std::is_void_v<ReturnType>) {
270274
std::apply(
271275
[&](auto &&...tailArgs) {
272276
(model.get()->*FnPtr)(
273-
runtime,
274-
args[0], // 1. PixelData (Manually passed)
275-
std::forward<decltype(tailArgs)>(
276-
tailArgs)...); // 2. The rest (Auto parsed)
277+
runtime, args[0],
278+
std::forward<decltype(tailArgs)>(tailArgs)...);
277279
},
278280
std::move(tailArgsTuple));
279281
return jsi::Value::undefined();

packages/react-native-executorch/common/rnexecutorch/metaprogramming/FunctionHelpers.h

Lines changed: 26 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,32 +10,6 @@
1010
namespace rnexecutorch::meta {
1111
using namespace facebook;
1212

13-
// =========================================================================
14-
// 1. Function Traits (Extracts Arity, Return Type, Args)
15-
// =========================================================================
16-
17-
template <typename T> struct FunctionTraits;
18-
19-
// Specialization for Member Functions
20-
template <typename R, typename C, typename... Args>
21-
struct FunctionTraits<R (C::*)(Args...)> {
22-
static constexpr std::size_t arity = sizeof...(Args);
23-
using return_type = R;
24-
using args_tuple = std::tuple<Args...>;
25-
};
26-
27-
// Specialization for const Member Functions
28-
template <typename R, typename C, typename... Args>
29-
struct FunctionTraits<R (C::*)(Args...) const> {
30-
static constexpr std::size_t arity = sizeof...(Args);
31-
using return_type = R;
32-
using args_tuple = std::tuple<Args...>;
33-
};
34-
35-
// =========================================================================
36-
// 2. Argument Counting Helpers
37-
// =========================================================================
38-
3913
template <typename Model, typename R, typename... Types>
4014
constexpr std::size_t getArgumentCount(R (Model::*f)(Types...)) {
4115
return sizeof...(Types);
@@ -46,10 +20,6 @@ constexpr std::size_t getArgumentCount(R (Model::*f)(Types...) const) {
4620
return sizeof...(Types);
4721
}
4822

49-
// =========================================================================
50-
// 3. JSI -> Tuple Conversion Logic
51-
// =========================================================================
52-
5323
template <typename... Types, std::size_t... I>
5424
std::tuple<Types...> fillTupleFromArgs(std::index_sequence<I...>,
5525
const jsi::Value *args,
@@ -62,6 +32,7 @@ std::tuple<Types...> fillTupleFromArgs(std::index_sequence<I...>,
6232
* arguments for method supplied with a pointer. The types in the tuple are
6333
* inferred from the method pointer.
6434
*/
35+
6536
template <typename Model, typename R, typename... Types>
6637
std::tuple<Types...> createArgsTupleFromJsi(R (Model::*f)(Types...),
6738
const jsi::Value *args,
@@ -78,7 +49,9 @@ std::tuple<Types...> createArgsTupleFromJsi(R (Model::*f)(Types...) const,
7849
runtime);
7950
}
8051

81-
// Overload for free functions (used by TailSignature dummy)
52+
// Free function overload used by visionHostFunction: accepts a dummy free
53+
// function pointer whose parameter types (Rest...) are extracted by
54+
// TailSignature and converted from JSI args.
8255
template <typename... Types>
8356
std::tuple<Types...> createArgsTupleFromJsi(void (*f)(Types...),
8457
const jsi::Value *args,
@@ -87,27 +60,40 @@ std::tuple<Types...> createArgsTupleFromJsi(void (*f)(Types...),
8760
runtime);
8861
}
8962

90-
// =========================================================================
91-
// 4. Tail Signature Helper (Crucial for Vision Functions)
92-
// =========================================================================
63+
// Extracts arity, return type, and argument types from a member function
64+
// pointer at compile time. Used by visionHostFunction to determine the expected
65+
// JS argument count and invoke the correct return path.
66+
template <typename T> struct FunctionTraits;
67+
68+
template <typename R, typename C, typename... Args>
69+
struct FunctionTraits<R (C::*)(Args...)> {
70+
static constexpr std::size_t arity = sizeof...(Args);
71+
using return_type = R;
72+
using args_tuple = std::tuple<Args...>;
73+
};
74+
75+
template <typename R, typename C, typename... Args>
76+
struct FunctionTraits<R (C::*)(Args...) const> {
77+
static constexpr std::size_t arity = sizeof...(Args);
78+
using return_type = R;
79+
using args_tuple = std::tuple<Args...>;
80+
};
9381

94-
// Extracts the "Tail" arguments of a function signature, skipping the first
95-
// two arguments (Runtime and FrameValue).
82+
// Strips the first two parameters (Runtime& and jsi::Value&) from a member
83+
// function pointer and exposes the remaining types as a dummy free function.
84+
// Used by visionHostFunction to parse only the tail JS args via
85+
// createArgsTupleFromJsi, while frameData at args[0] is passed manually.
9686
template <typename T> struct TailSignature;
9787

98-
// Non-const member function specialization
9988
template <typename R, typename C, typename Arg1, typename Arg2,
10089
typename... Rest>
10190
struct TailSignature<R (C::*)(Arg1, Arg2, Rest...)> {
102-
// A dummy function that has the signature of just the "Rest" arguments.
10391
static void dummy(Rest...) {}
10492
};
10593

106-
// Const member function specialization
10794
template <typename R, typename C, typename Arg1, typename Arg2,
10895
typename... Rest>
10996
struct TailSignature<R (C::*)(Arg1, Arg2, Rest...) const> {
11097
static void dummy(Rest...) {}
11198
};
112-
11399
} // namespace rnexecutorch::meta

packages/react-native-executorch/common/rnexecutorch/utils/FrameExtractor.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,14 @@ cv::Mat extractFromAHardwareBuffer(void *hardwareBuffer) {
8484
char errorMessage[100];
8585
std::snprintf(errorMessage, sizeof(errorMessage),
8686
"Unsupported AHardwareBuffer format: %u", desc.format);
87-
throw RnExecutorchError(RnExecutorchErrorCode::InvalidUserInput,
88-
errorMessage);
87+
throw RnExecutorchError(RnExecutorchErrorCode::UnknownError, errorMessage);
8988
}
9089

9190
// Note: We don't unlock here - Vision Camera manages the lifecycle
9291

9392
return mat;
9493
#else
95-
throw RnExecutorchError(RnExecutorchErrorCode::NotSupported,
94+
throw RnExecutorchError(RnExecutorchErrorCode::UnknownError,
9695
"AHardwareBuffer requires Android API 26+");
9796
#endif // __ANDROID_API__ >= 26
9897
}
@@ -106,7 +105,7 @@ cv::Mat extractFromNativeBuffer(uint64_t bufferPtr) {
106105
#elif defined(__ANDROID__)
107106
return extractFromAHardwareBuffer(reinterpret_cast<void *>(bufferPtr));
108107
#else
109-
throw RnExecutorchError(RnExecutorchErrorCode::NotSupported,
108+
throw RnExecutorchError(RnExecutorchErrorCode::UnknownError,
110109
"NativeBuffer not supported on this platform");
111110
#endif
112111
}

0 commit comments

Comments
 (0)