Skip to content

Commit 08cf445

Browse files
authored
[RNE Rewrite] style(cpp): improve error messages in core and extensions (#1339)
## Description This PR improves the quality of native C++ error messages thrown to JavaScript, making it easier to diagnose issues without needing to read source code. This is a follow-up to error messages improvements introduced in #1327. ### Introduces a breaking change? - [ ] Yes - [x] No ### Type of change - [ ] Bug fix (change which fixes an issue) - [ ] New feature (change which adds functionality) - [ ] Documentation update (improves or adds clarity to existing documentation) - [x] Other (chores, tests, code style improvements etc.) ### Tested on - [ ] iOS - [ ] Android ### Testing instructions N/A ### Screenshots <!-- Add screenshots here, if applicable --> ### Related issues <!-- Link related issues here using #issue-number --> ### Checklist - [ ] I have performed a self-review of my code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have updated the documentation accordingly - [ ] My changes generate no new warnings ### Additional notes <!-- Include any additional information, assumptions, or context that reviewers might need to understand this PR. -->
1 parent 8804d56 commit 08cf445

7 files changed

Lines changed: 47 additions & 25 deletions

File tree

packages/react-native-executorch/cpp/core/conversions.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ std::vector<T> fromJsiTypedArray(jsi::Runtime &rt, const std::string &ctx, const
138138
const size_t byteLength = getOptionalProperty<uint64_t>(rt, ctx, obj, "byteLength").value_or(buffer.size(rt));
139139

140140
if (byteOffset > buffer.size(rt) || byteLength > buffer.size(rt) - byteOffset) {
141-
throw jsi::JSError(rt, ctx + " has out-of-bounds byteOffset/byteLength for its ArrayBuffer");
141+
throw jsi::JSError(rt, std::format("{}: out-of-bounds byteOffset ({}) or byteLength ({}) for ArrayBuffer of size {}",
142+
ctx, byteOffset, byteLength, buffer.size(rt)));
142143
}
143144
if (byteLength % sizeof(T) != 0) {
144145
throw jsi::JSError(rt, std::format("{}: byteLength is not a multiple of sizeof(T)={}", ctx, sizeof(T)));
@@ -149,6 +150,9 @@ std::vector<T> fromJsiTypedArray(jsi::Runtime &rt, const std::string &ctx, const
149150
return vec;
150151
}
151152

153+
template <typename>
154+
inline constexpr bool kAlwaysFalse = false;
155+
152156
/**
153157
* Converts a std::vector of values to a new facebook::jsi::Array.
154158
* Handles strings, booleans, and numeric types appropriately.
@@ -165,17 +169,16 @@ jsi::Array toJsiArray(jsi::Runtime &rt, const std::vector<T> &vec) {
165169
if constexpr (std::is_same_v<T, std::string>) {
166170
arr.setValueAtIndex(rt, i, jsi::String::createFromUtf8(rt, vec[i]));
167171
} else if constexpr (std::is_same_v<T, bool>) {
168-
arr.setValueAtIndex(rt, i, jsi::Value(vec[i]));
169-
} else {
172+
arr.setValueAtIndex(rt, i, jsi::Value(static_cast<bool>(vec[i])));
173+
} else if constexpr (std::is_arithmetic_v<T>) {
170174
arr.setValueAtIndex(rt, i, jsi::Value(static_cast<double>(vec[i])));
175+
} else {
176+
static_assert(kAlwaysFalse<T>, "Unsupported vector element type for toJsiArray");
171177
}
172178
}
173179
return arr;
174180
}
175181

176-
template <typename>
177-
inline constexpr bool kAlwaysFalse = false;
178-
179182
/**
180183
* Maps an arithmetic C++ type to the name of the JS TypedArray constructor whose
181184
* elements have the same layout (e.g. int32_t -> "Int32Array"). 64-bit integers

packages/react-native-executorch/cpp/core/tensor.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,20 +64,24 @@ jsi::Value TensorHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &name)
6464
if (count == 2) {
6565
optsObj = conversions::asType<jsi::Object>(rt, "copyTo: options", args[1]);
6666
}
67+
6768
size_t offset = getOptionalProperty<uint64_t>(rt, "copyTo: options", optsObj, "offset").value_or(0);
6869
if (offset > self->numel_) {
69-
throw jsi::JSError(rt, "copyTo: offset is out of bounds for src tensor");
70+
throw jsi::JSError(rt, std::format("copyTo: offset {} is out of bounds for src tensor of size {} elements",
71+
offset, self->numel_));
7072
}
7173

7274
size_t length = getOptionalProperty<uint64_t>(rt, "copyTo: options", optsObj, "length").value_or(self->numel_ - offset);
7375
if (length > self->numel_ - offset) {
74-
throw jsi::JSError(rt, "copyTo: length is out of bounds for the given offset of the src tensor");
76+
throw jsi::JSError(rt, std::format("copyTo: length {} is out of bounds for offset {} of src tensor (numel {})",
77+
length, offset, self->numel_));
7578
}
7679

7780
const auto elemSize = types::elementSize(self->dtype_);
7881

7982
if (length * elemSize != dst->size_) {
80-
throw jsi::JSError(rt, "copyTo: size mismatch between copy byte size and dst tensor size");
83+
throw jsi::JSError(rt, std::format("copyTo: size mismatch between copy size ({} bytes) and dst tensor size ({} bytes)",
84+
length * elemSize, dst->size_));
8185
}
8286

8387
std::memcpy(dst->data_.get(), self->data_.get() + (offset * elemSize), length * elemSize);
@@ -102,7 +106,8 @@ jsi::Value TensorHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &name)
102106
auto lock = tryLockUnique(rt, "setData: self", self);
103107

104108
if (byteOffset > buffer.size(rt) || byteLength > buffer.size(rt) - byteOffset) {
105-
throw jsi::JSError(rt, "setData: Out of bounds offset/length for buffer");
109+
throw jsi::JSError(rt, std::format("setData: Out of bounds offset ({}) or length ({}) for buffer of size {}",
110+
byteOffset, byteLength, buffer.size(rt)));
106111
}
107112

108113
if (byteLength != self->size_) {
@@ -132,7 +137,8 @@ jsi::Value TensorHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &name)
132137
auto lock = tryLockShared(rt, "getData: self", self);
133138

134139
if (byteOffset > buffer.size(rt) || byteLength > buffer.size(rt) - byteOffset) {
135-
throw jsi::JSError(rt, "getData: Out of bounds offset/length for buffer");
140+
throw jsi::JSError(rt, std::format("getData: Out of bounds offset ({}) or length ({}) for buffer of size {}",
141+
byteOffset, byteLength, buffer.size(rt)));
136142
}
137143

138144
if (byteLength != self->size_) {

packages/react-native-executorch/cpp/extensions/cv/box_ops.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ BoxFormat parseBoxFormat(const std::string &s) {
4444
if (s == "cxcywh") {
4545
return BoxFormat::CXCYWH;
4646
}
47-
throw std::invalid_argument("unsupported boxFormat '" + s + "'");
47+
throw std::invalid_argument(std::format("unsupported boxFormat '{}'. Expected 'xyxy', 'xywh', or 'cxcywh'", s));
4848
}
4949

5050
enum class NmsType {
@@ -59,7 +59,7 @@ NmsType parseNmsType(const std::string &s) {
5959
if (s == "weighted") {
6060
return NmsType::Weighted;
6161
}
62-
throw std::invalid_argument("unsupported nmsType '" + s + "'");
62+
throw std::invalid_argument(std::format("unsupported nmsType '{}'. Expected 'standard' or 'weighted'", s));
6363
}
6464

6565
constexpr size_t kBoxCoords = 4;
@@ -223,7 +223,8 @@ void install_restrictToBox(jsi::Runtime &rt, jsi::Object &module) {
223223

224224
auto boxVec = conversions::asVector<float>(rt, "restrictToBox: boxTuple", args[2]);
225225
if (boxVec.size() != kBoxCoords) {
226-
throw jsi::JSError(rt, "restrictToBox: boxTuple must contain exactly 4 coordinates");
226+
throw jsi::JSError(rt, std::format("restrictToBox: boxTuple must contain exactly 4 coordinates (got {})",
227+
boxVec.size()));
227228
}
228229

229230
auto boxFormatStr = conversions::asType<std::string>(rt, "restrictToBox: format", args[3]);

packages/react-native-executorch/cpp/extensions/cv/image_ops.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ int interpToFlag(const std::string &interp) {
4141
if (interp == "lanczos") {
4242
return ::cv::INTER_LANCZOS4;
4343
}
44-
throw std::invalid_argument("unsupported interpolation '" + interp + "'");
44+
throw std::invalid_argument(std::format("unsupported interpolation '{}'. Expected"
45+
" 'nearest', 'area', 'linear', 'cubic', or 'lanczos'",
46+
interp));
4547
}
4648

4749
struct FitBox {
@@ -189,7 +191,9 @@ int codeToColorConversionFlag(const std::string &code) {
189191
if (code == "GRAY2BGRA") {
190192
return ::cv::COLOR_GRAY2BGRA;
191193
}
192-
throw std::invalid_argument("cvtColor: unsupported color conversion code '" + code + "'");
194+
throw std::invalid_argument(std::format("cvtColor: unsupported color conversion code '{}'."
195+
" Common values are 'RGB2BGR', 'BGR2RGB', 'RGBA2RGB', 'RGB2GRAY', etc.",
196+
code));
193197
}
194198
} // namespace
195199

@@ -414,7 +418,8 @@ void install_applyColormap(jsi::Runtime &rt, jsi::Object &module) {
414418
for (size_t i = 0; i < numColors; ++i) {
415419
auto colorVec = conversions::asVector<uint8_t>(rt, "applyColormap: colormap entry", colormapArray.getValueAtIndex(rt, i));
416420
if (colorVec.size() != numRgbaChannels) {
417-
throw jsi::JSError(rt, "applyColormap: colormap entry must be an RGBA color array of size 4");
421+
throw jsi::JSError(rt, std::format("applyColormap: colormap entry must be an RGBA color array of size 4 (got size {})",
422+
colorVec.size()));
418423
}
419424
for (size_t c = 0; c < numRgbaChannels; ++c) {
420425
lut[i][c] = colorVec[c];

packages/react-native-executorch/cpp/extensions/math/operations.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ void install_softmax(jsi::Runtime &rt, jsi::Object &module) {
7373
axis += rank;
7474
}
7575
if (axis < 0 || axis >= rank) {
76-
throw jsi::JSError(rt, "softmax: axis is out of range");
76+
throw jsi::JSError(rt, std::format("softmax: axis {} out of range for tensor of rank {}",
77+
axis, rank));
7778
}
7879
const auto axisIdx = static_cast<size_t>(axis);
7980

@@ -152,7 +153,8 @@ void install_argmax(jsi::Runtime &rt, jsi::Object &module) {
152153
axis += rank;
153154
}
154155
if (axis < 0 || axis >= rank) {
155-
throw jsi::JSError(rt, "argmax: axis is out of range");
156+
throw jsi::JSError(rt, std::format("argmax: axis {} out of range for tensor of rank {}",
157+
axis, rank));
156158
}
157159
const auto axisIdx = static_cast<size_t>(axis);
158160

packages/react-native-executorch/cpp/extensions/nlp/tokenizer.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,13 @@ TokenizerHostObject::TokenizerHostObject(std::string tokenizerPath)
6262
tokenizer_(std::make_unique<tokenizers::HFTokenizer>()) {
6363
auto error = tokenizer_->load(tokenizerPath_);
6464
if (error != tokenizers::Error::Ok) {
65-
throw std::runtime_error("Failed to load tokenizer from '" + tokenizerPath_ +
66-
"': " + toString(error));
65+
throw std::runtime_error(std::format("Failed to load tokenizer from '{}': {}",
66+
tokenizerPath_, toString(error)));
6767
}
6868
}
6969

7070
std::unique_lock<std::mutex> TokenizerHostObject::tryLockUnique(jsi::Runtime &rt,
71-
std::string_view context) {
71+
std::string_view context) {
7272
std::unique_lock<std::mutex> lock(mutex_, std::try_to_lock);
7373
if (!lock.owns_lock()) {
7474
throw jsi::JSError(rt, std::format("{} is currently in use", context));

packages/react-native-executorch/cpp/extensions/speech/operations.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <algorithm>
44
#include <cstddef>
55
#include <cstdint>
6+
#include <format>
67
#include <span>
78

89
#include "core/tensor.h"
@@ -41,16 +42,20 @@ void install_extractFrames(jsi::Runtime &rt, jsi::Object &module) {
4142
const auto chunkFrames = static_cast<uint64_t>(dst->shape_[0]);
4243
const auto fftLength = static_cast<uint64_t>(dst->shape_[1]);
4344
if (frameLength > fftLength) {
44-
throw jsi::JSError(rt, "extractFrames: hann length exceeds dst fftLength");
45+
throw jsi::JSError(rt, std::format("extractFrames: hann length ({}) exceeds dst fftLength ({})",
46+
frameLength, fftLength));
4547
}
4648
if (numFrames > chunkFrames) {
47-
throw jsi::JSError(rt, "extractFrames: numFrames out of dst frame capacity");
49+
throw jsi::JSError(rt, std::format("extractFrames: numFrames ({}) exceeds dst frame capacity ({})",
50+
numFrames, chunkFrames));
4851
}
4952

5053
if (numFrames > 0) {
5154
const uint64_t lastSample = (numFrames - 1) * hopLength + frameLength - 1;
5255
if (lastSample >= waveform->numel_) {
53-
throw jsi::JSError(rt, "extractFrames: frame window out of waveform bounds");
56+
throw jsi::JSError(rt, std::format("extractFrames: frame window (last sample index {})"
57+
" exceeds waveform bounds (numel {})",
58+
lastSample, waveform->numel_));
5459
}
5560
}
5661

0 commit comments

Comments
 (0)