157 строки
5.3 KiB
C++
157 строки
5.3 KiB
C++
// Copyright (c) Facebook, Inc. and its affiliates.
|
|
//
|
|
// This source code is licensed under the MIT license found in the
|
|
// LICENSE file in the root directory of this source tree.
|
|
|
|
#include "V8Runtime.h"
|
|
#include "V8Runtime_impl.h"
|
|
|
|
#include "FileUtils.h"
|
|
#include "v8.h"
|
|
#include "libplatform/libplatform.h"
|
|
|
|
#include <cxxreact/ReactMarker.h>
|
|
|
|
#include "V8Platform.h"
|
|
|
|
#include <cstdlib>
|
|
#include <iostream>
|
|
#include <mutex>
|
|
#include <atomic>
|
|
#include <list>
|
|
#include <sstream>
|
|
|
|
#include <folly/json.h>
|
|
|
|
namespace facebook { namespace v8runtime {
|
|
|
|
namespace {
|
|
int GetCacheTypeAsInt(const folly::dynamic& v8Config) {
|
|
return v8Config.getDefault("CacheType", static_cast<int>(CacheType::NoCache)).getInt();
|
|
}
|
|
|
|
std::string GetCacheDirectoryPath(const folly::dynamic& v8Config) {
|
|
return v8Config.getDefault("CacheDirectory", "").getString();
|
|
}
|
|
|
|
bool IsCacheEnabled(const folly::dynamic& v8Config) {
|
|
return
|
|
!v8Config.isNull() &&
|
|
!GetCacheDirectoryPath(v8Config).empty() &&
|
|
GetCacheTypeAsInt(v8Config) != static_cast<int>(CacheType::NoCache);
|
|
}
|
|
|
|
bool ShouldProduceFullCache(const folly::dynamic& v8Config) {
|
|
return GetCacheTypeAsInt(v8Config) == static_cast<int>(CacheType::FullCodeCache);
|
|
}
|
|
|
|
bool ShouldSetNoLazyFlag(const folly::dynamic& v8Config) {
|
|
return !v8Config.isNull() && !v8Config.getDefault("UseLazyScriptCompilation", false).getBool();
|
|
}
|
|
}
|
|
|
|
V8Runtime::V8Runtime(const folly::dynamic& v8Config, const std::shared_ptr<Logger>& logger) : V8Runtime() {
|
|
logger_ = logger;
|
|
isCacheEnabled_ = IsCacheEnabled(v8Config);
|
|
shouldProduceFullCache_ = ShouldProduceFullCache(v8Config);
|
|
shouldSetNoLazyFlag_ = ShouldSetNoLazyFlag(v8Config);
|
|
cacheDirectory_ = GetCacheDirectoryPath(v8Config);
|
|
cacheType_ = static_cast<CacheType>(v8Config.getDefault("CacheType", static_cast<int>(CacheType::NoCache)).getInt());
|
|
}
|
|
|
|
v8::ScriptCompiler::CachedData* V8Runtime::TryLoadCachedData(const std::string& path) {
|
|
long length = 0;
|
|
auto data = facebook::react::FileUtils::ReadBinary(path, length);
|
|
if (!data) {
|
|
return nullptr;
|
|
}
|
|
|
|
return new v8::ScriptCompiler::CachedData(reinterpret_cast<uint8_t*>(data), length, v8::ScriptCompiler::CachedData::BufferOwned);
|
|
}
|
|
|
|
void V8Runtime::PersistCachedData(std::unique_ptr<v8::ScriptCompiler::CachedData> cachedData, const std::string& path) {
|
|
if (!cachedData) {
|
|
if (react::ReactMarker::logTaggedMarker) {
|
|
react::ReactMarker::logMarker(react::ReactMarker::BYTECODE_CREATION_FAILED);
|
|
}
|
|
return;
|
|
}
|
|
|
|
int length = cachedData->length;
|
|
bool result = facebook::react::FileUtils::WriteBinary(path, cachedData->data, length);
|
|
|
|
if (!result) {
|
|
if (react::ReactMarker::logTaggedMarker) {
|
|
react::ReactMarker::logMarker(react::ReactMarker::BYTECODE_WRITE_FAILED);
|
|
}
|
|
}
|
|
}
|
|
|
|
v8::Local<v8::Script> V8Runtime::GetCompiledScriptFromCache(const v8::Local<v8::String> &sourceString, const std::string& sourceURL) {
|
|
std::size_t found = sourceURL.find_last_of("/");
|
|
std::string cacheFilePath = cacheDirectory_ + std::string("/") + sourceURL.substr(found + 1) + ".v8cache";
|
|
auto cacheData = TryLoadCachedData(cacheFilePath);
|
|
|
|
// No need to delete cacheData as ScriptCompiler::Source will take its ownership.
|
|
v8::ScriptCompiler::Source source(sourceString, cacheData);
|
|
|
|
v8::Local<v8::Script> script;
|
|
v8::Isolate *isolate = GetIsolate();
|
|
v8::Local<v8::Context> context(isolate->GetCurrentContext());
|
|
v8::TryCatch tc(isolate);
|
|
v8::ScriptCompiler::CompileOptions option = v8::ScriptCompiler::kNoCompileOptions;
|
|
|
|
if (shouldSetNoLazyFlag_) {
|
|
const char* lazy = "--nolazy";
|
|
v8::V8::SetFlagsFromString(lazy, strlen(lazy));
|
|
}
|
|
|
|
if (cacheData != nullptr) {
|
|
option = v8::ScriptCompiler::kConsumeCodeCache;
|
|
auto maybeScript = v8::ScriptCompiler::Compile(context, &source, option);
|
|
|
|
if (maybeScript.IsEmpty() || tc.HasCaught()) {
|
|
ReportException(&tc);
|
|
}
|
|
|
|
if (cacheData->rejected) {
|
|
remove(cacheFilePath.c_str());
|
|
}
|
|
|
|
script = maybeScript.ToLocalChecked();
|
|
}
|
|
else {
|
|
if (shouldProduceFullCache_) {
|
|
option = v8::ScriptCompiler::kProduceFullCodeCache;
|
|
}
|
|
else {
|
|
option = v8::ScriptCompiler::kProduceCodeCache;
|
|
}
|
|
auto maybeScript = v8::ScriptCompiler::Compile(context, &source, option);
|
|
|
|
if (maybeScript.IsEmpty() || tc.HasCaught()) {
|
|
ReportException(&tc);
|
|
}
|
|
|
|
script = maybeScript.ToLocalChecked();
|
|
|
|
// Don't create cache if the bundle is getting reloaded from dev server.
|
|
if (sourceURL.find("http://localhost:8081") == 0) {
|
|
return script;
|
|
}
|
|
|
|
v8::Local<v8::UnboundScript> unboundScript = script->GetUnboundScript();
|
|
|
|
// CreateCodeCache always create buffer with BufferPolicy::BufferOwned,
|
|
// We need to just free CachedData and it will take care of deleting underlying buffer.
|
|
std::unique_ptr<v8::ScriptCompiler::CachedData> cachedData{ v8::ScriptCompiler::CreateCodeCache(unboundScript) };
|
|
PersistCachedData(std::move(cachedData), cacheFilePath);
|
|
}
|
|
|
|
return script;
|
|
}
|
|
|
|
std::unique_ptr<jsi::Runtime> makeV8Runtime(const folly::dynamic& v8Config, const std::shared_ptr<Logger>& logger) {
|
|
return std::make_unique<V8Runtime>(v8Config, logger);
|
|
}
|
|
}} // namespace facebook::v8runtime
|