Skip to content

Conversation

@agracio
Copy link
Contributor

@agracio agracio commented Dec 2, 2025

Nan::Encode and Nan:TryEncode() changes

  • Added new Nan:TryEncode() method for Node version 24 and higher
  • Added deprecation warning to Nan::Encode for Node version 24 and higher
  • Changed string_bytes.md to reflect changes and rebuilt README

Miscellaneous

  • Added missing NODE_MODULE_VERSION definition for all Node version to maintain consistency
  • Changed macos-13 to macos-15-intel to avoid deprecation

AppVeyor tests for older versions of nan that are currently disabled in this repo (https://ci.appveyor.com/project/agracio/nan)

@kkoopa
Copy link
Collaborator

kkoopa commented Dec 2, 2025

I took a quick check and it looks alright, this is fine to merge as-is. However, I had a thought. Could not the old functions relying on the deprecated API be rewritten to use the new TryEncode where available, just by tacking on a ToLocalChecked() to get the old behavior? That way all deprecation warnings could be avoided. Like you pointed out, there does not seem to be a way of deprecating the old Nan::Encode functions, because of compatibility.

@agracio
Copy link
Contributor Author

agracio commented Dec 2, 2025

I think only Node version 24+ can use it but yes I could rewrite it to use TryEncode() only for those versions. Not sure what you mean by suggesting to use ToLocalChecked() since TryEncode() returns the same type of variable unless I misunderstand what you trying to do.

@kkoopa
Copy link
Collaborator

kkoopa commented Dec 2, 2025 via email

@agracio
Copy link
Contributor Author

agracio commented Dec 2, 2025

I made a mistake in code Nan::TryEncode() should return MaybeLocal<v8::Value>.

@agracio
Copy link
Contributor Author

agracio commented Dec 2, 2025

Question - since it's a direct call to node::TryEncode() should it return v8::MaybeLocal<> instead of Nan::MaybeLocal<>

@kkoopa
Copy link
Collaborator

kkoopa commented Dec 2, 2025 via email

@agracio
Copy link
Contributor Author

agracio commented Dec 2, 2025

Have another question and also want to make clear that C++ is not really a language I am too familiar with.

I was not able to reuse or call TryEncode() method that I have added in Encode() and ended up with duplicated code.
Currently it looks like this and I am still not sure if it's completely correct.

#if NODE_MAJOR_VERSION >= 24
inline MaybeLocal<v8::Value> TryEncode(
    const char *buf, size_t len, enum Encoding encoding = BINARY) {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  node::encoding node_enc = static_cast<node::encoding>(encoding);

  if (encoding == UCS2) {
    return node::TryEncode(
        isolate
      , reinterpret_cast<const uint16_t *>(buf)
      , len / 2);
  } else {
    return node::TryEncode(
        isolate
      , reinterpret_cast<const char *>(buf)
      , len
      , node_enc);
  }
}

NAN_DEPRECATED inline v8::Local<v8::Value> Encode(
    const void *buf, size_t len, enum Encoding encoding = BINARY){
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  node::encoding node_enc = static_cast<node::encoding>(encoding);

  if (encoding == UCS2) {
    return node::TryEncode(
        isolate
      , reinterpret_cast<const uint16_t *>(buf)
      , len / 2).ToLocalChecked();
  } else {
    return node::TryEncode(
        isolate
      , reinterpret_cast<const char *>(buf)
      , len
      , node_enc).ToLocalChecked();
  }
}

#else
...
#endif

What am I missing?

@kkoopa
Copy link
Collaborator

kkoopa commented Dec 2, 2025

Your TryEncode takes a char *, the other takes a void *. In C++, unlike C, void pointers are not implicitly convertible to other pointers, so an explicit cast is required. I got it to compile like this:

diff --git a/nan.h b/nan.h
index fdfce0a..8d2948d 100644
--- a/nan.h
+++ b/nan.h
@@ -2420,7 +2420,7 @@ enum Encoding {ASCII, UTF8, BASE64, UCS2, BINARY, HEX, BUFFER};
 #endif
 
 #if NODE_MAJOR_VERSION >= 24
-inline v8::Local<v8::Value> TryEncode(
+inline v8::MaybeLocal<v8::Value> TryEncode(
     const char *buf, size_t len, enum Encoding encoding = BINARY) {
   v8::Isolate* isolate = v8::Isolate::GetCurrent();
   node::encoding node_enc = static_cast<node::encoding>(encoding);
@@ -2429,13 +2429,13 @@ inline v8::Local<v8::Value> TryEncode(
     return node::TryEncode(
         isolate
       , reinterpret_cast<const uint16_t *>(buf)
-      , len / 2).FromMaybe(v8::Local<v8::Value>());
+      , len / 2);
   } else {
     return node::TryEncode(
         isolate
       , reinterpret_cast<const char *>(buf)
       , len
-      , node_enc).FromMaybe(v8::Local<v8::Value>());
+      , node_enc);
   }
 }
 #endif
@@ -2449,21 +2449,7 @@ inline v8::Local<v8::Value> Encode(
 #endif
 {
 #if (NODE_MODULE_VERSION >= ATOM_0_21_MODULE_VERSION)
-  v8::Isolate* isolate = v8::Isolate::GetCurrent();
-  node::encoding node_enc = static_cast<node::encoding>(encoding);
-
-  if (encoding == UCS2) {
-    return node::Encode(
-        isolate
-      , reinterpret_cast<const uint16_t *>(buf)
-      , len / 2);
-  } else {
-    return node::Encode(
-        isolate
-      , reinterpret_cast<const char *>(buf)
-      , len
-      , node_enc);
-  }
+  return Nan::TryEncode(static_cast<const char *>(buf), len, encoding).ToLocalChecked();
 #elif (NODE_MODULE_VERSION > NODE_0_10_MODULE_VERSION)
   return node::Encode(
       v8::Isolate::GetCurrent()

@kkoopa
Copy link
Collaborator

kkoopa commented Dec 2, 2025

I did not do the necessary ifdeffing, so it is obviously not right, but it compiles.

@agracio
Copy link
Contributor Author

agracio commented Dec 2, 2025

I do not know how I missed that and actually no idea where void came from in the first place. I copy/pasted Encode() and changed it to TryEncode(). 🤦‍♂️ 🤦‍♂️🤦‍♂️

Thank you!

@kkoopa
Copy link
Collaborator

kkoopa commented Dec 2, 2025

I was wondering about the origin of the void pointer myself. The old node API also uses char pointers in the latest version. Either it used to be a void pointer a long time ago, or I changed it to a void pointer, because the data is not necessarily a char array at all (e.g. utf-16) making a char pointer semantically wrong.

@agracio
Copy link
Contributor Author

agracio commented Dec 2, 2025

In my code I marked Encode() as NAN_DEPRECATED for Node >=24 to make users aware of deprecation, is it not something you want to implement?

@agracio
Copy link
Contributor Author

agracio commented Dec 2, 2025

I was wondering about the origin of the void pointer myself. The old node API also uses char pointers in the latest version. Either it used to be a void pointer a long time ago, or I changed it to a void pointer, because the data is not necessarily a char array at all (e.g. utf-16) making a char pointer semantically wrong.

Your second assumption is correct, tests do not build with a non void parameter.

@kkoopa
Copy link
Collaborator

kkoopa commented Dec 2, 2025 via email

@agracio
Copy link
Contributor Author

agracio commented Dec 2, 2025

I can do it either way, the only issue I see is that if it is not marked as deprecated and Node native warning is suppressed many users will not even be aware of the change and that they might want to look at it. Although flooding some users with warning is also not ideal.

@agracio
Copy link
Contributor Author

agracio commented Dec 2, 2025

Changes complete

@kkoopa kkoopa merged commit fd5ff3f into nodejs:main Dec 3, 2025
17 checks passed
@kkoopa
Copy link
Collaborator

kkoopa commented Dec 3, 2025

Wonderful. Thank you very much for your effort. I will push a new release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants