Cannot convert 'v8::MaybeLocal<v8::String>' to 'v8::Local<v8::Value>'

Viewed 1674

I'm trying to upgrade a working Electron app from 5 to 6 which has introduced a breaking change...

error C2664: 'v8::MaybeLocal<v8::Value> v8::Object::Get(v8::Local<v8::Context>,uint32_t)': cannot convert argument 1 from 'v8::MaybeLocal<v8::String>' to 'v8::Local<v8::Value>'

...compiling the following code.

  Isolate* isolate = info.GetIsolate();

  Local<Object> group = Local<Object>::Cast(info[0]);
  Local<Array> A = Local<Array>::Cast(group->Get(String::NewFromUtf8(isolate,"A"))); <<-- Error here
  Local<Array> B = Local<Array>::Cast(group->Get(String::NewFromUtf8(isolate,"B"))); << Same error here

Erroring lines are e.g. 113/114 in this file if more context would help

I've found some suggestions, e.g. that probably come close but C++ is totally alien to me and I've spent too long trying to do this already. I expect the fix will be really easy to someone who knows C++ and it'll be much appreciated!

2 Answers

Got it. Suffix each with .ToLocalChecked()

e.g. Local<Array> A = Local<Array>::Cast(group->Get(String::NewFromUtf8(isolate,"A").ToLocalChecked()));

(V8 developer here.)

The background is that MaybeLocals are returned by functions that can fail, in particular: that can throw an exception. That includes pretty much everything that involves JavaScript execution, including setting or reading properties to/from JS objects (getters, setters, Proxies, read-only properties/objects can all throw), most conversions (anything involving .valueOf or .toString, also BigInt-to-Number or Symbol-to-String or vice versa, ...), etc.

The idea behind MaybeLocal is to make this explicit, so as an embedder of V8 you can be sure that your code is robust towards such JavaScript shenanigans. Generally, you should check whether a MaybeLocal IsEmpty(), and if so, handle that error case. As a convenience, there's the conditional conversion to Local:

Local<Value> value;
MaybeLocal<Value> maybe_value = FunctionThatCanThrowAnException();
if (!maybe_value.ToLocal(&value)) {
  // No value was returned. If you have a `v8::TryCatch`, it should have
  // caught an exception. Handle that somehow, and return early.
}
// Otherwise: everything's good, continue with `value`.

If you simply use .ToLocalChecked(), you're saying: assume that this MaybeLocal is, in fact, a Local, and crash otherwise. That may be appropriate (when you can guarantee that a particular operation couldn't possibly have thrown an exception), but if you just slap these on everywhere, chances are you'll see crashes when something throws that you didn't anticipate.

Allocating a new short string such as "A" is going to be fine (the only way it can fail is OOM, in which case the whole process is killed anyway); but as soon as your input string is user-controlled (and hence could be arbitrarily large), the allocation attempt could throw a RangeError.

Related