Introduce MaybeHandle to police exception checking in handlified code.
authoryangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 3 Apr 2014 05:57:43 +0000 (05:57 +0000)
committeryangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 3 Apr 2014 05:57:43 +0000 (05:57 +0000)
R=ishell@chromium.org

Review URL: https://codereview.chromium.org/222163002

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@20457 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/handles.h
src/isolate.h
src/objects.cc
src/objects.h
src/runtime.cc
src/v8globals.h

index bcd7eb23744fa2c631ffa1c43a58342f93604dc4..77e5e528da6e2fa04ea7386fe6ff00bd79394401 100644 (file)
 namespace v8 {
 namespace internal {
 
+// A Handle can be converted into a MaybeHandle. Converting a MaybeHandle
+// into a Handle requires checking that it does not point to NULL.  This
+// ensures NULL checks before use.
+
+template<typename T>
+class MaybeHandle {
+ public:
+  INLINE(MaybeHandle()) : location_(NULL) { }
+
+  // Constructor for handling automatic up casting from Handle.
+  // Ex. Handle<JSArray> can be passed when MaybeHandle<Object> is expected.
+  template <class S> MaybeHandle(Handle<S> handle) {
+#ifdef DEBUG
+    T* a = NULL;
+    S* b = NULL;
+    a = b;  // Fake assignment to enforce type checks.
+    USE(a);
+#endif
+    this->location_ = reinterpret_cast<T**>(handle.location());
+  }
+
+  // Constructor for handling automatic up casting.
+  // Ex. MaybeHandle<JSArray> can be passed when Handle<Object> is expected.
+  template <class S> MaybeHandle(MaybeHandle<S> maybe_handle) {
+#ifdef DEBUG
+    T* a = NULL;
+    S* b = NULL;
+    a = b;  // Fake assignment to enforce type checks.
+    USE(a);
+#endif
+    location_ = reinterpret_cast<T**>(maybe_handle.location_);
+  }
+
+  INLINE(Handle<T> ToHandleChecked()) {
+    CHECK(location_ != NULL);
+    return Handle<T>(location_);
+  }
+
+  INLINE(bool ToHandle(Handle<T>* out)) {
+    if (location_ == NULL) {
+      *out = Handle<T>::null();
+      return false;
+    } else {
+      *out = Handle<T>(location_);
+      return true;
+    }
+  }
+
+ protected:
+  T** location_;
+
+  // MaybeHandles of different classes are allowed to access each
+  // other's location_.
+  template<class S> friend class MaybeHandle;
+};
+
 // ----------------------------------------------------------------------------
 // A Handle provides a reference to an object that survives relocation by
 // the garbage collector.
@@ -47,7 +103,9 @@ class Handle {
   INLINE(explicit Handle(T* obj));
   INLINE(Handle(T* obj, Isolate* isolate));
 
-  INLINE(Handle()) : location_(NULL) {}
+  // TODO(yangguo): Values that contain empty handles should be declared as
+  // MaybeHandle to force validation before being used as handles.
+  INLINE(Handle()) : location_(NULL) { }
 
   // Constructor for handling automatic up casting.
   // Ex. Handle<JSFunction> can be passed when Handle<Object> is expected.
@@ -77,6 +135,8 @@ class Handle {
     return Handle<T>(reinterpret_cast<T**>(that.location_));
   }
 
+  // TODO(yangguo): Values that contain empty handles should be declared as
+  // MaybeHandle to force validation before being used as handles.
   static Handle<T> null() { return Handle<T>(); }
   bool is_null() const { return location_ == NULL; }
 
index b4713786abb0feaedf6598d6686694fcb579c243..2d7995ca08e253c2574f4e9b273d32a7e20f5be6 100644 (file)
@@ -134,12 +134,12 @@ typedef ZoneList<Handle<Object> > ZoneObjectList;
     }                                                     \
   } while (false)
 
-#define RETURN_IF_EMPTY_HANDLE_VALUE(isolate, call, value) \
-  do {                                                     \
-    if ((call).is_null()) {                                \
-      ASSERT((isolate)->has_pending_exception());          \
-      return (value);                                      \
-    }                                                      \
+#define RETURN_IF_EMPTY_HANDLE_VALUE(isolate, call, value)  \
+  do {                                                      \
+    if ((call).is_null()) {                                 \
+      ASSERT((isolate)->has_pending_exception());           \
+      return (value);                                       \
+    }                                                       \
   } while (false)
 
 #define CHECK_NOT_EMPTY_HANDLE(isolate, call)     \
@@ -148,9 +148,51 @@ typedef ZoneList<Handle<Object> > ZoneObjectList;
     CHECK(!(call).is_null());                     \
   } while (false)
 
-#define RETURN_IF_EMPTY_HANDLE(isolate, call)                       \
+#define RETURN_IF_EMPTY_HANDLE(isolate, call)  \
   RETURN_IF_EMPTY_HANDLE_VALUE(isolate, call, Failure::Exception())
 
+
+// Macros for MaybeHandle.
+
+#define RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, T)  \
+  do {                                                       \
+    Isolate* __isolate__ = (isolate);                        \
+    if (__isolate__->has_scheduled_exception()) {            \
+      __isolate__->PromoteScheduledException();              \
+      return MaybeHandle<T>();                               \
+    }                                                        \
+  } while (false)
+
+#define ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, dst, call, value)  \
+  do {                                                               \
+    if (!(call).ToHandle(&dst)) {                                    \
+      ASSERT((isolate)->has_pending_exception());                    \
+      return value;                                                  \
+    }                                                                \
+  } while (false)
+
+#define ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, dst, call)  \
+  ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, dst, call, Failure::Exception())
+
+#define ASSIGN_RETURN_ON_EXCEPTION(isolate, dst, call, T)  \
+  ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, dst, call, MaybeHandle<T>())
+
+#define RETURN_ON_EXCEPTION_VALUE(isolate, dst, call, value)  \
+  do {                                                             \
+    if (call.is_null()) {                                          \
+      ASSERT((isolate)->has_pending_exception());                  \
+      return value;                                                \
+    }                                                              \
+  } while (false)
+
+#define RETURN_FAILURE_ON_EXCEPTION(isolate, call)  \
+  RETURN_ON_EXCEPTION_VALUE(isolate, dst, call, Failure::Exception())
+
+#define RETURN_ON_EXCEPTION(isolate, call, T)  \
+  RETURN_ON_EXCEPTION_VALUE(                   \
+      isolate, dst, call, MaybeHandle<T>::Exception())
+
+
 #define FOR_EACH_ISOLATE_ADDRESS_NAME(C)                \
   C(Handler, handler)                                   \
   C(CEntryFP, c_entry_fp)                               \
@@ -767,6 +809,14 @@ class Isolate {
   // Exception throwing support. The caller should use the result
   // of Throw() as its return value.
   Failure* Throw(Object* exception, MessageLocation* location = NULL);
+
+  template <typename T>
+  MUST_USE_RESULT MaybeHandle<T> Throw(Handle<Object> exception,
+                                       MessageLocation* location = NULL) {
+    Throw(*exception, location);
+    return MaybeHandle<T>();
+  }
+
   // Re-throw an exception.  This involves no error reporting since
   // error reporting was handled when the exception was thrown
   // originally.
index 72c5dbe87d06a7fae5f245f138b5e4bd993139db..9cda2baf0d44476c6aeef51b8deb600a5fe35395 100644 (file)
@@ -5519,7 +5519,7 @@ static void FreezeDictionary(Dictionary* dictionary) {
 }
 
 
-Handle<Object> JSObject::Freeze(Handle<JSObject> object) {
+MaybeHandle<Object> JSObject::Freeze(Handle<JSObject> object) {
   // Freezing sloppy arguments should be handled elsewhere.
   ASSERT(!object->HasSloppyArgumentsElements());
   ASSERT(!object->map()->is_observed());
@@ -5532,7 +5532,7 @@ Handle<Object> JSObject::Freeze(Handle<JSObject> object) {
                                       isolate->factory()->undefined_value(),
                                       v8::ACCESS_KEYS)) {
     isolate->ReportFailedAccessCheckWrapper(object, v8::ACCESS_KEYS);
-    RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object);
+    RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
     return isolate->factory()->false_value();
   }
 
@@ -5550,8 +5550,7 @@ Handle<Object> JSObject::Freeze(Handle<JSObject> object) {
         isolate->factory()->NewTypeError(
             "cant_prevent_ext_external_array_elements",
             HandleVector(&object, 1));
-    isolate->Throw(*error);
-    return Handle<Object>();
+    return isolate->Throw<Object>(error);
   }
 
   Handle<SeededNumberDictionary> new_element_dictionary;
index 8d6d23e1fa4a951806d3cbf1659438a12fe36529..dad070e0c330df8ef5befe4d3183a354001cd2eb 100644 (file)
@@ -2640,7 +2640,7 @@ class JSObject: public JSReceiver {
   static Handle<Object> PreventExtensions(Handle<JSObject> object);
 
   // ES5 Object.freeze
-  static Handle<Object> Freeze(Handle<JSObject> object);
+  static MaybeHandle<Object> Freeze(Handle<JSObject> object);
 
   // Called the first time an object is observed with ES7 Object.observe.
   static void SetObserved(Handle<JSObject> object);
index 215c4ebf5c12f3e7bb1765b3146f6175f7a1cd17..fe5d15f6be3ed5140c607d2f450d70426dd98c2e 100644 (file)
@@ -3231,8 +3231,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ObjectFreeze) {
   HandleScope scope(isolate);
   ASSERT(args.length() == 1);
   CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
-  Handle<Object> result = JSObject::Freeze(object);
-  RETURN_IF_EMPTY_HANDLE(isolate, result);
+  Handle<Object> result;
+  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, JSObject::Freeze(object));
   return *result;
 }
 
index cb296146a7d59e13f88e4a9e3f63941a256a81df..8a89a933a7f08d7ffdfe905faa09fd1ec602542a 100644 (file)
@@ -128,6 +128,7 @@ class MemoryChunk;
 class SeededNumberDictionary;
 class UnseededNumberDictionary;
 class NameDictionary;
+template <typename T> class MaybeHandle;
 template <typename T> class Handle;
 class Heap;
 class HeapObject;