| /* |
| * 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" |
| |
| 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()); |
| JS::RootedObject to_object(context_, to->js_object_proxy()); |
| referenced_objects_.push_back( |
| std::make_pair(from_key, WeakHeapObject(context_, to_object))); |
| referenced_object_map_->AddReferencedObject(from_key, to_object); |
| } |
| |
| ~OpaqueRootStateImpl() { |
| for (ReferencedObjectPairVector::iterator it = referenced_objects_.begin(); |
| it != referenced_objects_.end(); ++it) { |
| if (it->second.Get()) { |
| JS::RootedObject reachable_object(context_, it->second.Get()); |
| referenced_object_map_->RemoveReferencedObject(it->first, |
| reachable_object); |
| } |
| } |
| } |
| |
| 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* reachable_object_wrapper_private = *it; |
| Wrappable* opaque_root = reachable_object_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->TrackReachability(reachable_object_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 (reachable_object_wrapper_private |
| ->ShouldKeepWrapperAliveIfReachable()) { |
| state->TrackReachability(opaque_root_private, |
| reachable_object_wrapper_private); |
| } |
| } |
| } |
| return state.PassAs<OpaqueRootState>(); |
| } |
| |
| } // namespace mozjs |
| } // namespace script |
| } // namespace cobalt |