[api] Remove unsafe ToLocal and ToMaybeLocal

Replace by `Utils::To(Maybe)Local` which does type inference, followed
by a cast, if needed.

R=leszeks@chromium.org
CC=jkummerow@chromium.org

Bug: 415850621
Change-Id: Ifffc2430b469fe32eaaf554d14529dd75d87cc7a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/6531243
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/main@{#100193}
diff --git a/src/api/api-inl.h b/src/api/api-inl.h
index 2a70d67..1f2597f 100644
--- a/src/api/api-inl.h
+++ b/src/api/api-inl.h
@@ -399,6 +399,11 @@
     return handle_scope_.Escape(value);
   }
 
+  template <typename T>
+  V8_INLINE MaybeLocal<T> EscapeMaybe(MaybeLocal<T> value) {
+    return handle_scope_.EscapeMaybe(value);
+  }
+
  private:
   InternalEscapableScope handle_scope_;
   CallDepthScope<false> call_depth_scope_;
diff --git a/src/api/api.cc b/src/api/api.cc
index 48dcaa9..2ac3992 100644
--- a/src/api/api.cc
+++ b/src/api/api.cc
@@ -3766,7 +3766,8 @@
   PrepareForExecutionScope api_scope{context, RCCId::kAPI_Object_ToPrimitive};
   i::Isolate* i_isolate = api_scope.i_isolate();
   return api_scope.EscapeMaybe(
-      ToMaybeLocal<Primitive>(i::Object::ToPrimitive(i_isolate, obj)));
+      Utils::ToMaybeLocal(i::Object::ToPrimitive(i_isolate, obj))
+          .As<Primitive>());
 }
 
 MaybeLocal<Numeric> Value::ToNumeric(Local<Context> context) const {
@@ -3775,7 +3776,7 @@
   PrepareForExecutionScope api_scope{context, RCCId::kAPI_Object_ToNumeric};
   i::Isolate* i_isolate = api_scope.i_isolate();
   return api_scope.EscapeMaybe(
-      ToMaybeLocal<Numeric>(i::Object::ToNumeric(i_isolate, obj)));
+      Utils::ToMaybeLocal(i::Object::ToNumeric(i_isolate, obj)).As<Numeric>());
 }
 
 Local<Boolean> Value::ToBoolean(Isolate* v8_isolate) const {
@@ -3792,7 +3793,7 @@
   PrepareForExecutionScope api_scope{context, RCCId::kAPI_Object_ToNumber};
   i::Isolate* i_isolate = api_scope.i_isolate();
   return api_scope.EscapeMaybe(
-      ToMaybeLocal<Number>(i::Object::ToNumber(i_isolate, obj)));
+      Utils::ToMaybeLocal(i::Object::ToNumber(i_isolate, obj)).As<Number>());
 }
 
 MaybeLocal<Integer> Value::ToInteger(Local<Context> context) const {
@@ -3801,7 +3802,7 @@
   PrepareForExecutionScope api_scope{context, RCCId::kAPI_Object_ToInteger};
   i::Isolate* i_isolate = api_scope.i_isolate();
   return api_scope.EscapeMaybe(
-      ToMaybeLocal<Integer>(i::Object::ToInteger(i_isolate, obj)));
+      Utils::ToMaybeLocal(i::Object::ToInteger(i_isolate, obj)).As<Integer>());
 }
 
 MaybeLocal<Int32> Value::ToInt32(Local<Context> context) const {
@@ -3810,7 +3811,7 @@
   PrepareForExecutionScope api_scope{context, RCCId::kAPI_Object_ToInt32};
   i::Isolate* i_isolate = api_scope.i_isolate();
   return api_scope.EscapeMaybe(
-      ToMaybeLocal<Int32>(i::Object::ToInt32(i_isolate, obj)));
+      Utils::ToMaybeLocal(i::Object::ToInt32(i_isolate, obj)).As<Int32>());
 }
 
 MaybeLocal<Uint32> Value::ToUint32(Local<Context> context) const {
@@ -3819,7 +3820,7 @@
   PrepareForExecutionScope api_scope{context, RCCId::kAPI_Object_ToUint32};
   i::Isolate* i_isolate = api_scope.i_isolate();
   return api_scope.EscapeMaybe(
-      ToMaybeLocal<Uint32>(i::Object::ToUint32(i_isolate, obj)));
+      Utils::ToMaybeLocal(i::Object::ToUint32(i_isolate, obj)).As<Uint32>());
 }
 
 i::Isolate* i::IsolateFromNeverReadOnlySpaceObject(i::Address obj) {
@@ -5040,10 +5041,9 @@
   i::PropertyKey lookup_key(i_isolate, key_obj);
   i::LookupIterator it(i_isolate, self, lookup_key, proto,
                        i::LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR);
-  Local<Value> result;
-  if (!ToLocal<Value>(i::Object::GetProperty(&it), &result)) return {};
+  MaybeLocal<Value> result = Utils::ToMaybeLocal(i::Object::GetProperty(&it));
   if (!it.IsFound()) return {};
-  return api_scope.Escape(result);
+  return api_scope.EscapeMaybe(result);
 }
 
 Maybe<PropertyAttribute>
@@ -5081,10 +5081,9 @@
   i::PropertyKey lookup_key(i_isolate, key_obj);
   i::LookupIterator it(i_isolate, self, lookup_key, self,
                        i::LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR);
-  Local<Value> result;
-  if (!ToLocal<Value>(i::Object::GetProperty(&it), &result)) return {};
+  MaybeLocal<Value> result = Utils::ToMaybeLocal(i::Object::GetProperty(&it));
   if (!it.IsFound()) return {};
-  return api_scope.Escape(result);
+  return api_scope.EscapeMaybe(result);
 }
 
 Maybe<PropertyAttribute> v8::Object::GetRealNamedPropertyAttributes(
@@ -5280,7 +5279,7 @@
   auto recv_obj = Utils::OpenDirectHandle(*recv);
   auto args = PrepareArguments(argc, argv);
   return api_scope.EscapeMaybe(
-      ToMaybeLocal<Value>(i::Execution::Call(i_isolate, self, recv_obj, args)));
+      Utils::ToMaybeLocal(i::Execution::Call(i_isolate, self, recv_obj, args)));
 }
 
 MaybeLocal<Value> Object::CallAsConstructor(Local<Context> context, int argc,
@@ -5295,7 +5294,7 @@
   auto self = Utils::OpenDirectHandle(this);
   auto args = PrepareArguments(argc, argv);
   return api_scope.EscapeMaybe(
-      ToMaybeLocal<Value>(i::Execution::New(i_isolate, self, self, args)));
+      Utils::ToMaybeLocal(i::Execution::New(i_isolate, self, self, args)));
 }
 
 MaybeLocal<Function> Function::New(Local<Context> context,
@@ -5345,7 +5344,7 @@
   }
   auto args = PrepareArguments(argc, argv);
   return api_scope.EscapeMaybe(
-      ToMaybeLocal<Object>(i::Execution::New(i_isolate, self, self, args)));
+      Utils::ToMaybeLocal(i::Execution::New(i_isolate, self, self, args)));
 }
 
 MaybeLocal<v8::Value> Function::Call(v8::Isolate* isolate,
@@ -5365,7 +5364,7 @@
   auto recv_obj = Utils::OpenDirectHandle(*recv);
   auto args = PrepareArguments(argc, argv);
   return api_scope.EscapeMaybe(
-      ToMaybeLocal<Value>(i::Execution::Call(i_isolate, self, recv_obj, args)));
+      Utils::ToMaybeLocal(i::Execution::Call(i_isolate, self, recv_obj, args)));
 }
 
 MaybeLocal<v8::Value> Function::Call(Local<Context> context,
@@ -7958,7 +7957,7 @@
   }
   PrepareForExecutionScope api_scope{context, RCCId::kAPI_Date_New};
   i::Isolate* i_isolate = api_scope.i_isolate();
-  return api_scope.EscapeMaybe(ToMaybeLocal<Value>(i::JSDate::New(
+  return api_scope.EscapeMaybe(Utils::ToMaybeLocal(i::JSDate::New(
       i_isolate->date_function(), i_isolate->date_function(), time)));
 }
 
@@ -7968,7 +7967,7 @@
   auto string = Utils::OpenDirectHandle(*value);
   double time = ParseDateTimeString(i_isolate, string);
 
-  return api_scope.EscapeMaybe(ToMaybeLocal<Value>(i::JSDate::New(
+  return api_scope.EscapeMaybe(Utils::ToMaybeLocal(i::JSDate::New(
       i_isolate->date_function(), i_isolate->date_function(), time)));
 }
 
@@ -8079,9 +8078,17 @@
   // mind. It fetches the 'exec' property and then calls it through JSEntry.
   // Unfortunately, this is currently the only full implementation of
   // RegExp.prototype.exec available in C++.
-  return api_scope.EscapeMaybe(ToMaybeLocal<Object>(
-      i::RegExpUtils::RegExpExec(i_isolate, regexp, subject_string,
-                                 i_isolate->factory()->undefined_value())));
+  i::DirectHandle<i::JSAny> result;
+  if (!i::RegExpUtils::RegExpExec(i_isolate, regexp, subject_string,
+                                  i_isolate->factory()->undefined_value())
+           .ToHandle(&result)) {
+    return {};
+  }
+  // RegExp::Exec is documented to return Null on failure, but the return type
+  // is `MaybeLocal<Object>` which cannot legally hold `Null`, thus the unsafe
+  // `Utils::Convert<Object>`.
+  // TODO(jgruber): Fix this.
+  return api_scope.Escape(Utils::Convert<i::JSAny, Object>(result));
 }
 
 Local<v8::Array> v8::Array::New(Isolate* v8_isolate, int length) {
@@ -8599,15 +8606,13 @@
 MaybeLocal<Promise::Resolver> Promise::Resolver::New(Local<Context> context) {
   PrepareForExecutionScope api_scope{context, RCCId::kAPI_Promise_Resolver_New};
   i::Isolate* i_isolate = api_scope.i_isolate();
-  Local<Promise::Resolver> result;
-  if (!ToLocal<Promise::Resolver>(i_isolate->factory()->NewJSPromise(),
-                                  &result) ||
-      // Also check if promise hooks set an exception.
-      // TODO(clemensb): Should `Factory::NewJSPromise()` return a MaybeHandle
-      // instead?
-      i_isolate->has_exception()) {
-    return {};
-  }
+  Local<Promise::Resolver> result =
+      Utils::ToLocal(Cast<i::JSObject>(i_isolate->factory()->NewJSPromise()))
+          .As<Promise::Resolver>();
+  // Also check if promise hooks set an exception.
+  // TODO(clemensb): Should `Factory::NewJSPromise()` return a MaybeHandle
+  // instead?
+  if (i_isolate->has_exception()) return {};
   return api_scope.Escape(result);
 }
 
diff --git a/src/api/api.h b/src/api/api.h
index c2bfb00..5d4bbd7 100644
--- a/src/api/api.h
+++ b/src/api/api.h
@@ -95,6 +95,7 @@
   V(ToLocal, Name, Name)                                                \
   V(ToLocal, String, String)                                            \
   V(ToLocal, Symbol, Symbol)                                            \
+  V(ToLocal, JSDate, Object)                                            \
   V(ToLocal, JSRegExp, RegExp)                                          \
   V(ToLocal, JSReceiver, Object)                                        \
   V(ToLocal, JSObject, Object)                                          \
@@ -312,22 +313,6 @@
   return Utils::Convert<i::Object, T>(obj);
 }
 
-// Convert MaybeDirectHandle to MaybeLocal w/o type inference or type checks.
-// To get type inference (translating from internal to API types), use
-// Utils::ToMaybeLocal.
-template <class T>
-inline MaybeLocal<T> ToMaybeLocal(i::MaybeDirectHandle<i::Object> maybe) {
-  i::DirectHandle<i::Object> handle;
-  if (maybe.ToHandle(&handle)) return Utils::Convert<i::Object, T>(handle);
-  return {};
-}
-
-// Same as above, but writes into an output variable and returns bool.
-template <class T>
-inline bool ToLocal(i::MaybeDirectHandle<i::Object> maybe, Local<T>* local) {
-  return ToMaybeLocal<T>(maybe).ToLocal(local);
-}
-
 namespace internal {
 
 class PersistentHandles;
diff --git a/src/debug/debug-interface.cc b/src/debug/debug-interface.cc
index a88704b..7e86aed 100644
--- a/src/debug/debug-interface.cc
+++ b/src/debug/debug-interface.cc
@@ -1212,15 +1212,12 @@
   if (throw_on_side_effect) {
     isolate->debug()->StartSideEffectCheckMode();
   }
-  Local<Value> result;
-  bool has_exception = !ToLocal<Value>(
-      i::Execution::Call(isolate, self, recv_obj, {arguments, args.size()}),
-      &result);
+  MaybeLocal<Value> result = Utils::ToMaybeLocal(
+      i::Execution::Call(isolate, self, recv_obj, {arguments, args.size()}));
   if (throw_on_side_effect) {
     isolate->debug()->StopSideEffectCheckMode();
   }
-  if (has_exception) return {};
-  return api_scope.Escape(result);
+  return api_scope.EscapeMaybe(result);
 }
 
 MaybeLocal<v8::Value> EvaluateGlobal(v8::Isolate* isolate,
@@ -1230,13 +1227,9 @@
   v8::Local<v8::Context> context = Utils::ToLocal(i_isolate->native_context());
   PrepareForDebugInterfaceExecutionScope api_scope(i_isolate, context);
   i::REPLMode repl_mode = repl ? i::REPLMode::kYes : i::REPLMode::kNo;
-  Local<Value> result;
-  bool has_exception = !ToLocal<Value>(
-      i::DebugEvaluate::Global(i_isolate, Utils::OpenHandle(*source), mode,
-                               repl_mode),
-      &result);
-  if (has_exception) return {};
-  return api_scope.Escape(result);
+  MaybeLocal<Value> result = Utils::ToMaybeLocal(i::DebugEvaluate::Global(
+      i_isolate, Utils::OpenHandle(*source), mode, repl_mode));
+  return api_scope.EscapeMaybe(result);
 }
 
 void GlobalLexicalScopeNames(v8::Local<v8::Context> v8_context,
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index 45ea5be..28ec5cb 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -19416,6 +19416,7 @@
     CHECK(result0->IsArray());
     v8::Local<v8::Object> result1 =
         regexp->Exec(context.local(), v8_str("abd")).ToLocalChecked();
+    // TODO(jgruber): Fix the API; `v8::Null` is not a valid `v8::Object`.
     CHECK(result1->IsNull());
   }
 }