Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ jsconfig.json
.cache/
compile_commands.json
compile_flags.txt
# ...but the core package ships a shared, committed clangd config for its C++ sources:
!/packages/react-native-executorch/.clangd
!/packages/react-native-executorch/compile_flags.txt

# Xcode
#
Expand Down
34 changes: 34 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,40 @@ Found a model you would like to use in your app but it is not currently supplied
Do you have a neat example use case and want to share it with us? You can just drop us a message on Discord server and/or open a PR to `apps` directory here.
If you found some inconsistencies in our documentation or just something is missing just open a PR with suggested changes (remember to add changes to previous docs versions too, e.g `docs/versioned_docs/version-0.3.x`, `docs/versioned_docs/version-0.4.x`).

# C++ tooling (clangd)

The core package ships a committed [clangd](https://clangd.llvm.org/) setup so the
C/C++ sources under `packages/react-native-executorch/cpp` get code intelligence and
a strict, shared set of compiler warnings:

- `packages/react-native-executorch/compile_flags.txt` — the compilation database:
language standard, preprocessor defines, and include roots (paths are relative to
the package, so the config is portable). ExecuTorch/torch/JSI headers are added as
system includes so their internal warnings don't pollute diagnostics for our code.
- `packages/react-native-executorch/.clangd` — the warning policy layered on top.

For clangd to resolve the `<executorch/...>` and `<jsi/jsi.h>` includes you need the
same prerequisites as a native build:

1. `yarn install` at the repo root (provides the JSI headers under `node_modules`).
2. ExecuTorch headers provisioned under `packages/react-native-executorch/third-party/include`
(see [third-party/README.md](./packages/react-native-executorch/third-party/README.md)).

Without them clangd still lints your own code; the third-party includes simply stay
unresolved until the headers are present.

A `pre-commit` hook (lefthook) compiles staged `cpp/` sources with this same warning set
and aborts the commit if any warning is introduced — the editor and the commit gate stay
in sync. It skips automatically when no compiler or the provisioned headers are available,
so it never blocks contributors who don't build the native code; bypass with
`git commit --no-verify`.

Editor setup: install the official **clangd** extension (e.g. `llvm-vs-code-extensions.vscode-clangd`
for VS Code) and disable the default Microsoft C/C++ IntelliSense engine so the two
don't conflict. clangd discovers `compile_flags.txt`/`.clangd` automatically from the
open file's directory. If you produce a `compile_commands.json` from a native build,
clangd will prefer it — it's gitignored and takes precedence over `compile_flags.txt`.

# Creating a Pull Request

Before writing any code reach out to us to make sure no one is currently working on it, you can always open an issue first.
Expand Down
3 changes: 3 additions & 0 deletions lefthook.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ pre-commit:
format-objc:
glob: '*.{h,hpp,m,mm,c,cpp}'
run: clang-format -i {staged_files} && git add {staged_files}
cpp-warnings:
glob: '*.{h,hpp,c,cpp}'
run: packages/react-native-executorch/scripts/check-cpp-warnings.sh {staged_files}
format-kotlin:
glob: '*.{kt}'
run: ktlint -F {staged_files} && git add {staged_files}
Expand Down
33 changes: 33 additions & 0 deletions packages/react-native-executorch/.clangd
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# clangd configuration for the react-native-executorch C/C++ sources.
#
# Include roots, the language standard, and preprocessor defines live in
# `compile_flags.txt` (the compilation database). This file layers the project's
# warning policy on top and configures clangd's diagnostics.
#
# The warning set is intentionally strict to surface bugs early during the
# rewrite. Third-party headers are included as `-isystem` (see compile_flags.txt),
# so these warnings only apply to our own code in `cpp/`.
CompileFlags:
Add:
- -Wall
- -Wextra
- -Wpedantic
- -Wshadow
- -Wnon-virtual-dtor
- -Woverloaded-virtual
- -Wold-style-cast
- -Wcast-align
- -Wunused
- -Wnull-dereference
- -Wimplicit-fallthrough
- -Wextra-semi
- -Wformat=2
- -Wconversion
- -Wsign-conversion
- -Wdouble-promotion

Diagnostics:
# ExecuTorch/torch rely heavily on umbrella headers, which clangd's include
# cleaner misreports as unused/missing. Disable it to avoid false positives.
UnusedIncludes: None
MissingIncludes: None
11 changes: 11 additions & 0 deletions packages/react-native-executorch/compile_flags.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-xc++
-std=c++20
-DC10_USING_CUSTOM_GENERATED_MACROS=1
-DEXECUTORCH_ENABLE_EXECUTION_PROFILING=1
-Icpp
-isystem../../node_modules/react-native/ReactCommon/jsi
-isystemthird-party/include
-isystemthird-party/include/executorch/extension/llm/tokenizers/include
-isystemthird-party/include/executorch/extension/llm/tokenizers/third-party/json/include
-isystemthird-party/include/executorch/extension/llm/tokenizers/third-party/re2
-isystemthird-party/include/executorch/extension/llm/tokenizers/third-party/abseil-cpp
10 changes: 5 additions & 5 deletions packages/react-native-executorch/cpp/core/model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jsi::Value ModelHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &name) {

if (nameStr == "getMethodNames") {
auto self = shared_from_this();
auto fnBody = [self](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value {
auto fnBody = [self](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value * /*args*/, size_t count) -> jsi::Value {
if (count != 0) {
throw jsi::JSError(rt, "getMethodNames: Usage: getMethodNames()");
}
Expand Down Expand Up @@ -61,7 +61,7 @@ jsi::Value ModelHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &name) {

if (nameStr == "getMethodMeta") {
auto self = shared_from_this();
auto fnBody = [self](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value {
auto fnBody = [self](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value {
if (count != 1) {
throw jsi::JSError(rt, "getMethodMeta: Usage: getMethodMeta(methodName)");
}
Expand Down Expand Up @@ -176,7 +176,7 @@ jsi::Value ModelHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &name) {

if (nameStr == "execute") {
auto self = shared_from_this();
auto fnBody = [self](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value {
auto fnBody = [self](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value {
if (count != 3) {
throw jsi::JSError(rt, "execute: Usage: execute(methodName, inputs, outputTensors)");
}
Expand Down Expand Up @@ -435,7 +435,7 @@ jsi::Value ModelHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &name) {

if (nameStr == "dispose") {
auto self = shared_from_this();
auto fnBody = [self](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value {
auto fnBody = [self](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value * /*args*/, size_t count) -> jsi::Value {
if (count != 0) {
throw jsi::JSError(rt, "dispose: Usage: dispose()");
}
Expand Down Expand Up @@ -468,7 +468,7 @@ std::vector<facebook::jsi::PropNameID> ModelHostObject::getPropertyNames(jsi::Ru

void install_loadModel(jsi::Runtime &rt, jsi::Object &module) {
auto name = "loadModel";
auto fnBody = [](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value {
auto fnBody = [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value {
if (count != 1) {
throw jsi::JSError(rt, "loadModel: Usage: loadModel(arg0)");
}
Expand Down
8 changes: 4 additions & 4 deletions packages/react-native-executorch/cpp/core/tensor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jsi::Value TensorHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &name)

if (nameStr == "copyTo") {
auto self = shared_from_this();
auto fnBody = [self](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value {
auto fnBody = [self](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value {
if (count != 1) {
throw jsi::JSError(rt, "copyTo: Usage: copyTo(dst)");
}
Expand Down Expand Up @@ -143,7 +143,7 @@ jsi::Value TensorHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &name)

if (nameStr == "getData") {
auto self = shared_from_this();
auto fnBody = [self](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value {
auto fnBody = [self](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value {
if (count != 1) {
throw jsi::JSError(rt, "getData: Usage: getData(array)");
}
Expand Down Expand Up @@ -259,7 +259,7 @@ jsi::Value TensorHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &name)

if (nameStr == "dispose") {
auto self = shared_from_this();
auto fnBody = [self](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value {
auto fnBody = [self](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value * /*args*/, size_t count) -> jsi::Value {
if (count != 0) {
throw jsi::JSError(rt, "dispose: Usage: dispose()");
}
Expand Down Expand Up @@ -297,7 +297,7 @@ std::vector<facebook::jsi::PropNameID> TensorHostObject::getPropertyNames(jsi::R

void install_createTensor(jsi::Runtime &rt, jsi::Object &module) {
auto name = "createTensor";
auto fnBody = [](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value {
auto fnBody = [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value {
if (count != 2) {
throw jsi::JSError(rt, "createTensor: Usage: createTensor(shape, dtype)");
}
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native-executorch/cpp/core/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace jsi = facebook::jsi;

void install_getExecuTorchRegisteredBackends(jsi::Runtime &rt, jsi::Object &module) {
auto name = "getExecuTorchRegisteredBackends";
auto fnBody = [](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value {
auto fnBody = [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value * /*args*/, size_t count) -> jsi::Value {
if (count != 0) {
throw jsi::JSError(rt, "Usage: getExecuTorchRegisteredBackends()");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ std::array<float, 4> decodeToXyxy(

void install_nms(jsi::Runtime &rt, jsi::Object &module) {
auto name = "nms";
auto fnBody = [](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value {
auto fnBody = [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value {
if (count < 3) {
throw jsi::JSError(rt, "Usage: nms(boxes, scores, options)");
}
Expand Down Expand Up @@ -143,7 +143,7 @@ void install_nms(jsi::Runtime &rt, jsi::Object &module) {
const float *scoresPtr = reinterpret_cast<const float *>(scores->data_.get());

std::vector<std::pair<std::int32_t, float>> candidates;
candidates.reserve(numAnchors);
candidates.reserve(static_cast<size_t>(numAnchors));

for (size_t idx = 0; std::cmp_less(idx, numAnchors); ++idx) {
float score = scoresPtr[idx];
Expand Down
25 changes: 13 additions & 12 deletions packages/react-native-executorch/cpp/extensions/cv/image_ops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ FitBox computeFit(int32_t srcW, int32_t srcH, int32_t dstW, int32_t dstH, bool i

void install_resize(jsi::Runtime &rt, jsi::Object &module) {
auto name = "resize";
auto fnBody = [](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value {
auto fnBody = [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value {
if (count != 3) {
throw jsi::JSError(rt, "Usage: resize(src, dst, options)");
}
Expand Down Expand Up @@ -233,7 +233,7 @@ int codeToColorConversionFlag(const std::string &code) {

void install_cvtColor(jsi::Runtime &rt, jsi::Object &module) {
auto name = "cvtColor";
auto fnBody = [](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value {
auto fnBody = [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value {
if (count != 3) {
throw jsi::JSError(rt, "Usage: cvtColor(src, dst, code)");
}
Expand Down Expand Up @@ -319,7 +319,7 @@ void install_cvtColor(jsi::Runtime &rt, jsi::Object &module) {

void install_toChannelsFirst(jsi::Runtime &rt, jsi::Object &module) {
auto name = "toChannelsFirst";
auto fnBody = [](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value {
auto fnBody = [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value {
if (count != 2) {
throw jsi::JSError(rt, "Usage: toChannelsFirst(src, dst)");
}
Expand Down Expand Up @@ -391,7 +391,7 @@ void install_toChannelsFirst(jsi::Runtime &rt, jsi::Object &module) {
std::vector<::cv::Mat> channels;
::cv::split(srcMat, channels);

int32_t hw = srcH * srcW;
size_t hw = static_cast<size_t>(srcH) * static_cast<size_t>(srcW);
size_t elemSize = rnexecutorch::core::types::elementSize(src->dtype_);
uint8_t *dstPtr = dst->data_.get();

Expand All @@ -407,7 +407,7 @@ void install_toChannelsFirst(jsi::Runtime &rt, jsi::Object &module) {

void install_toChannelsLast(jsi::Runtime &rt, jsi::Object &module) {
auto name = "toChannelsLast";
auto fnBody = [](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value {
auto fnBody = [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value {
if (count != 2) {
throw jsi::JSError(rt, "Usage: toChannelsLast(src, dst)");
}
Expand Down Expand Up @@ -475,7 +475,7 @@ void install_toChannelsLast(jsi::Runtime &rt, jsi::Object &module) {
throw jsi::JSError(rt, "toChannelsLast: " + std::string(e.what()));
}

int32_t hw = srcH * srcW;
size_t hw = static_cast<size_t>(srcH) * static_cast<size_t>(srcW);
size_t elemSize = rnexecutorch::core::types::elementSize(src->dtype_);
uint8_t *srcPtr = src->data_.get();

Expand All @@ -495,7 +495,7 @@ void install_toChannelsLast(jsi::Runtime &rt, jsi::Object &module) {

void install_normalize(jsi::Runtime &rt, jsi::Object &module) {
auto name = "normalize";
auto fnBody = [](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value {
auto fnBody = [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value {
if (count != 3) {
throw jsi::JSError(rt, "Usage: normalize(src, dst, options)");
}
Expand Down Expand Up @@ -541,7 +541,7 @@ void install_normalize(jsi::Runtime &rt, jsi::Object &module) {
}

auto val = opts.getProperty(rt, name);
std::vector<double> result(c);
std::vector<double> result(static_cast<size_t>(c));

if (val.isNumber()) {
std::ranges::fill(result, val.asNumber());
Expand Down Expand Up @@ -602,9 +602,10 @@ void install_normalize(jsi::Runtime &rt, jsi::Object &module) {
uint8_t *srcPtr = src->data_.get();
uint8_t *dstPtr = dst->data_.get();

const size_t plane = static_cast<size_t>(h) * static_cast<size_t>(w);
for (size_t ch = 0; std::cmp_less(ch, c); ++ch) {
::cv::Mat srcChannel(h, w, srcDepthType, srcPtr + ch * h * w * srcElemSize);
::cv::Mat dstChannel(h, w, dstDepthType, dstPtr + ch * h * w * dstElemSize);
::cv::Mat srcChannel(h, w, srcDepthType, srcPtr + ch * plane * srcElemSize);
::cv::Mat dstChannel(h, w, dstDepthType, dstPtr + ch * plane * dstElemSize);

srcChannel.convertTo(dstChannel, dstDepthType, alpha[ch], beta[ch]);
}
Expand All @@ -617,7 +618,7 @@ void install_normalize(jsi::Runtime &rt, jsi::Object &module) {

void install_applyColormap(jsi::Runtime &rt, jsi::Object &module) {
auto name = "applyColormap";
auto fnBody = [](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value {
auto fnBody = [](jsi::Runtime &rt, const jsi::Value & /*thisVal*/, const jsi::Value *args, size_t count) -> jsi::Value {
if (count != 3) {
throw jsi::JSError(rt, "Usage: applyColormap(src, dst, colormap)");
}
Expand Down Expand Up @@ -695,7 +696,7 @@ void install_applyColormap(jsi::Runtime &rt, jsi::Object &module) {
std::to_string(numColors) + ")");
}
for (size_t c = 0; c < numRgbaChannels; ++c) {
dstData[i * numRgbaChannels + c] = lut[idx][c];
dstData[i * numRgbaChannels + c] = lut[static_cast<size_t>(idx)][c];
}
}

Expand Down
Loading