blob: a6c1d865c76b2a2a56e8fc2d9099dd19e987e316 [file] [log] [blame]
// Copyright 2018 The Cobalt Authors. 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/v8c/v8c_heap_tracer.h"
#include <algorithm>
#include "base/trace_event/trace_event.h"
#include "cobalt/script/v8c/v8c_engine.h"
#include "cobalt/script/v8c/v8c_global_environment.h"
#include "cobalt/script/v8c/wrapper_private.h"
namespace cobalt {
namespace script {
namespace v8c {
void V8cHeapTracer::RegisterV8References(
const std::vector<std::pair<void*, void*>>& embedder_fields) {
for (const auto& embedder_field : embedder_fields) {
WrapperPrivate* wrapper_private =
static_cast<WrapperPrivate*>(embedder_field.first);
Wrappable* wrappable = wrapper_private->raw_wrappable();
MaybeAddToFrontier(wrappable);
DCHECK(embedder_field.second == WrapperPrivate::kInternalFieldIdValue);
}
}
void V8cHeapTracer::TracePrologue() {
TRACE_EVENT0("cobalt::script", "V8cHeapTracer::TracePrologue");
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (disabled_) {
return;
}
DCHECK_EQ(frontier_.size(), 0);
DCHECK_EQ(visited_.size(), 0);
}
bool V8cHeapTracer::AdvanceTracing(double deadline_in_ms) {
TRACE_EVENT0("cobalt::script", "V8cHeapTracer::AdvanceTracing");
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
double start_time = platform_->MonotonicallyIncreasingTime();
if (disabled_) {
return true;
}
// This feels a bit weird, but as far as I can tell, we're expected to
// manually decide to trace the from the global object.
MaybeAddToFrontier(
V8cGlobalEnvironment::GetFromIsolate(isolate_)->global_wrappable());
// Objects that we want to keep alive.
for (Traceable* traceable : roots_) {
MaybeAddToFrontier(traceable);
}
for (v8::TracedGlobal<v8::Value>* traced_global : globals_) {
RegisterEmbedderReference(*traced_global);
}
while (platform_->MonotonicallyIncreasingTime() - start_time <
deadline_in_ms) {
if (frontier_.empty()) {
return false;
}
Traceable* traceable = frontier_.back();
frontier_.pop_back();
DCHECK(traceable);
if (traceable->IsWrappable()) {
Wrappable* wrappable = base::polymorphic_downcast<Wrappable*>(traceable);
auto pair_range = reference_map_.equal_range(wrappable);
for (auto it = pair_range.first; it != pair_range.second; ++it) {
// Tell v8 this object is referenced on Cobalt heap.
RegisterEmbedderReference(it->second->traced_global());
}
WrapperFactory* wrapper_factory =
V8cGlobalEnvironment::GetFromIsolate(isolate_)->wrapper_factory();
WrapperPrivate* maybe_wrapper_private =
wrapper_factory->MaybeGetWrapperPrivate(
static_cast<Wrappable*>(traceable));
if (maybe_wrapper_private) {
RegisterEmbedderReference(maybe_wrapper_private->traced_global());
}
}
traceable->TraceMembers(this);
}
return true;
}
bool V8cHeapTracer::IsTracingDone() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (disabled_) {
return true;
}
return frontier_.empty();
}
void V8cHeapTracer::TraceEpilogue() {
TRACE_EVENT0("cobalt::script", "V8cHeapTracer::TraceEpilogue");
if (disabled_) {
return;
}
DCHECK(frontier_.empty());
visited_.clear();
}
void V8cHeapTracer::EnterFinalPause(EmbedderStackState stack_state) {
TRACE_EVENT0("cobalt::script", "V8cHeapTracer::EnterFinalPause");
}
void V8cHeapTracer::Trace(Traceable* traceable) {
DCHECK(!disabled_);
MaybeAddToFrontier(traceable);
}
void V8cHeapTracer::AddReferencedObject(Wrappable* owner,
ScopedPersistent<v8::Value>* value) {
DCHECK(owner);
DCHECK(value);
auto it = reference_map_.insert({owner, value});
}
void V8cHeapTracer::RemoveReferencedObject(Wrappable* owner,
ScopedPersistent<v8::Value>* value) {
DCHECK(owner);
DCHECK(value);
auto pair_range = reference_map_.equal_range(owner);
auto it = std::find_if(
pair_range.first, pair_range.second,
[&](decltype(*pair_range.first) it) { return it.second == value; });
DCHECK(it != pair_range.second);
reference_map_.erase(it);
}
void V8cHeapTracer::AddRoot(Traceable* traceable) {
DCHECK(traceable);
roots_.insert(traceable);
}
void V8cHeapTracer::RemoveRoot(Traceable* traceable) {
DCHECK(traceable);
auto it = roots_.find(traceable);
DCHECK(it != roots_.end());
roots_.erase(it);
}
void V8cHeapTracer::AddRoot(v8::TracedGlobal<v8::Value>* traced_global) {
DCHECK(traced_global);
globals_.insert(traced_global);
}
void V8cHeapTracer::RemoveRoot(v8::TracedGlobal<v8::Value>* traced_global) {
DCHECK(traced_global);
auto it = globals_.find(traced_global);
DCHECK(it != globals_.end());
globals_.erase(it);
}
void V8cHeapTracer::MaybeAddToFrontier(Traceable* traceable) {
if (!traceable) {
return;
}
if (!visited_.insert(traceable).second) {
return;
}
frontier_.push_back(traceable);
}
} // namespace v8c
} // namespace script
} // namespace cobalt