| // Copyright 2016 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "cobalt/script/mozjs/opaque_root_tracker.h" |
| |
| #include <utility> |
| #include <vector> |
| |
| #include "cobalt/script/mozjs/weak_heap_object.h" |
| #include "third_party/mozjs/js/src/jsapi.h" |
| |
| namespace cobalt { |
| namespace script { |
| namespace mozjs { |
| namespace { |
| // Implementation of OpaqueRootTracker::OpaqueRootState. |
| // On creation, this class will register reachability between objects and their |
| // roots in |ReferencedObjectMap|. On destruction, the reachability |
| // relationship will be removed. |
| class OpaqueRootStateImpl : public OpaqueRootTracker::OpaqueRootState { |
| public: |
| OpaqueRootStateImpl(JSContext* context, |
| ReferencedObjectMap* referenced_object_map) |
| : context_(context), referenced_object_map_(referenced_object_map) {} |
| |
| void TrackReachability(WrapperPrivate* from, WrapperPrivate* to) { |
| intptr_t from_key = ReferencedObjectMap::GetKeyForWrappable( |
| from->wrappable<Wrappable>().get()); |
| JSObject* to_proxy = to->js_object_proxy(); |
| DCHECK(to_proxy); |
| JS::RootedValue to_value(context_, JS::ObjectValue(*to_proxy)); |
| referenced_objects_.push_back( |
| std::make_pair(from_key, WeakHeapObject(context_, to_value))); |
| referenced_object_map_->AddReferencedObject(from_key, to_value); |
| } |
| |
| ~OpaqueRootStateImpl() { |
| JSAutoRequest auto_request(context_); |
| for (ReferencedObjectPairVector::iterator it = referenced_objects_.begin(); |
| it != referenced_objects_.end(); ++it) { |
| WeakHeapObject &value = it->second; |
| if (value.IsGcThing() && !value.WasCollected()) { |
| JS::RootedValue reachable_value(context_, value.GetValue()); |
| referenced_object_map_->RemoveReferencedObject(it->first, |
| reachable_value); |
| } |
| } |
| } |
| |
| private: |
| typedef std::vector<std::pair<intptr_t, WeakHeapObject> > |
| ReferencedObjectPairVector; |
| |
| JSContext* context_; |
| ReferencedObjectMap* referenced_object_map_; |
| ReferencedObjectPairVector referenced_objects_; |
| }; |
| } // namespace |
| |
| OpaqueRootTracker::OpaqueRootTracker(JSContext* context, |
| ReferencedObjectMap* referenced_object_map, |
| WrapperFactory* wrapper_factory) |
| : context_(context), |
| referenced_object_map_(referenced_object_map), |
| wrapper_factory_(wrapper_factory) {} |
| |
| void OpaqueRootTracker::AddObjectWithOpaqueRoot( |
| WrapperPrivate* wrapper_private) { |
| all_objects_.insert(wrapper_private); |
| } |
| |
| void OpaqueRootTracker::RemoveObjectWithOpaqueRoot( |
| WrapperPrivate* wrapper_private) { |
| all_objects_.erase(wrapper_private); |
| } |
| |
| scoped_ptr<OpaqueRootTracker::OpaqueRootState> |
| OpaqueRootTracker::GetCurrentOpaqueRootState() { |
| scoped_ptr<OpaqueRootStateImpl> state( |
| new OpaqueRootStateImpl(context_, referenced_object_map_)); |
| // Get the current opaque root for all objects that are being tracked. |
| for (WrapperPrivateSet::iterator it = all_objects_.begin(); |
| it != all_objects_.end(); ++it) { |
| WrapperPrivate* wrapper_private = *it; |
| TrackReachabilityToOpaqueRoot(state.get(), wrapper_private); |
| TrackReachableWrappables(state.get(), wrapper_private); |
| } |
| return state.PassAs<OpaqueRootState>(); |
| } |
| |
| void OpaqueRootTracker::TrackReachabilityToOpaqueRoot( |
| OpaqueRootState* state, WrapperPrivate* wrapper_private) { |
| OpaqueRootStateImpl* state_impl = |
| base::polymorphic_downcast<OpaqueRootStateImpl*>(state); |
| // If this wrappable has an opaque root, track reachability between this |
| // wrappable and its root. |
| Wrappable* opaque_root = wrapper_private->GetOpaqueRoot(); |
| if (opaque_root) { |
| WrapperPrivate* opaque_root_private = WrapperPrivate::GetFromWrappable( |
| opaque_root, context_, wrapper_factory_); |
| // Always mark the root as reachable from the non-root object. |
| state_impl->TrackReachability(wrapper_private, opaque_root_private); |
| |
| // Only mark the non-root object as reachable if we need to keep the |
| // wrapper alive for some reason. In general it's okay for a wrapper to |
| // get GC'd because the Cobalt object will still be kept alive, and a new |
| // JS object can be created if needed again. |
| if (wrapper_private->ShouldKeepWrapperAliveIfReachable()) { |
| state_impl->TrackReachability(opaque_root_private, wrapper_private); |
| } |
| } |
| } |
| |
| void OpaqueRootTracker::TrackReachableWrappables( |
| OpaqueRootState* state, WrapperPrivate* wrapper_private) { |
| OpaqueRootStateImpl* state_impl = |
| base::polymorphic_downcast<OpaqueRootStateImpl*>(state); |
| // Track any wrappables that are explicitly marked as reachable from |
| // this wrappable. |
| typedef std::vector<Wrappable*> WrappableVector; |
| WrappableVector reachable_objects; |
| wrapper_private->GetReachableWrappables(&reachable_objects); |
| for (size_t i = 0; i < reachable_objects.size(); ++i) { |
| WrapperPrivate* reachable_object_private = WrapperPrivate::GetFromWrappable( |
| reachable_objects[i], context_, wrapper_factory_); |
| state_impl->TrackReachability(wrapper_private, reachable_object_private); |
| } |
| } |
| |
| } // namespace mozjs |
| } // namespace script |
| } // namespace cobalt |