blob: 0afcf0b3471b058ca0ce8f52cd3aec62e84217cf [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
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/script/v8c/isolate_fellowship.h"
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
#include "base/logging.h"
#include "base/trace_event/trace_event.h"
#include "cobalt/script/v8c/v8c_tracing_controller.h"
#include "starboard/configuration_constants.h"
#include "starboard/file.h"
#include "starboard/memory.h"
namespace cobalt {
namespace script {
namespace v8c {
namespace {
// This file will also be touched and rebuilt every time V8 is re-built
// according to the update_snapshot_time gyp target.
const char kIsolateFellowshipBuildTime[] = __DATE__ " " __TIME__;
const char* kV8CommandLineFlags[] = {"--optimize_for_size",
// Starboard disallow rwx memory access.
// Cobalt's TraceMembers and
// ScriptValue::*Reference do not currently
// support incremental tracing.
#if defined(COBALT_GC_ZEAL)
// Configure v8's global command line flag options for Cobalt.
// It can be called more than once, but make sure it is called before any
// v8 instance is created.
void V8FlagsInit() {
for (auto flag_str : kV8CommandLineFlags) {
v8::V8::SetFlagsFromString(flag_str, SbStringGetLength(flag_str));
} // namespace
IsolateFellowship::IsolateFellowship() {
TRACE_EVENT0("cobalt::script", "IsolateFellowship::IsolateFellowship");
// TODO: Initialize V8 ICU stuff here as well.
platform.reset(new CobaltPlatform(v8::platform::NewDefaultPlatform(
0 /*thread_pool_size*/, v8::platform::IdleTaskSupport::kDisabled,
std::unique_ptr<v8::TracingController>(new V8cTracingController()))));
array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
// If a new snapshot data is needed, a temporary v8 isolate will be created
// to write the snapshot data. We need to make sure all global command line
// flags are set before that.
#endif // !defined(COBALT_V8_BUILDTIME_SNAPSHOT)
IsolateFellowship::~IsolateFellowship() {
TRACE_EVENT0("cobalt::script", "IsolateFellowship::~IsolateFellowship");
delete array_buffer_allocator;
array_buffer_allocator = nullptr;
// Note that both us and V8 will have created this with new[], see
// "". Also note that both startup data creation failure
// from V8 is possible, and deleting a null pointer is safe, so there is no
// DCHECK here.
startup_data = {nullptr, 0};
void IsolateFellowship::InitializeStartupData() {
TRACE_EVENT0("cobalt::script", "IsolateFellowship::InitializeStartupData");
DCHECK( == nullptr);
std::vector<char> cache_path(kSbFileMaxPath);
if (!SbSystemGetPath(kSbSystemPathCacheDirectory,,
cache_path.size())) {
// If there is no cache directory, then just save the startup data in
// memory.
LOG(WARNING) << "Unable to read/write V8 startup snapshot data to file.";
startup_data = v8::SnapshotCreator().CreateBlob(
// Whether or not we should attempt to remove the existing cache file due to
// it being in an invalid state. We will do so in the case that it either
// was of size 0 (but existed), or we failed to write it.
bool should_remove_cache_file = false;
// Attempt to read the cache file.
std::string snapshot_file_full_path =
std::string( + kSbFileSepString +
bool read_file = ([&]() {
starboard::ScopedFile scoped_file(snapshot_file_full_path.c_str(),
kSbFileOpenOnly | kSbFileRead);
if (!scoped_file.IsValid()) {
LOG(INFO) << "Can not open snapshot file";
return false;
int64_t size = scoped_file.GetSize();
if (size == -1) {
LOG(ERROR) << "Received size of -1 for file that was valid.";
return false;
if (size == 0) {
LOG(ERROR) << "Read V8 snapshot file of size 0.";
should_remove_cache_file = true;
return false;
int64_t data_size = size - sizeof(kIsolateFellowshipBuildTime);
char snapshot_time[sizeof(kIsolateFellowshipBuildTime)];
int read =
scoped_file.ReadAll(snapshot_time, sizeof(kIsolateFellowshipBuildTime));
// Logically, this could be collapsed to just "read != data_size", but this
// should be read as "if the platform explicitly told us reading failed,
// or the platform told us we read less than we expected".
if (read == -1 || read != sizeof(kIsolateFellowshipBuildTime)) {
LOG(ERROR) << "Reading V8 startup snapshot time failed.";
should_remove_cache_file = true;
return false;
// kIsolateFellowshipBuildTime is an auto-generated/updated time stamp when
// v8 target is compiled to update snapshot data after any v8 change.
if (SbMemoryCompare(snapshot_time, kIsolateFellowshipBuildTime,
sizeof(kIsolateFellowshipBuildTime)) != 0) {
LOG(INFO) << "V8 code was modified since last V8 startup snapshot cache "
"file was generated, creating a new one.";
should_remove_cache_file = true;
return false;
std::unique_ptr<char[]> data(new char[data_size]);
read = scoped_file.ReadAll(data.get(), data_size);
if (read == -1 || read != data_size) {
LOG(ERROR) << "Reading V8 startup snapshot cache file failed for some "
"unknown reason.";
should_remove_cache_file = true;
return false;
LOG(INFO) << "Successfully read V8 startup snapshot cache file."; = data.release();
startup_data.raw_size = data_size;
return true;
auto maybe_remove_cache_file = [&]() {
if (should_remove_cache_file) {
if (!SbFileDelete(snapshot_file_full_path.c_str())) {
LOG(ERROR) << "Failed to delete V8 startup snapshot cache file.";
should_remove_cache_file = false;
// If we failed to read the file, then create the snapshot data and attempt
// to write it.
if (!read_file) {
([&]() {
startup_data = v8::SnapshotCreator().CreateBlob(
if ( == nullptr) {
// Trust the V8 API, but verify. |raw_size| should also be 0.
DCHECK_EQ(startup_data.raw_size, 0);
// This is technically legal w.r.t. to the API documentation, but
// *probably* indicates a serious problem (are you hacking V8
// internals or something?).
LOG(WARNING) << "Failed to create V8 startup snapshot.";
starboard::ScopedFile scoped_file(snapshot_file_full_path.c_str(),
kSbFileCreateOnly | kSbFileWrite);
if (!scoped_file.IsValid()) {
<< "Failed to open V8 startup snapshot cache file for writing.";
int written = scoped_file.WriteAll(kIsolateFellowshipBuildTime,
if (written < sizeof(kIsolateFellowshipBuildTime)) {
LOG(ERROR) << "Failed to write V8 startup snapshot time.";
should_remove_cache_file = true;
written = scoped_file.WriteAll(, startup_data.raw_size);
if (written < startup_data.raw_size) {
LOG(ERROR) << "Failed to write entire V8 startup snapshot.";
should_remove_cache_file = true;
LOG(INFO) << "Successfully wrote V8 startup snapshot cache file.";
#endif // !defined(COBALT_V8_BUILDTIME_SNAPSHOT)
} // namespace v8c
} // namespace script
} // namespace cobalt