blob: 2663e0b10a4bf951e085873ee40e2745e70c495e [file] [log] [blame]
// 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