Reland "Add support to produce code cache after execute"
Adds new API function to request code cache. Earlier code cache was produced along with compile requests. This new API allows us to request code cache after executing. Also adds support in the code serializer to serialize after executing the script. Bug: chromium:783124,chromium:789694 Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng Change-Id: Id4e6a967e176e3e979dc4ccb9a37a353c70c3890 Reviewed-on: https://chromium-review.googlesource.com/797036 Reviewed-by: Yang Guo <yangguo@chromium.org> Commit-Queue: Mythri Alle <mythria@chromium.org> Cr-Commit-Position: refs/heads/master@{#49793}
This commit is contained in:
parent
e9c9322339
commit
dae20b0df6
@ -1576,6 +1576,14 @@ class V8_EXPORT ScriptCompiler {
|
||||
Local<String> arguments[], size_t context_extension_count,
|
||||
Local<Object> context_extensions[]);
|
||||
|
||||
/**
|
||||
* Creates and returns code cache for the specified unbound_script.
|
||||
* This will return nullptr if the script cannot be serialized. The
|
||||
* CachedData returned by this function should be owned by the caller.
|
||||
*/
|
||||
static CachedData* CreateCodeCache(Local<UnboundScript> unbound_script,
|
||||
Local<String> source);
|
||||
|
||||
private:
|
||||
static V8_WARN_UNUSED_RESULT MaybeLocal<UnboundScript> CompileUnboundInternal(
|
||||
Isolate* isolate, Source* source, CompileOptions options,
|
||||
|
20
src/api.cc
20
src/api.cc
@ -2660,6 +2660,26 @@ uint32_t ScriptCompiler::CachedDataVersionTag() {
|
||||
static_cast<uint32_t>(internal::CpuFeatures::SupportedFeatures())));
|
||||
}
|
||||
|
||||
ScriptCompiler::CachedData* ScriptCompiler::CreateCodeCache(
|
||||
Local<UnboundScript> unbound_script, Local<String> source) {
|
||||
i::Handle<i::SharedFunctionInfo> shared =
|
||||
i::Handle<i::SharedFunctionInfo>::cast(
|
||||
Utils::OpenHandle(*unbound_script));
|
||||
DCHECK(shared->is_toplevel());
|
||||
i::Handle<i::Script> script(i::Script::cast(shared->script()));
|
||||
i::Isolate* isolate = shared->GetIsolate();
|
||||
// TODO(7110): Enable serialization of Asm modules once the AsmWasmData is
|
||||
// context independent.
|
||||
if (script->ContainsAsmModule()) return nullptr;
|
||||
if (isolate->debug()->is_loaded()) return nullptr;
|
||||
i::ScriptData* script_data =
|
||||
i::CodeSerializer::Serialize(isolate, shared, Utils::OpenHandle(*source));
|
||||
CachedData* result = new CachedData(
|
||||
script_data->data(), script_data->length(), CachedData::BufferOwned);
|
||||
script_data->ReleaseDataOwnership();
|
||||
delete script_data;
|
||||
return result;
|
||||
}
|
||||
|
||||
MaybeLocal<Script> Script::Compile(Local<Context> context, Local<String> source,
|
||||
ScriptOrigin* origin) {
|
||||
|
@ -1177,15 +1177,6 @@ MaybeHandle<JSFunction> Compiler::GetFunctionFromEval(
|
||||
|
||||
namespace {
|
||||
|
||||
bool ContainsAsmModule(Handle<Script> script) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
SharedFunctionInfo::ScriptIterator iter(script);
|
||||
while (SharedFunctionInfo* info = iter.Next()) {
|
||||
if (info->HasAsmWasmData()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShouldProduceCodeCache(ScriptCompiler::CompileOptions options) {
|
||||
return options == ScriptCompiler::kProduceCodeCache ||
|
||||
options == ScriptCompiler::kProduceFullCodeCache;
|
||||
@ -1573,7 +1564,7 @@ MaybeHandle<SharedFunctionInfo> Compiler::GetSharedFunctionInfoForScript(
|
||||
compilation_cache->PutScript(source, context, language_mode, result,
|
||||
vector);
|
||||
if (ShouldProduceCodeCache(compile_options) &&
|
||||
!ContainsAsmModule(script)) {
|
||||
!script->ContainsAsmModule()) {
|
||||
compile_timer.set_producing_code_cache();
|
||||
|
||||
HistogramTimerScope histogram_timer(
|
||||
|
200
src/d8.cc
200
src/d8.cc
@ -506,6 +506,8 @@ std::vector<Worker*> Shell::workers_;
|
||||
std::vector<ExternalizedContents> Shell::externalized_contents_;
|
||||
base::LazyMutex Shell::isolate_status_lock_;
|
||||
std::map<v8::Isolate*, bool> Shell::isolate_status_;
|
||||
std::map<std::string, std::unique_ptr<ScriptCompiler::CachedData>>
|
||||
Shell::cached_code_map_;
|
||||
|
||||
Global<Context> Shell::evaluation_context_;
|
||||
ArrayBuffer::Allocator* Shell::array_buffer_allocator;
|
||||
@ -566,95 +568,37 @@ class BackgroundCompileThread : public base::Thread {
|
||||
std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask> task_;
|
||||
};
|
||||
|
||||
ScriptCompiler::CachedData* CompileForCachedData(
|
||||
Local<String> source, Local<Value> name,
|
||||
ScriptCompiler::CompileOptions compile_options) {
|
||||
int source_length = source->Length();
|
||||
uint16_t* source_buffer = new uint16_t[source_length];
|
||||
source->Write(source_buffer, 0, source_length);
|
||||
int name_length = 0;
|
||||
uint16_t* name_buffer = nullptr;
|
||||
if (name->IsString()) {
|
||||
Local<String> name_string = Local<String>::Cast(name);
|
||||
name_length = name_string->Length();
|
||||
name_buffer = new uint16_t[name_length];
|
||||
name_string->Write(name_buffer, 0, name_length);
|
||||
ScriptCompiler::CachedData* Shell::LookupCodeCache(Isolate* isolate,
|
||||
Local<Value> source) {
|
||||
CHECK(source->IsString());
|
||||
v8::String::Utf8Value key(isolate, source);
|
||||
DCHECK(*key);
|
||||
auto entry = cached_code_map_.find(*key);
|
||||
if (entry != cached_code_map_.end() && entry->second) {
|
||||
int length = entry->second->length;
|
||||
uint8_t* cache = new uint8_t[length];
|
||||
memcpy(cache, entry->second->data, length);
|
||||
ScriptCompiler::CachedData* cached_data = new ScriptCompiler::CachedData(
|
||||
cache, length, ScriptCompiler::CachedData::BufferOwned);
|
||||
return cached_data;
|
||||
}
|
||||
Isolate::CreateParams create_params;
|
||||
create_params.array_buffer_allocator = Shell::array_buffer_allocator;
|
||||
i::FLAG_hash_seed ^= 1337; // Use a different hash seed.
|
||||
Isolate* temp_isolate = Isolate::New(create_params);
|
||||
i::FLAG_hash_seed ^= 1337; // Restore old hash seed.
|
||||
temp_isolate->SetHostImportModuleDynamicallyCallback(
|
||||
Shell::HostImportModuleDynamically);
|
||||
temp_isolate->SetHostInitializeImportMetaObjectCallback(
|
||||
Shell::HostInitializeImportMetaObject);
|
||||
ScriptCompiler::CachedData* result = nullptr;
|
||||
{
|
||||
Isolate::Scope isolate_scope(temp_isolate);
|
||||
HandleScope handle_scope(temp_isolate);
|
||||
Context::Scope context_scope(Context::New(temp_isolate));
|
||||
Local<String> source_copy =
|
||||
v8::String::NewFromTwoByte(temp_isolate, source_buffer,
|
||||
v8::NewStringType::kNormal, source_length)
|
||||
.ToLocalChecked();
|
||||
Local<Value> name_copy;
|
||||
if (name_buffer) {
|
||||
name_copy =
|
||||
v8::String::NewFromTwoByte(temp_isolate, name_buffer,
|
||||
v8::NewStringType::kNormal, name_length)
|
||||
.ToLocalChecked();
|
||||
} else {
|
||||
name_copy = v8::Undefined(temp_isolate);
|
||||
}
|
||||
ScriptCompiler::Source script_source(source_copy, ScriptOrigin(name_copy));
|
||||
if (!ScriptCompiler::CompileUnboundScript(temp_isolate, &script_source,
|
||||
compile_options)
|
||||
.IsEmpty() &&
|
||||
script_source.GetCachedData()) {
|
||||
int length = script_source.GetCachedData()->length;
|
||||
uint8_t* cache = new uint8_t[length];
|
||||
memcpy(cache, script_source.GetCachedData()->data, length);
|
||||
result = new ScriptCompiler::CachedData(
|
||||
cache, length, ScriptCompiler::CachedData::BufferOwned);
|
||||
}
|
||||
}
|
||||
temp_isolate->Dispose();
|
||||
delete[] source_buffer;
|
||||
delete[] name_buffer;
|
||||
return result;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
// Compile a string within the current v8 context.
|
||||
MaybeLocal<Script> Shell::CompileString(
|
||||
Isolate* isolate, Local<String> source, Local<Value> name,
|
||||
ScriptCompiler::CompileOptions compile_options) {
|
||||
Local<Context> context(isolate->GetCurrentContext());
|
||||
ScriptOrigin origin(name);
|
||||
if (compile_options == ScriptCompiler::kNoCompileOptions) {
|
||||
ScriptCompiler::Source script_source(source, origin);
|
||||
return ScriptCompiler::Compile(context, &script_source, compile_options);
|
||||
}
|
||||
|
||||
ScriptCompiler::CachedData* data =
|
||||
CompileForCachedData(source, name, compile_options);
|
||||
ScriptCompiler::Source cached_source(source, origin, data);
|
||||
if (compile_options == ScriptCompiler::kProduceCodeCache) {
|
||||
compile_options = ScriptCompiler::kConsumeCodeCache;
|
||||
} else if (compile_options == ScriptCompiler::kProduceParserCache) {
|
||||
compile_options = ScriptCompiler::kConsumeParserCache;
|
||||
} else {
|
||||
DCHECK(false); // A new compile option?
|
||||
}
|
||||
if (data == nullptr) compile_options = ScriptCompiler::kNoCompileOptions;
|
||||
MaybeLocal<Script> result =
|
||||
ScriptCompiler::Compile(context, &cached_source, compile_options);
|
||||
CHECK(data == nullptr || !data->rejected);
|
||||
return result;
|
||||
void Shell::StoreInCodeCache(Isolate* isolate, Local<Value> source,
|
||||
const ScriptCompiler::CachedData* cache_data) {
|
||||
CHECK(source->IsString());
|
||||
if (cache_data == nullptr) return;
|
||||
v8::String::Utf8Value key(isolate, source);
|
||||
DCHECK(*key);
|
||||
int length = cache_data->length;
|
||||
uint8_t* cache = new uint8_t[length];
|
||||
memcpy(cache, cache_data->data, length);
|
||||
cached_code_map_[*key] = std::unique_ptr<ScriptCompiler::CachedData>(
|
||||
new ScriptCompiler::CachedData(cache, length,
|
||||
ScriptCompiler::CachedData::BufferOwned));
|
||||
}
|
||||
|
||||
|
||||
// Executes a string within the current v8 context.
|
||||
bool Shell::ExecuteString(Isolate* isolate, Local<String> source,
|
||||
Local<Value> name, bool print_result,
|
||||
@ -671,7 +615,23 @@ bool Shell::ExecuteString(Isolate* isolate, Local<String> source,
|
||||
Local<Context>::New(isolate, data->realms_[data->realm_current_]);
|
||||
Context::Scope context_scope(realm);
|
||||
MaybeLocal<Script> maybe_script;
|
||||
if (options.stress_background_compile) {
|
||||
Local<Context> context(isolate->GetCurrentContext());
|
||||
ScriptOrigin origin(name);
|
||||
|
||||
if (options.compile_options == ScriptCompiler::kConsumeCodeCache) {
|
||||
ScriptCompiler::CachedData* cached_code =
|
||||
LookupCodeCache(isolate, source);
|
||||
if (cached_code != nullptr) {
|
||||
ScriptCompiler::Source script_source(source, origin, cached_code);
|
||||
maybe_script = ScriptCompiler::Compile(context, &script_source,
|
||||
options.compile_options);
|
||||
CHECK(!cached_code->rejected);
|
||||
} else {
|
||||
ScriptCompiler::Source script_source(source, origin);
|
||||
maybe_script = ScriptCompiler::Compile(
|
||||
context, &script_source, ScriptCompiler::kNoCompileOptions);
|
||||
}
|
||||
} else if (options.stress_background_compile) {
|
||||
// Start a background thread compiling the script.
|
||||
BackgroundCompileThread background_compile_thread(isolate, source);
|
||||
background_compile_thread.Start();
|
||||
@ -679,18 +639,26 @@ bool Shell::ExecuteString(Isolate* isolate, Local<String> source,
|
||||
// In parallel, compile on the main thread to flush out any data races.
|
||||
{
|
||||
TryCatch ignore_try_catch(isolate);
|
||||
Shell::CompileString(isolate, source, name, options.compile_options);
|
||||
ScriptCompiler::Source script_source(source, origin);
|
||||
USE(ScriptCompiler::Compile(context, &script_source,
|
||||
ScriptCompiler::kNoCompileOptions));
|
||||
}
|
||||
|
||||
// Join with background thread and finalize compilation.
|
||||
background_compile_thread.Join();
|
||||
ScriptOrigin origin(name);
|
||||
maybe_script = v8::ScriptCompiler::Compile(
|
||||
isolate->GetCurrentContext(),
|
||||
background_compile_thread.streamed_source(), source, origin);
|
||||
context, background_compile_thread.streamed_source(), source, origin);
|
||||
} else {
|
||||
ScriptCompiler::Source script_source(source, origin);
|
||||
ScriptCompiler::CompileOptions compile_options =
|
||||
options.cache_code_after_execute ? ScriptCompiler::kNoCompileOptions
|
||||
: options.compile_options;
|
||||
maybe_script =
|
||||
Shell::CompileString(isolate, source, name, options.compile_options);
|
||||
ScriptCompiler::Compile(context, &script_source, compile_options);
|
||||
if (compile_options == ScriptCompiler::kProduceCodeCache ||
|
||||
compile_options == ScriptCompiler::kProduceParserCache) {
|
||||
StoreInCodeCache(isolate, source, script_source.GetCachedData());
|
||||
}
|
||||
}
|
||||
|
||||
Local<Script> script;
|
||||
@ -701,6 +669,14 @@ bool Shell::ExecuteString(Isolate* isolate, Local<String> source,
|
||||
}
|
||||
|
||||
maybe_result = script->Run(realm);
|
||||
if (options.compile_options == ScriptCompiler::kProduceCodeCache &&
|
||||
options.cache_code_after_execute) {
|
||||
// Serialize and store it in memory for the next execution.
|
||||
ScriptCompiler::CachedData* cached_data =
|
||||
ScriptCompiler::CreateCodeCache(script->GetUnboundScript(), source);
|
||||
StoreInCodeCache(isolate, source, cached_data);
|
||||
delete cached_data;
|
||||
}
|
||||
if (!EmptyMessageQueues(isolate)) success = false;
|
||||
data->realm_current_ = data->realm_switch_;
|
||||
}
|
||||
@ -2868,6 +2844,9 @@ bool Shell::SetOptions(int argc, char* argv[]) {
|
||||
options.compile_options = v8::ScriptCompiler::kProduceParserCache;
|
||||
} else if (strncmp(value, "=none", 6) == 0) {
|
||||
options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
|
||||
} else if (strncmp(value, "=after-execute", 10) == 0) {
|
||||
options.compile_options = v8::ScriptCompiler::kProduceCodeCache;
|
||||
options.cache_code_after_execute = true;
|
||||
} else {
|
||||
printf("Unknown option to --cache.\n");
|
||||
return false;
|
||||
@ -3417,6 +3396,47 @@ int Shell::Main(int argc, char* argv[]) {
|
||||
bool last_run = i == options.stress_runs - 1;
|
||||
result = RunMain(isolate, argc, argv, last_run);
|
||||
}
|
||||
} else if (options.compile_options ==
|
||||
v8::ScriptCompiler::kProduceCodeCache ||
|
||||
options.compile_options ==
|
||||
v8::ScriptCompiler::kProduceParserCache) {
|
||||
printf("============ Run: Produce code cache ============\n");
|
||||
// First run to produce the cache
|
||||
result = RunMain(isolate, argc, argv, false);
|
||||
|
||||
// Change the options to consume cache
|
||||
if (options.compile_options == v8::ScriptCompiler::kProduceCodeCache) {
|
||||
options.compile_options = v8::ScriptCompiler::kConsumeCodeCache;
|
||||
} else if (options.compile_options ==
|
||||
v8::ScriptCompiler::kProduceParserCache) {
|
||||
options.compile_options = v8::ScriptCompiler::kConsumeParserCache;
|
||||
} else {
|
||||
// We only expect ProduceCodeCache or ProduceParserCache here.
|
||||
// compile_options cannot be NoCompileOptions.
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
printf("============ Run: Consume code cache ============\n");
|
||||
// Second run to consume the cache in new isolate
|
||||
Isolate::CreateParams create_params;
|
||||
create_params.array_buffer_allocator = Shell::array_buffer_allocator;
|
||||
i::FLAG_hash_seed ^= 1337; // Use a different hash seed.
|
||||
Isolate* isolate2 = Isolate::New(create_params);
|
||||
i::FLAG_hash_seed ^= 1337; // Restore old hash seed.
|
||||
isolate2->SetHostImportModuleDynamicallyCallback(
|
||||
Shell::HostImportModuleDynamically);
|
||||
isolate2->SetHostInitializeImportMetaObjectCallback(
|
||||
Shell::HostInitializeImportMetaObject);
|
||||
{
|
||||
D8Console console(isolate2);
|
||||
debug::SetConsoleDelegate(isolate2, &console);
|
||||
PerIsolateData data(isolate2);
|
||||
Isolate::Scope isolate_scope(isolate2);
|
||||
|
||||
result = RunMain(isolate2, argc, argv, true);
|
||||
}
|
||||
cached_code_map_.clear();
|
||||
isolate2->Dispose();
|
||||
} else {
|
||||
bool last_run = true;
|
||||
result = RunMain(isolate, argc, argv, last_run);
|
||||
|
11
src/d8.h
11
src/d8.h
@ -296,6 +296,7 @@ class ShellOptions {
|
||||
num_isolates(1),
|
||||
compile_options(v8::ScriptCompiler::kNoCompileOptions),
|
||||
stress_background_compile(false),
|
||||
cache_code_after_execute(false),
|
||||
isolate_sources(nullptr),
|
||||
icu_data_file(nullptr),
|
||||
natives_blob(nullptr),
|
||||
@ -330,6 +331,7 @@ class ShellOptions {
|
||||
int num_isolates;
|
||||
v8::ScriptCompiler::CompileOptions compile_options;
|
||||
bool stress_background_compile;
|
||||
bool cache_code_after_execute;
|
||||
SourceGroup* isolate_sources;
|
||||
const char* icu_data_file;
|
||||
const char* natives_blob;
|
||||
@ -346,9 +348,6 @@ class ShellOptions {
|
||||
|
||||
class Shell : public i::AllStatic {
|
||||
public:
|
||||
static MaybeLocal<Script> CompileString(
|
||||
Isolate* isolate, Local<String> source, Local<Value> name,
|
||||
v8::ScriptCompiler::CompileOptions compile_options);
|
||||
static bool ExecuteString(Isolate* isolate, Local<String> source,
|
||||
Local<Value> name, bool print_result,
|
||||
bool report_exceptions);
|
||||
@ -506,10 +505,16 @@ class Shell : public i::AllStatic {
|
||||
int index);
|
||||
static MaybeLocal<Module> FetchModuleTree(v8::Local<v8::Context> context,
|
||||
const std::string& file_name);
|
||||
static ScriptCompiler::CachedData* LookupCodeCache(Isolate* isolate,
|
||||
Local<Value> name);
|
||||
static void StoreInCodeCache(Isolate* isolate, Local<Value> name,
|
||||
const ScriptCompiler::CachedData* data);
|
||||
// We may have multiple isolates running concurrently, so the access to
|
||||
// the isolate_status_ needs to be concurrency-safe.
|
||||
static base::LazyMutex isolate_status_lock_;
|
||||
static std::map<Isolate*, bool> isolate_status_;
|
||||
static std::map<std::string, std::unique_ptr<ScriptCompiler::CachedData>>
|
||||
cached_code_map_;
|
||||
};
|
||||
|
||||
|
||||
|
@ -13264,6 +13264,15 @@ bool Script::GetPositionInfo(Handle<Script> script, int position,
|
||||
|
||||
bool Script::IsUserJavaScript() { return type() == Script::TYPE_NORMAL; }
|
||||
|
||||
bool Script::ContainsAsmModule() {
|
||||
DisallowHeapAllocation no_gc;
|
||||
SharedFunctionInfo::ScriptIterator iter(Handle<Script>(this));
|
||||
while (SharedFunctionInfo* info = iter.Next()) {
|
||||
if (info->HasAsmWasmData()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool GetPositionInfoSlow(const Script* script, int position,
|
||||
Script::PositionInfo* info) {
|
||||
|
@ -635,6 +635,14 @@ ByteArray* BytecodeArray::SourcePositionTable() {
|
||||
->source_position_table();
|
||||
}
|
||||
|
||||
void BytecodeArray::ClearFrameCacheFromSourcePositionTable() {
|
||||
Object* maybe_table = source_position_table();
|
||||
if (maybe_table->IsByteArray()) return;
|
||||
DCHECK(maybe_table->IsSourcePositionTableWithFrameCache());
|
||||
set_source_position_table(SourcePositionTableWithFrameCache::cast(maybe_table)
|
||||
->source_position_table());
|
||||
}
|
||||
|
||||
int BytecodeArray::BytecodeArraySize() { return SizeFor(this->length()); }
|
||||
|
||||
int BytecodeArray::SizeIncludingMetadata() {
|
||||
|
@ -791,6 +791,7 @@ class BytecodeArray : public FixedArrayBase {
|
||||
DECL_ACCESSORS(source_position_table, Object)
|
||||
|
||||
inline ByteArray* SourcePositionTable();
|
||||
inline void ClearFrameCacheFromSourcePositionTable();
|
||||
|
||||
DECL_CAST(BytecodeArray)
|
||||
|
||||
|
@ -119,6 +119,9 @@ class Script : public Struct {
|
||||
// Retrieve source position from where eval was called.
|
||||
int GetEvalPosition();
|
||||
|
||||
// Check if the script contains any Asm modules.
|
||||
bool ContainsAsmModule();
|
||||
|
||||
// Init line_ends array with source code positions of line ends.
|
||||
static void InitLineEnds(Handle<Script> script);
|
||||
|
||||
|
@ -106,12 +106,37 @@ void CodeSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
|
||||
}
|
||||
|
||||
if (obj->IsScript()) {
|
||||
Script* script_obj = Script::cast(obj);
|
||||
DCHECK_NE(script_obj->compilation_type(), Script::COMPILATION_TYPE_EVAL);
|
||||
// Wrapper object is a context-dependent JSValue. Reset it here.
|
||||
Script::cast(obj)->set_wrapper(isolate()->heap()->undefined_value());
|
||||
script_obj->set_wrapper(isolate()->heap()->undefined_value());
|
||||
// We want to differentiate between undefined and uninitialized_symbol for
|
||||
// context_data for now. It is hack to allow debugging for scripts that are
|
||||
// included as a part of custom snapshot. (see debug::Script::IsEmbedded())
|
||||
Object* context_data = script_obj->context_data();
|
||||
if (context_data != isolate()->heap()->undefined_value() &&
|
||||
context_data != isolate()->heap()->uninitialized_symbol()) {
|
||||
script_obj->set_context_data(isolate()->heap()->undefined_value());
|
||||
}
|
||||
// We don't want to serialize host options to avoid serializing unnecessary
|
||||
// object graph.
|
||||
FixedArray* host_options = script_obj->host_defined_options();
|
||||
script_obj->set_host_defined_options(
|
||||
isolate()->heap()->empty_fixed_array());
|
||||
SerializeGeneric(obj, how_to_code, where_to_point);
|
||||
script_obj->set_host_defined_options(host_options);
|
||||
script_obj->set_context_data(context_data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj->IsSharedFunctionInfo()) {
|
||||
SharedFunctionInfo* sfi = SharedFunctionInfo::cast(obj);
|
||||
// TODO(7110): Enable serializing of Asm modules once the AsmWasmData
|
||||
// is context independent.
|
||||
DCHECK(!sfi->IsApiFunction() && !sfi->HasAsmWasmData());
|
||||
// Do not serialize when a debugger is active.
|
||||
DCHECK(sfi->debug_info()->IsSmi());
|
||||
|
||||
// Mark SFI to indicate whether the code is cached.
|
||||
bool was_deserialized = sfi->deserialized();
|
||||
sfi->set_deserialized(sfi->is_compiled());
|
||||
@ -120,6 +145,11 @@ void CodeSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj->IsBytecodeArray()) {
|
||||
// Clear the stack frame cache if present
|
||||
BytecodeArray::cast(obj)->ClearFrameCacheFromSourcePositionTable();
|
||||
}
|
||||
|
||||
// Past this point we should not see any (context-specific) maps anymore.
|
||||
CHECK(!obj->IsMap());
|
||||
// There should be no references to the global object embedded.
|
||||
|
@ -234,6 +234,13 @@ HeapObject* Deserializer<AllocatorT>::PostProcessNewObject(HeapObject* obj,
|
||||
void* backing_store = off_heap_backing_stores_[store_index->value()];
|
||||
fta->set_external_pointer(backing_store);
|
||||
}
|
||||
} else if (obj->IsBytecodeArray()) {
|
||||
// TODO(mythria): Remove these once we store the default values for these
|
||||
// fields in the serializer.
|
||||
BytecodeArray* bytecode_array = BytecodeArray::cast(obj);
|
||||
bytecode_array->set_interrupt_budget(
|
||||
interpreter::Interpreter::kInterruptBudget);
|
||||
bytecode_array->set_osr_loop_nesting_level(0);
|
||||
}
|
||||
// Check alignment.
|
||||
DCHECK_EQ(0, Heap::GetFillToAlign(obj->address(), obj->RequiredAlignment()));
|
||||
|
@ -57,6 +57,8 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
enum CodeCacheType { kLazy, kEager, kAfterExecute };
|
||||
|
||||
void DisableLazyDeserialization() {
|
||||
// UNINITIALIZED tests do not set up the isolate sufficiently for lazy
|
||||
// deserialization to work.
|
||||
@ -1838,8 +1840,8 @@ static void SerializerCodeEventListener(const v8::JitCodeEvent* event) {
|
||||
}
|
||||
}
|
||||
|
||||
v8::ScriptCompiler::CachedData* ProduceCache(const char* source,
|
||||
bool eager = false) {
|
||||
v8::ScriptCompiler::CachedData* ProduceCache(
|
||||
const char* source, CodeCacheType cacheType = CodeCacheType::kLazy) {
|
||||
v8::ScriptCompiler::CachedData* cache;
|
||||
v8::Isolate::CreateParams create_params;
|
||||
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
||||
@ -1853,19 +1855,23 @@ v8::ScriptCompiler::CachedData* ProduceCache(const char* source,
|
||||
v8::Local<v8::String> source_str = v8_str(source);
|
||||
v8::ScriptOrigin origin(v8_str("test"));
|
||||
v8::ScriptCompiler::Source source(source_str, origin);
|
||||
v8::ScriptCompiler::CompileOptions options =
|
||||
eager ? v8::ScriptCompiler::kProduceFullCodeCache
|
||||
: v8::ScriptCompiler::kProduceCodeCache;
|
||||
v8::ScriptCompiler::CompileOptions options;
|
||||
switch (cacheType) {
|
||||
case CodeCacheType::kLazy:
|
||||
options = v8::ScriptCompiler::kProduceCodeCache;
|
||||
break;
|
||||
case CodeCacheType::kEager:
|
||||
options = v8::ScriptCompiler::kProduceFullCodeCache;
|
||||
break;
|
||||
case CodeCacheType::kAfterExecute:
|
||||
options = v8::ScriptCompiler::kNoCompileOptions;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
v8::Local<v8::UnboundScript> script =
|
||||
v8::ScriptCompiler::CompileUnboundScript(isolate1, &source, options)
|
||||
.ToLocalChecked();
|
||||
const v8::ScriptCompiler::CachedData* data = source.GetCachedData();
|
||||
CHECK(data);
|
||||
// Persist cached data.
|
||||
uint8_t* buffer = NewArray<uint8_t>(data->length);
|
||||
MemCopy(buffer, data->data, data->length);
|
||||
cache = new v8::ScriptCompiler::CachedData(
|
||||
buffer, data->length, v8::ScriptCompiler::CachedData::BufferOwned);
|
||||
|
||||
v8::Local<v8::Value> result = script->BindToCurrentContext()
|
||||
->Run(isolate1->GetCurrentContext())
|
||||
@ -1874,6 +1880,18 @@ v8::ScriptCompiler::CachedData* ProduceCache(const char* source,
|
||||
result->ToString(isolate1->GetCurrentContext()).ToLocalChecked();
|
||||
CHECK(result_string->Equals(isolate1->GetCurrentContext(), v8_str("abcdef"))
|
||||
.FromJust());
|
||||
|
||||
if (cacheType == CodeCacheType::kAfterExecute) {
|
||||
cache = ScriptCompiler::CreateCodeCache(script, source_str);
|
||||
} else {
|
||||
const ScriptCompiler::CachedData* data = source.GetCachedData();
|
||||
CHECK(data);
|
||||
uint8_t* buffer = NewArray<uint8_t>(data->length);
|
||||
MemCopy(buffer, data->data, data->length);
|
||||
cache = new v8::ScriptCompiler::CachedData(
|
||||
buffer, data->length, v8::ScriptCompiler::CachedData::BufferOwned);
|
||||
}
|
||||
CHECK(cache);
|
||||
}
|
||||
isolate1->Dispose();
|
||||
return cache;
|
||||
@ -1936,7 +1954,8 @@ TEST(CodeSerializerIsolatesEager) {
|
||||
" }"
|
||||
"}"
|
||||
"f()() + 'def'";
|
||||
v8::ScriptCompiler::CachedData* cache = ProduceCache(source, true);
|
||||
v8::ScriptCompiler::CachedData* cache =
|
||||
ProduceCache(source, CodeCacheType::kEager);
|
||||
|
||||
v8::Isolate::CreateParams create_params;
|
||||
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
||||
@ -1974,6 +1993,68 @@ TEST(CodeSerializerIsolatesEager) {
|
||||
isolate2->Dispose();
|
||||
}
|
||||
|
||||
TEST(CodeSerializerAfterExecute) {
|
||||
// We test that no compilations happen when running this code. Forcing
|
||||
// to always optimize breaks this test.
|
||||
bool prev_opt_value = FLAG_opt;
|
||||
bool prev_always_opt_value = FLAG_always_opt;
|
||||
FLAG_always_opt = false;
|
||||
FLAG_opt = false;
|
||||
const char* source = "function f() { return 'abc'; }; f() + 'def'";
|
||||
v8::ScriptCompiler::CachedData* cache =
|
||||
ProduceCache(source, CodeCacheType::kAfterExecute);
|
||||
|
||||
v8::Isolate::CreateParams create_params;
|
||||
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
||||
v8::Isolate* isolate2 = v8::Isolate::New(create_params);
|
||||
|
||||
{
|
||||
v8::Isolate::Scope iscope(isolate2);
|
||||
v8::HandleScope scope(isolate2);
|
||||
v8::Local<v8::Context> context = v8::Context::New(isolate2);
|
||||
v8::Context::Scope context_scope(context);
|
||||
|
||||
v8::Local<v8::String> source_str = v8_str(source);
|
||||
v8::ScriptOrigin origin(v8_str("test"));
|
||||
v8::ScriptCompiler::Source source(source_str, origin, cache);
|
||||
v8::Local<v8::UnboundScript> script;
|
||||
{
|
||||
DisallowCompilation no_compile_expected(
|
||||
reinterpret_cast<Isolate*>(isolate2));
|
||||
script = v8::ScriptCompiler::CompileUnboundScript(
|
||||
isolate2, &source, v8::ScriptCompiler::kConsumeCodeCache)
|
||||
.ToLocalChecked();
|
||||
}
|
||||
CHECK(!cache->rejected);
|
||||
CheckDeserializedFlag(script);
|
||||
|
||||
Handle<SharedFunctionInfo> sfi = v8::Utils::OpenHandle(*script);
|
||||
CHECK(sfi->HasBytecodeArray());
|
||||
BytecodeArray* bytecode = sfi->bytecode_array();
|
||||
CHECK_EQ(bytecode->interrupt_budget(),
|
||||
interpreter::Interpreter::kInterruptBudget);
|
||||
CHECK_EQ(bytecode->osr_loop_nesting_level(), 0);
|
||||
|
||||
{
|
||||
DisallowCompilation no_compile_expected(
|
||||
reinterpret_cast<Isolate*>(isolate2));
|
||||
v8::Local<v8::Value> result = script->BindToCurrentContext()
|
||||
->Run(isolate2->GetCurrentContext())
|
||||
.ToLocalChecked();
|
||||
v8::Local<v8::String> result_string =
|
||||
result->ToString(isolate2->GetCurrentContext()).ToLocalChecked();
|
||||
CHECK(
|
||||
result_string->Equals(isolate2->GetCurrentContext(), v8_str("abcdef"))
|
||||
.FromJust());
|
||||
}
|
||||
}
|
||||
isolate2->Dispose();
|
||||
|
||||
// Restore the flags.
|
||||
FLAG_always_opt = prev_always_opt_value;
|
||||
FLAG_opt = prev_opt_value;
|
||||
}
|
||||
|
||||
TEST(CodeSerializerFlagChange) {
|
||||
const char* source = "function f() { return 'abc'; }; f() + 'def'";
|
||||
v8::ScriptCompiler::CachedData* cache = ProduceCache(source);
|
||||
|
15
test/mjsunit/serialize-after-execute.js
Normal file
15
test/mjsunit/serialize-after-execute.js
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright 2017 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --cache=after-execute
|
||||
|
||||
function g() {
|
||||
function h() {
|
||||
function k() { return 0; };
|
||||
return k;
|
||||
}
|
||||
return h();
|
||||
}
|
||||
|
||||
g();
|
Loading…
Reference in New Issue
Block a user