Search code examples
c++node.jsv8

V8 updating code using deprecated v8::Local<v8::Value> v8::Object::Get(v8::Local<v8::Value>)?


I am in the process of updating a NodeJS package, due to a breakage in NodeJS 14. This library makes use of C++ code. In NodeJS 12 the same code appears as a deprecation warning:

warning: ‘v8::Local<v8::Value> v8::Object::Get(v8::Local<v8::Value>)’ is deprecated: Use maybe version

With the code in question being:

v8::Local<v8::Object> options = v8::Local<v8::Object>::Cast(info[0]);
v8::Local<v8::Value> debug = options->Get(Nan::New<v8::String>("debug").ToLocalChecked());

if (true) {
    v8::Local<v8::Value> leds = options->Get(Nan::New<v8::String>("leds").ToLocalChecked());

    if (!leds->IsUndefined())
        ws2811.channel[0].count = Nan::To<int>(leds).FromMaybe(ws2811.channel[0].count);
    else
        return Nan::ThrowTypeError("configure(): leds must be defined");
}

I did try the following and while it does compile, runtime suggests this may be wrong, since I get failure which didn't exist before this code change:

v8::Local<v8::Object> options = v8::Local<v8::Object>::Cast(info[0]);
Nan::MaybeLocal<v8::Value> debug = Nan::Get(options, Nan::New<v8::String>("debug").ToLocalChecked());

if (true) {
    Nan::MaybeLocal<v8::Value> maybe_leds = Nan::Get(options, Nan::New<v8::String>("leds").ToLocalChecked());
    v8::Local<v8::Value> leds;

    if (!maybe_leds.IsEmpty() && maybe_leds.ToLocal(&leds))
        ws2811.channel[0].count = Nan::To<int>(leds).FromMaybe(ws2811.channel[0].count);
    else
        return Nan::ThrowTypeError("configure(): leds must be defined");
}

Being pretty rusty with C++ and new to V8, I am a little confused as to what the right replacement for the Get method is, in this context. What I do think I understand is that we need to use MaybeLocal instead of Local. Doing a search turns up a lot of other people with similar issues, but nothing that I can use as a solution.

BTW this project does depend on nan.


Solution

  • The key insight is that most operations that involve JavaScript can throw an exception instead of returning a value. The MaybeLocal convention makes this explicit: a MaybeLocal is either a Local (if the function/operation returned a value), or empty (if there was an exception). If you have a v8::TryCatch or Nan::TryCatch, it will have caught the exception in the latter case.

    There are several ways for embedding code to deal with MaybeLocals; the most elegant is the bool-returning .ToLocal(...) method. It is equivalent to checking .IsEmpty(), so you don't need to do both.

    So that would give you:

    Nan::TryCatch try_catch;
    v8::Local<v8::String> leds_string = Nan::New<v8::String>("leds").ToLocalChecked();
    Nan::MaybeLocal<v8::Value> maybe_leds = Nan::Get(options, leds_string);
    
    v8::Local<v8::Value> leds;
    if (!maybe_leds.ToLocal(&leds)) {
      // An exception was thrown while reading `options["leds"]`.
      // This is the same as `maybe_leds.IsEmpty() == true`.
      // It is also the same as `try_catch.HasCaught() == true`.
      return try_catch.ReThrow();
    }
    // Now follows your original code.
    if (leds->IsUndefined()) {
      // The `options` object didn't have a `leds` property, or it was undefined.
      return Nan::ThrowTypeError("configure(): leds must be defined");
    }
    
    // Success case: all good.
    ws2811.channel[0].count = Nan::To<int>(leds).FromMaybe(ws2811.channel[0].count);
    

    See documentation at https://github.com/nodejs/nan/blob/master/doc/maybe_types.md.