Merge "Split ArtJvmtiEvent::kClassFileLoadHook in two."
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 195f179..90467db 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -905,11 +905,15 @@
     ENSURE_NON_NULL(capabilities_ptr);
     ArtJvmTiEnv* art_env = static_cast<ArtJvmTiEnv*>(env);
     jvmtiError ret = OK;
+    jvmtiCapabilities changed;
 #define ADD_CAPABILITY(e) \
     do { \
       if (capabilities_ptr->e == 1) { \
         if (kPotentialCapabilities.e == 1) { \
-          art_env->capabilities.e = 1;\
+          if (art_env->capabilities.e != 1) { \
+            art_env->capabilities.e = 1; \
+            changed.e = 1; \
+          }\
         } else { \
           ret = ERR(NOT_AVAILABLE); \
         } \
@@ -958,6 +962,9 @@
     ADD_CAPABILITY(can_generate_resource_exhaustion_heap_events);
     ADD_CAPABILITY(can_generate_resource_exhaustion_threads_events);
 #undef ADD_CAPABILITY
+    gEventHandler.HandleChangedCapabilities(ArtJvmTiEnv::AsArtJvmTiEnv(env),
+                                            changed,
+                                            /*added*/true);
     return ret;
   }
 
@@ -966,10 +973,14 @@
     ENSURE_VALID_ENV(env);
     ENSURE_NON_NULL(capabilities_ptr);
     ArtJvmTiEnv* art_env = reinterpret_cast<ArtJvmTiEnv*>(env);
+    jvmtiCapabilities changed;
 #define DEL_CAPABILITY(e) \
     do { \
       if (capabilities_ptr->e == 1) { \
-        art_env->capabilities.e = 0;\
+        if (art_env->capabilities.e == 1) { \
+          art_env->capabilities.e = 0;\
+          changed.e = 1; \
+        } \
       } \
     } while (false)
 
@@ -1015,6 +1026,9 @@
     DEL_CAPABILITY(can_generate_resource_exhaustion_heap_events);
     DEL_CAPABILITY(can_generate_resource_exhaustion_threads_events);
 #undef DEL_CAPABILITY
+    gEventHandler.HandleChangedCapabilities(ArtJvmTiEnv::AsArtJvmTiEnv(env),
+                                            changed,
+                                            /*added*/false);
     return OK;
   }
 
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index fb39db5..1e07bc6 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -17,15 +17,24 @@
 #ifndef ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_
 #define ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_
 
+#include <array>
+
 #include "events.h"
 
 #include "art_jvmti.h"
 
 namespace openjdkjvmti {
 
-static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env ATTRIBUTE_UNUSED,
-                                                           jvmtiEvent e) {
-  return static_cast<ArtJvmtiEvent>(e);
+static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e) {
+  if (UNLIKELY(e == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK)) {
+    if (env->capabilities.can_retransform_classes) {
+      return ArtJvmtiEvent::kClassFileLoadHookRetransformable;
+    } else {
+      return ArtJvmtiEvent::kClassFileLoadHookNonRetransformable;
+    }
+  } else {
+    return static_cast<ArtJvmtiEvent>(e);
+  }
 }
 
 template <typename FnType>
@@ -46,7 +55,8 @@
       return reinterpret_cast<FnType*>(env->event_callbacks->ThreadStart);
     case ArtJvmtiEvent::kThreadEnd:
       return reinterpret_cast<FnType*>(env->event_callbacks->ThreadEnd);
-    case ArtJvmtiEvent::kClassFileLoadHook:
+    case ArtJvmtiEvent::kClassFileLoadHookRetransformable:
+    case ArtJvmtiEvent::kClassFileLoadHookNonRetransformable:
       return reinterpret_cast<FnType*>(env->event_callbacks->ClassFileLoadHook);
     case ArtJvmtiEvent::kClassLoad:
       return reinterpret_cast<FnType*>(env->event_callbacks->ClassLoad);
@@ -131,6 +141,40 @@
   return dispatch;
 }
 
+inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) {
+  bool union_value = false;
+  for (const ArtJvmTiEnv* stored_env : envs) {
+    union_value |= stored_env->event_masks.global_event_mask.Test(event);
+    union_value |= stored_env->event_masks.unioned_thread_event_mask.Test(event);
+    if (union_value) {
+      break;
+    }
+  }
+  global_mask.Set(event, union_value);
+}
+
+inline bool EventHandler::NeedsEventUpdate(ArtJvmTiEnv* env,
+                                           const jvmtiCapabilities& caps,
+                                           bool added) {
+  ArtJvmtiEvent event = added ? ArtJvmtiEvent::kClassFileLoadHookNonRetransformable
+                              : ArtJvmtiEvent::kClassFileLoadHookRetransformable;
+  return caps.can_retransform_classes == 1 &&
+      IsEventEnabledAnywhere(event) &&
+      env->event_masks.IsEnabledAnywhere(event);
+}
+
+inline void EventHandler::HandleChangedCapabilities(ArtJvmTiEnv* env,
+                                                    const jvmtiCapabilities& caps,
+                                                    bool added) {
+  if (UNLIKELY(NeedsEventUpdate(env, caps, added))) {
+    env->event_masks.HandleChangedCapabilities(caps, added);
+    if (caps.can_retransform_classes == 1) {
+      RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookRetransformable);
+      RecalculateGlobalEventMask(ArtJvmtiEvent::kClassFileLoadHookNonRetransformable);
+    }
+  }
+}
+
 }  // namespace openjdkjvmti
 
 #endif  // ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index 66929cf..f38aa86 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -47,6 +47,10 @@
 
 namespace openjdkjvmti {
 
+bool EventMasks::IsEnabledAnywhere(ArtJvmtiEvent event) {
+  return global_event_mask.Test(event) || unioned_thread_event_mask.Test(event);
+}
+
 EventMask& EventMasks::GetEventMask(art::Thread* thread) {
   if (thread == nullptr) {
     return global_event_mask;
@@ -107,6 +111,35 @@
   }
 }
 
+void EventMasks::HandleChangedCapabilities(const jvmtiCapabilities& caps, bool caps_added) {
+  if (UNLIKELY(caps.can_retransform_classes == 1)) {
+    // If we are giving this env the retransform classes cap we need to switch all events of
+    // NonTransformable to Transformable and vice versa.
+    ArtJvmtiEvent to_remove = caps_added ? ArtJvmtiEvent::kClassFileLoadHookNonRetransformable
+                                         : ArtJvmtiEvent::kClassFileLoadHookRetransformable;
+    ArtJvmtiEvent to_add = caps_added ? ArtJvmtiEvent::kClassFileLoadHookRetransformable
+                                      : ArtJvmtiEvent::kClassFileLoadHookNonRetransformable;
+    if (global_event_mask.Test(to_remove)) {
+      CHECK(!global_event_mask.Test(to_add));
+      global_event_mask.Set(to_remove, false);
+      global_event_mask.Set(to_add, true);
+    }
+
+    if (unioned_thread_event_mask.Test(to_remove)) {
+      CHECK(!unioned_thread_event_mask.Test(to_add));
+      unioned_thread_event_mask.Set(to_remove, false);
+      unioned_thread_event_mask.Set(to_add, true);
+    }
+    for (auto thread_mask : thread_event_masks) {
+      if (thread_mask.second.Test(to_remove)) {
+        CHECK(!thread_mask.second.Test(to_add));
+        thread_mask.second.Set(to_remove, false);
+        thread_mask.second.Set(to_add, true);
+      }
+    }
+  }
+}
+
 void EventHandler::RegisterArtJvmTiEnv(ArtJvmTiEnv* env) {
   envs.push_back(env);
 }
@@ -293,17 +326,7 @@
     DCHECK_EQ(mode, JVMTI_DISABLE);
 
     env->event_masks.DisableEvent(thread, event);
-
-    // Gotta recompute the global mask.
-    bool union_value = false;
-    for (const ArtJvmTiEnv* stored_env : envs) {
-      union_value |= stored_env->event_masks.global_event_mask.Test(event);
-      union_value |= stored_env->event_masks.unioned_thread_event_mask.Test(event);
-      if (union_value) {
-        break;
-      }
-    }
-    global_mask.Set(event, union_value);
+    RecalculateGlobalEventMask(event);
   }
 
   bool new_state = global_mask.Test(event);
diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h
index 8f56145..7990141 100644
--- a/runtime/openjdkjvmti/events.h
+++ b/runtime/openjdkjvmti/events.h
@@ -30,14 +30,15 @@
 class JvmtiAllocationListener;
 class JvmtiGcPauseListener;
 
-// an enum for ArtEvents.
+// an enum for ArtEvents. This differs from the JVMTI events only in that we distinguish between
+// retransformation capable and incapable loading
 enum class ArtJvmtiEvent {
     kMinEventTypeVal = JVMTI_MIN_EVENT_TYPE_VAL,
     kVmInit = JVMTI_EVENT_VM_INIT,
     kVmDeath = JVMTI_EVENT_VM_DEATH,
     kThreadStart = JVMTI_EVENT_THREAD_START,
     kThreadEnd = JVMTI_EVENT_THREAD_END,
-    kClassFileLoadHook = JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
+    kClassFileLoadHookNonRetransformable = JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
     kClassLoad = JVMTI_EVENT_CLASS_LOAD,
     kClassPrepare = JVMTI_EVENT_CLASS_PREPARE,
     kVmStart = JVMTI_EVENT_VM_START,
@@ -64,14 +65,19 @@
     kGarbageCollectionFinish = JVMTI_EVENT_GARBAGE_COLLECTION_FINISH,
     kObjectFree = JVMTI_EVENT_OBJECT_FREE,
     kVmObjectAlloc = JVMTI_EVENT_VM_OBJECT_ALLOC,
-    kMaxEventTypeVal = JVMTI_MAX_EVENT_TYPE_VAL,
+    kClassFileLoadHookRetransformable = JVMTI_MAX_EVENT_TYPE_VAL + 1,
+    kMaxEventTypeVal = kClassFileLoadHookRetransformable,
 };
 
 // Convert a jvmtiEvent into a ArtJvmtiEvent
 ALWAYS_INLINE static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e);
 
-ALWAYS_INLINE static inline jvmtiEvent GetJvmtiEvent(ArtJvmtiEvent e) {
-  return static_cast<jvmtiEvent>(e);
+static inline jvmtiEvent GetJvmtiEvent(ArtJvmtiEvent e) {
+  if (UNLIKELY(e == ArtJvmtiEvent::kClassFileLoadHookRetransformable)) {
+    return JVMTI_EVENT_CLASS_FILE_LOAD_HOOK;
+  } else {
+    return static_cast<jvmtiEvent>(e);
+  }
 }
 
 struct EventMask {
@@ -118,6 +124,11 @@
   EventMask* GetEventMaskOrNull(art::Thread* thread);
   void EnableEvent(art::Thread* thread, ArtJvmtiEvent event);
   void DisableEvent(art::Thread* thread, ArtJvmtiEvent event);
+  bool IsEnabledAnywhere(ArtJvmtiEvent event);
+  // Make any changes to event masks needed for the given capability changes. If caps_added is true
+  // then caps is all the newly set capabilities of the jvmtiEnv. If it is false then caps is the
+  // set of all capabilities that were removed from the jvmtiEnv.
+  void HandleChangedCapabilities(const jvmtiCapabilities& caps, bool caps_added);
 };
 
 // Helper class for event handling.
@@ -146,10 +157,27 @@
   ALWAYS_INLINE
   inline void DispatchEvent(art::Thread* thread, ArtJvmtiEvent event, Args... args) const;
 
+  // Tell the event handler capabilities were added/lost so it can adjust the sent events.If
+  // caps_added is true then caps is all the newly set capabilities of the jvmtiEnv. If it is false
+  // then caps is the set of all capabilities that were removed from the jvmtiEnv.
+  ALWAYS_INLINE
+  inline void HandleChangedCapabilities(ArtJvmTiEnv* env,
+                                        const jvmtiCapabilities& caps,
+                                        bool added);
+
  private:
   ALWAYS_INLINE
   static inline bool ShouldDispatch(ArtJvmtiEvent event, ArtJvmTiEnv* env, art::Thread* thread);
 
+  ALWAYS_INLINE
+  inline bool NeedsEventUpdate(ArtJvmTiEnv* env,
+                               const jvmtiCapabilities& caps,
+                               bool added);
+
+  // Recalculates the event mask for the given event.
+  ALWAYS_INLINE
+  inline void RecalculateGlobalEventMask(ArtJvmtiEvent event);
+
   void HandleEventType(ArtJvmtiEvent event, bool enable);
 
   // List of all JvmTiEnv objects that have been created, in their creation order.