Skip to content

Commit f6e341a

Browse files
committed
src: improve ToV8Value() functions
- Cache the `isolate` value between calls - Introduce an overload for dealing with integers/numbers - Use the vectored `v8::Array::New` constructor + `MaybeStackBuffer` for faster array creation PR-URL: #25288 Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent b9b2ba2 commit f6e341a

File tree

2 files changed

+53
-17
lines changed

2 files changed

+53
-17
lines changed

src/util-inl.h

+41-14
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,9 @@ inline char* UncheckedCalloc(size_t n) { return UncheckedCalloc<char>(n); }
377377
void ThrowErrStringTooLong(v8::Isolate* isolate);
378378

379379
v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context,
380-
const std::string& str) {
381-
v8::Isolate* isolate = context->GetIsolate();
380+
const std::string& str,
381+
v8::Isolate* isolate) {
382+
if (isolate == nullptr) isolate = context->GetIsolate();
382383
if (UNLIKELY(str.size() >= static_cast<size_t>(v8::String::kMaxLength))) {
383384
// V8 only has a TODO comment about adding an exception when the maximum
384385
// string size is exceeded.
@@ -393,33 +394,33 @@ v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context,
393394

394395
template <typename T>
395396
v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context,
396-
const std::vector<T>& vec) {
397-
v8::Isolate* isolate = context->GetIsolate();
397+
const std::vector<T>& vec,
398+
v8::Isolate* isolate) {
399+
if (isolate == nullptr) isolate = context->GetIsolate();
398400
v8::EscapableHandleScope handle_scope(isolate);
399401

400-
v8::Local<v8::Array> arr = v8::Array::New(isolate, vec.size());
402+
MaybeStackBuffer<v8::Local<v8::Value>, 128> arr(vec.size());
403+
arr.SetLength(vec.size());
401404
for (size_t i = 0; i < vec.size(); ++i) {
402-
v8::Local<v8::Value> val;
403-
if (!ToV8Value(context, vec[i]).ToLocal(&val) ||
404-
arr->Set(context, i, val).IsNothing()) {
405+
if (!ToV8Value(context, vec[i], isolate).ToLocal(&arr[i]))
405406
return v8::MaybeLocal<v8::Value>();
406-
}
407407
}
408408

409-
return handle_scope.Escape(arr);
409+
return handle_scope.Escape(v8::Array::New(isolate, arr.out(), arr.length()));
410410
}
411411

412412
template <typename T, typename U>
413413
v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context,
414-
const std::unordered_map<T, U>& map) {
415-
v8::Isolate* isolate = context->GetIsolate();
414+
const std::unordered_map<T, U>& map,
415+
v8::Isolate* isolate) {
416+
if (isolate == nullptr) isolate = context->GetIsolate();
416417
v8::EscapableHandleScope handle_scope(isolate);
417418

418419
v8::Local<v8::Map> ret = v8::Map::New(isolate);
419420
for (const auto& item : map) {
420421
v8::Local<v8::Value> first, second;
421-
if (!ToV8Value(context, item.first).ToLocal(&first) ||
422-
!ToV8Value(context, item.second).ToLocal(&second) ||
422+
if (!ToV8Value(context, item.first, isolate).ToLocal(&first) ||
423+
!ToV8Value(context, item.second, isolate).ToLocal(&second) ||
423424
ret->Set(context, first, second).IsEmpty()) {
424425
return v8::MaybeLocal<v8::Value>();
425426
}
@@ -428,6 +429,32 @@ v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context,
428429
return handle_scope.Escape(ret);
429430
}
430431

432+
template <typename T, typename >
433+
v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context,
434+
const T& number,
435+
v8::Isolate* isolate) {
436+
if (isolate == nullptr) isolate = context->GetIsolate();
437+
438+
using Limits = std::numeric_limits<T>;
439+
// Choose Uint32, Int32, or Double depending on range checks.
440+
// These checks should all collapse at compile time.
441+
if (static_cast<uint32_t>(Limits::max()) <=
442+
std::numeric_limits<uint32_t>::max() &&
443+
static_cast<uint32_t>(Limits::min()) >=
444+
std::numeric_limits<uint32_t>::min() && Limits::is_exact) {
445+
return v8::Integer::NewFromUnsigned(isolate, static_cast<uint32_t>(number));
446+
}
447+
448+
if (static_cast<int32_t>(Limits::max()) <=
449+
std::numeric_limits<int32_t>::max() &&
450+
static_cast<int32_t>(Limits::min()) >=
451+
std::numeric_limits<int32_t>::min() && Limits::is_exact) {
452+
return v8::Integer::New(isolate, static_cast<int32_t>(number));
453+
}
454+
455+
return v8::Number::New(isolate, static_cast<double>(number));
456+
}
457+
431458
} // namespace node
432459

433460
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

src/util.h

+12-3
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include <string.h>
3636

3737
#include <functional> // std::function
38+
#include <limits>
3839
#include <set>
3940
#include <string>
4041
#include <array>
@@ -519,13 +520,21 @@ using DeleteFnPtr = typename FunctionDeleter<T, function>::Pointer;
519520
std::set<std::string> ParseCommaSeparatedSet(const std::string& in);
520521

521522
inline v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context,
522-
const std::string& str);
523+
const std::string& str,
524+
v8::Isolate* isolate = nullptr);
525+
template <typename T, typename test_for_number =
526+
typename std::enable_if<std::numeric_limits<T>::is_specialized, bool>::type>
527+
inline v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context,
528+
const T& number,
529+
v8::Isolate* isolate = nullptr);
523530
template <typename T>
524531
inline v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context,
525-
const std::vector<T>& vec);
532+
const std::vector<T>& vec,
533+
v8::Isolate* isolate = nullptr);
526534
template <typename T, typename U>
527535
inline v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context,
528-
const std::unordered_map<T, U>& map);
536+
const std::unordered_map<T, U>& map,
537+
v8::Isolate* isolate = nullptr);
529538

530539
// These macros expects a `Isolate* isolate` and a `Local<Context> context`
531540
// to be in the scope.

0 commit comments

Comments
 (0)