| // Copyright 2017 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/v8c/v8c_engine.h" |
| |
| #include <algorithm> |
| #include <string> |
| |
| #include "base/debug/trace_event.h" |
| #include "base/logging.h" |
| #include "base/message_loop.h" |
| #include "cobalt/base/c_val.h" |
| #include "cobalt/browser/stack_size_constants.h" |
| #include "cobalt/script/v8c/isolate_fellowship.h" |
| #include "cobalt/script/v8c/v8c_global_environment.h" |
| |
| namespace cobalt { |
| namespace script { |
| namespace v8c { |
| |
| namespace { |
| |
| void VisitWeakHandlesForMinorGC(v8::Isolate* isolate) { |
| class V8cPersistentHandleVisitor : public v8::PersistentHandleVisitor { |
| public: |
| void VisitPersistentHandle(v8::Persistent<v8::Value>* value, |
| uint16_t class_id) override { |
| DCHECK(value); |
| value->MarkActive(); |
| } |
| } visitor; |
| isolate->VisitWeakHandles(&visitor); |
| } |
| |
| size_t UsedHeapSize(v8::Isolate* isolate) { |
| v8::HeapStatistics heap_statistics; |
| isolate->GetHeapStatistics(&heap_statistics); |
| return heap_statistics.used_heap_size(); |
| } |
| |
| void GCPrologueCallback(v8::Isolate* isolate, v8::GCType type, |
| v8::GCCallbackFlags) { |
| switch (type) { |
| case v8::kGCTypeScavenge: |
| TRACE_EVENT_BEGIN1("cobalt::script", "MinorGC", "usedHeapSizeBefore", |
| UsedHeapSize(isolate)); |
| VisitWeakHandlesForMinorGC(isolate); |
| break; |
| case v8::kGCTypeMarkSweepCompact: |
| TRACE_EVENT_BEGIN2("cobalt::script", "MajorGC", "usedHeapSizeBefore", |
| UsedHeapSize(isolate), "type", "atomic pause"); |
| break; |
| case v8::kGCTypeIncrementalMarking: |
| TRACE_EVENT_BEGIN2("cobalt::script", "MajorGC", "usedHeapSizeBefore", |
| UsedHeapSize(isolate), "type", "incremental marking"); |
| break; |
| case v8::kGCTypeProcessWeakCallbacks: |
| TRACE_EVENT_BEGIN2("cobalt::script", "MajorGC", "usedHeapSizeBefore", |
| UsedHeapSize(isolate), "type", "weak processing"); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| void GCEpilogueCallback(v8::Isolate* isolate, v8::GCType type, |
| v8::GCCallbackFlags) { |
| switch (type) { |
| case v8::kGCTypeScavenge: |
| TRACE_EVENT_END1("cobalt::script", "MinorGC", "usedHeapSizeAfter", |
| UsedHeapSize(isolate)); |
| break; |
| case v8::kGCTypeMarkSweepCompact: |
| TRACE_EVENT_END1("cobalt::script", "MajorGC", "usedHeapSizeAfter", |
| UsedHeapSize(isolate)); |
| break; |
| case v8::kGCTypeIncrementalMarking: |
| TRACE_EVENT_END1("cobalt::script", "MajorGC", "usedHeapSizeAfter", |
| UsedHeapSize(isolate)); |
| break; |
| case v8::kGCTypeProcessWeakCallbacks: |
| TRACE_EVENT_END1("cobalt::script", "MajorGC", "usedHeapSizeAfter", |
| UsedHeapSize(isolate)); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| } // namespace |
| |
| V8cEngine::V8cEngine(const Options& options) : options_(options) { |
| TRACE_EVENT0("cobalt::script", "V8cEngine::V8cEngine()"); |
| |
| auto* isolate_fellowship = IsolateFellowship::GetInstance(); |
| |
| v8::Isolate::CreateParams create_params; |
| create_params.array_buffer_allocator = |
| isolate_fellowship->array_buffer_allocator; |
| auto* startup_data = &isolate_fellowship->startup_data; |
| if (startup_data->data != nullptr) { |
| create_params.snapshot_blob = startup_data; |
| } else { |
| // Technically possible to attempt to recover here, but hitting this |
| // indicates that something is probably seriously wrong. |
| LOG(WARNING) << "Isolate fellowship startup data was null, this will " |
| "significantly slow down startup time."; |
| } |
| |
| isolate_ = v8::Isolate::New(create_params); |
| CHECK(isolate_); |
| |
| // There are 2 total isolate data slots, one for the sole |V8cEngine| (us), |
| // and one for the |V8cGlobalEnvironment|. |
| const int kTotalIsolateDataSlots = 2; |
| DCHECK_GE(v8::Isolate::GetNumberOfDataSlots(), kTotalIsolateDataSlots); |
| isolate_->SetData(kIsolateDataIndex, this); |
| |
| v8c_heap_tracer_.reset(new V8cHeapTracer(isolate_)); |
| isolate_->SetEmbedderHeapTracer(v8c_heap_tracer_.get()); |
| |
| isolate_->AddGCPrologueCallback(GCPrologueCallback); |
| isolate_->AddGCEpilogueCallback(GCEpilogueCallback); |
| } |
| |
| V8cEngine::~V8cEngine() { |
| TRACE_EVENT0("cobalt::script", "V8cEngine::~V8cEngine"); |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| // Send a low memory notification to V8 in order to force a garbage |
| // collection before shut down. This is required to run weak callbacks that |
| // are responsible for freeing native objects that live in the internal |
| // fields of V8 objects. In the future, we should consider investigating if |
| // there are startup performance wins to be made by delaying this work until |
| // later (e.g., does the GC of the splash screen web module dying hurt us?). |
| isolate_->LowMemoryNotification(); |
| isolate_->Dispose(); |
| isolate_ = nullptr; |
| } |
| |
| scoped_refptr<GlobalEnvironment> V8cEngine::CreateGlobalEnvironment() { |
| TRACE_EVENT0("cobalt::script", "V8cEngine::CreateGlobalEnvironment()"); |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| return new V8cGlobalEnvironment(isolate_); |
| } |
| |
| void V8cEngine::CollectGarbage() { |
| TRACE_EVENT0("cobalt::script", "V8cEngine::CollectGarbage()"); |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| isolate_->LowMemoryNotification(); |
| } |
| |
| void V8cEngine::AdjustAmountOfExternalAllocatedMemory(int64_t bytes) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| isolate_->AdjustAmountOfExternalAllocatedMemory(bytes); |
| } |
| |
| bool V8cEngine::RegisterErrorHandler(JavaScriptEngine::ErrorHandler handler) { |
| error_handler_ = handler; |
| return true; |
| } |
| |
| void V8cEngine::SetGcThreshold(int64_t bytes) { NOTIMPLEMENTED(); } |
| |
| HeapStatistics V8cEngine::GetHeapStatistics() { |
| v8::HeapStatistics v8_heap_statistics; |
| isolate_->GetHeapStatistics(&v8_heap_statistics); |
| return {v8_heap_statistics.total_heap_size(), |
| v8_heap_statistics.used_heap_size()}; |
| } |
| |
| } // namespace v8c |
| |
| // static |
| scoped_ptr<JavaScriptEngine> JavaScriptEngine::CreateEngine( |
| const JavaScriptEngine::Options& options) { |
| TRACE_EVENT0("cobalt::script", "JavaScriptEngine::CreateEngine()"); |
| return make_scoped_ptr<JavaScriptEngine>(new v8c::V8cEngine(options)); |
| } |
| |
| } // namespace script |
| } // namespace cobalt |