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
Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng
Change-Id: Id7b972a2b4c8dcf7a6d9f5ea210890ae968320bd
Reviewed-on: https://chromium-review.googlesource.com/781767
Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Commit-Queue: Mythri Alle <mythria@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49717}
This commit is contained in:
Mythri 2017-11-28 17:22:33 +00:00 committed by Commit Bot
parent f043bf8a57
commit 5d4a090377
13 changed files with 314 additions and 117 deletions

View File

@ -1576,6 +1576,14 @@ class V8_EXPORT ScriptCompiler {
Local<String> arguments[], size_t context_extension_count, Local<String> arguments[], size_t context_extension_count,
Local<Object> context_extensions[]); 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: private:
static V8_WARN_UNUSED_RESULT MaybeLocal<UnboundScript> CompileUnboundInternal( static V8_WARN_UNUSED_RESULT MaybeLocal<UnboundScript> CompileUnboundInternal(
Isolate* isolate, Source* source, CompileOptions options, Isolate* isolate, Source* source, CompileOptions options,

View File

@ -2660,6 +2660,26 @@ uint32_t ScriptCompiler::CachedDataVersionTag() {
static_cast<uint32_t>(internal::CpuFeatures::SupportedFeatures()))); 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, MaybeLocal<Script> Script::Compile(Local<Context> context, Local<String> source,
ScriptOrigin* origin) { ScriptOrigin* origin) {

View File

@ -1176,15 +1176,6 @@ MaybeHandle<JSFunction> Compiler::GetFunctionFromEval(
namespace { 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) { bool ShouldProduceCodeCache(ScriptCompiler::CompileOptions options) {
return options == ScriptCompiler::kProduceCodeCache || return options == ScriptCompiler::kProduceCodeCache ||
options == ScriptCompiler::kProduceFullCodeCache; options == ScriptCompiler::kProduceFullCodeCache;
@ -1572,7 +1563,7 @@ MaybeHandle<SharedFunctionInfo> Compiler::GetSharedFunctionInfoForScript(
compilation_cache->PutScript(source, context, language_mode, result, compilation_cache->PutScript(source, context, language_mode, result,
vector); vector);
if (ShouldProduceCodeCache(compile_options) && if (ShouldProduceCodeCache(compile_options) &&
!ContainsAsmModule(script)) { !script->ContainsAsmModule()) {
compile_timer.set_producing_code_cache(); compile_timer.set_producing_code_cache();
HistogramTimerScope histogram_timer( HistogramTimerScope histogram_timer(

199
src/d8.cc
View File

@ -506,6 +506,8 @@ std::vector<Worker*> Shell::workers_;
std::vector<ExternalizedContents> Shell::externalized_contents_; std::vector<ExternalizedContents> Shell::externalized_contents_;
base::LazyMutex Shell::isolate_status_lock_; base::LazyMutex Shell::isolate_status_lock_;
std::map<v8::Isolate*, bool> Shell::isolate_status_; 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_; Global<Context> Shell::evaluation_context_;
ArrayBuffer::Allocator* Shell::array_buffer_allocator; ArrayBuffer::Allocator* Shell::array_buffer_allocator;
@ -566,95 +568,37 @@ class BackgroundCompileThread : public base::Thread {
std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask> task_; std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask> task_;
}; };
ScriptCompiler::CachedData* CompileForCachedData( ScriptCompiler::CachedData* Shell::LookupCodeCache(Isolate* isolate,
Local<String> source, Local<Value> name, Local<Value> name) {
ScriptCompiler::CompileOptions compile_options) { CHECK(name->IsString());
int source_length = source->Length(); v8::String::Utf8Value key(isolate, name);
uint16_t* source_buffer = new uint16_t[source_length]; DCHECK(*key);
source->Write(source_buffer, 0, source_length); auto entry = cached_code_map_.find(*key);
int name_length = 0; if (entry != cached_code_map_.end() && entry->second) {
uint16_t* name_buffer = nullptr; int length = entry->second->length;
if (name->IsString()) { uint8_t* cache = new uint8_t[length];
Local<String> name_string = Local<String>::Cast(name); memcpy(cache, entry->second->data, length);
name_length = name_string->Length(); ScriptCompiler::CachedData* cached_data = new ScriptCompiler::CachedData(
name_buffer = new uint16_t[name_length]; cache, length, ScriptCompiler::CachedData::BufferOwned);
name_string->Write(name_buffer, 0, name_length); return cached_data;
} }
Isolate::CreateParams create_params; return nullptr;
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;
} }
void Shell::StoreInCodeCache(Isolate* isolate, Local<Value> name,
// Compile a string within the current v8 context. const ScriptCompiler::CachedData* cache_data) {
MaybeLocal<Script> Shell::CompileString( CHECK(name->IsString());
Isolate* isolate, Local<String> source, Local<Value> name, if (cache_data == nullptr) return;
ScriptCompiler::CompileOptions compile_options) { v8::String::Utf8Value key(isolate, name);
Local<Context> context(isolate->GetCurrentContext()); DCHECK(*key);
ScriptOrigin origin(name); int length = cache_data->length;
if (compile_options == ScriptCompiler::kNoCompileOptions) { uint8_t* cache = new uint8_t[length];
ScriptCompiler::Source script_source(source, origin); memcpy(cache, cache_data->data, length);
return ScriptCompiler::Compile(context, &script_source, compile_options); cached_code_map_[*key] = std::unique_ptr<ScriptCompiler::CachedData>(
} new ScriptCompiler::CachedData(cache, length,
ScriptCompiler::CachedData::BufferOwned));
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;
} }
// Executes a string within the current v8 context. // Executes a string within the current v8 context.
bool Shell::ExecuteString(Isolate* isolate, Local<String> source, bool Shell::ExecuteString(Isolate* isolate, Local<String> source,
Local<Value> name, bool print_result, Local<Value> name, bool print_result,
@ -671,7 +615,22 @@ bool Shell::ExecuteString(Isolate* isolate, Local<String> source,
Local<Context>::New(isolate, data->realms_[data->realm_current_]); Local<Context>::New(isolate, data->realms_[data->realm_current_]);
Context::Scope context_scope(realm); Context::Scope context_scope(realm);
MaybeLocal<Script> maybe_script; 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, name);
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. // Start a background thread compiling the script.
BackgroundCompileThread background_compile_thread(isolate, source); BackgroundCompileThread background_compile_thread(isolate, source);
background_compile_thread.Start(); background_compile_thread.Start();
@ -679,18 +638,26 @@ bool Shell::ExecuteString(Isolate* isolate, Local<String> source,
// In parallel, compile on the main thread to flush out any data races. // In parallel, compile on the main thread to flush out any data races.
{ {
TryCatch ignore_try_catch(isolate); 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. // Join with background thread and finalize compilation.
background_compile_thread.Join(); background_compile_thread.Join();
ScriptOrigin origin(name);
maybe_script = v8::ScriptCompiler::Compile( maybe_script = v8::ScriptCompiler::Compile(
isolate->GetCurrentContext(), context, background_compile_thread.streamed_source(), source, origin);
background_compile_thread.streamed_source(), source, origin);
} else { } else {
ScriptCompiler::Source script_source(source, origin);
ScriptCompiler::CompileOptions compile_options =
options.cache_code_after_execute ? ScriptCompiler::kNoCompileOptions
: options.compile_options;
maybe_script = 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, name, script_source.GetCachedData());
}
} }
Local<Script> script; Local<Script> script;
@ -701,6 +668,14 @@ bool Shell::ExecuteString(Isolate* isolate, Local<String> source,
} }
maybe_result = script->Run(realm); 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, name, cached_data);
delete cached_data;
}
if (!EmptyMessageQueues(isolate)) success = false; if (!EmptyMessageQueues(isolate)) success = false;
data->realm_current_ = data->realm_switch_; data->realm_current_ = data->realm_switch_;
} }
@ -2868,6 +2843,9 @@ bool Shell::SetOptions(int argc, char* argv[]) {
options.compile_options = v8::ScriptCompiler::kProduceParserCache; options.compile_options = v8::ScriptCompiler::kProduceParserCache;
} else if (strncmp(value, "=none", 6) == 0) { } else if (strncmp(value, "=none", 6) == 0) {
options.compile_options = v8::ScriptCompiler::kNoCompileOptions; 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 { } else {
printf("Unknown option to --cache.\n"); printf("Unknown option to --cache.\n");
return false; return false;
@ -3413,6 +3391,47 @@ int Shell::Main(int argc, char* argv[]) {
bool last_run = i == options.stress_runs - 1; bool last_run = i == options.stress_runs - 1;
result = RunMain(isolate, argc, argv, last_run); 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, false);
}
cached_code_map_.clear();
isolate2->Dispose();
} else { } else {
bool last_run = true; bool last_run = true;
result = RunMain(isolate, argc, argv, last_run); result = RunMain(isolate, argc, argv, last_run);

View File

@ -296,6 +296,7 @@ class ShellOptions {
num_isolates(1), num_isolates(1),
compile_options(v8::ScriptCompiler::kNoCompileOptions), compile_options(v8::ScriptCompiler::kNoCompileOptions),
stress_background_compile(false), stress_background_compile(false),
cache_code_after_execute(false),
isolate_sources(nullptr), isolate_sources(nullptr),
icu_data_file(nullptr), icu_data_file(nullptr),
natives_blob(nullptr), natives_blob(nullptr),
@ -329,6 +330,7 @@ class ShellOptions {
int num_isolates; int num_isolates;
v8::ScriptCompiler::CompileOptions compile_options; v8::ScriptCompiler::CompileOptions compile_options;
bool stress_background_compile; bool stress_background_compile;
bool cache_code_after_execute;
SourceGroup* isolate_sources; SourceGroup* isolate_sources;
const char* icu_data_file; const char* icu_data_file;
const char* natives_blob; const char* natives_blob;
@ -344,9 +346,6 @@ class ShellOptions {
class Shell : public i::AllStatic { class Shell : public i::AllStatic {
public: 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, static bool ExecuteString(Isolate* isolate, Local<String> source,
Local<Value> name, bool print_result, Local<Value> name, bool print_result,
bool report_exceptions); bool report_exceptions);
@ -504,10 +503,16 @@ class Shell : public i::AllStatic {
int index); int index);
static MaybeLocal<Module> FetchModuleTree(v8::Local<v8::Context> context, static MaybeLocal<Module> FetchModuleTree(v8::Local<v8::Context> context,
const std::string& file_name); 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 // We may have multiple isolates running concurrently, so the access to
// the isolate_status_ needs to be concurrency-safe. // the isolate_status_ needs to be concurrency-safe.
static base::LazyMutex isolate_status_lock_; static base::LazyMutex isolate_status_lock_;
static std::map<Isolate*, bool> isolate_status_; static std::map<Isolate*, bool> isolate_status_;
static std::map<std::string, std::unique_ptr<ScriptCompiler::CachedData>>
cached_code_map_;
}; };

View File

@ -13263,6 +13263,15 @@ bool Script::GetPositionInfo(Handle<Script> script, int position,
bool Script::IsUserJavaScript() { return type() == Script::TYPE_NORMAL; } 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 { namespace {
bool GetPositionInfoSlow(const Script* script, int position, bool GetPositionInfoSlow(const Script* script, int position,
Script::PositionInfo* info) { Script::PositionInfo* info) {

View File

@ -635,6 +635,14 @@ ByteArray* BytecodeArray::SourcePositionTable() {
->source_position_table(); ->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::BytecodeArraySize() { return SizeFor(this->length()); }
int BytecodeArray::SizeIncludingMetadata() { int BytecodeArray::SizeIncludingMetadata() {

View File

@ -790,6 +790,7 @@ class BytecodeArray : public FixedArrayBase {
DECL_ACCESSORS(source_position_table, Object) DECL_ACCESSORS(source_position_table, Object)
inline ByteArray* SourcePositionTable(); inline ByteArray* SourcePositionTable();
inline void ClearFrameCacheFromSourcePositionTable();
DECL_CAST(BytecodeArray) DECL_CAST(BytecodeArray)

View File

@ -118,6 +118,9 @@ class Script : public Struct {
// Retrieve source position from where eval was called. // Retrieve source position from where eval was called.
int GetEvalPosition(); int GetEvalPosition();
// Check if the script contains any Asm modules.
bool ContainsAsmModule();
// Init line_ends array with source code positions of line ends. // Init line_ends array with source code positions of line ends.
static void InitLineEnds(Handle<Script> script); static void InitLineEnds(Handle<Script> script);

View File

@ -106,12 +106,37 @@ void CodeSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
} }
if (obj->IsScript()) { 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. // 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()) { if (obj->IsSharedFunctionInfo()) {
SharedFunctionInfo* sfi = SharedFunctionInfo::cast(obj); 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. // Mark SFI to indicate whether the code is cached.
bool was_deserialized = sfi->deserialized(); bool was_deserialized = sfi->deserialized();
sfi->set_deserialized(sfi->is_compiled()); sfi->set_deserialized(sfi->is_compiled());
@ -120,6 +145,11 @@ void CodeSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
return; 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. // Past this point we should not see any (context-specific) maps anymore.
CHECK(!obj->IsMap()); CHECK(!obj->IsMap());
// There should be no references to the global object embedded. // There should be no references to the global object embedded.

View File

@ -234,6 +234,13 @@ HeapObject* Deserializer<AllocatorT>::PostProcessNewObject(HeapObject* obj,
void* backing_store = off_heap_backing_stores_[store_index->value()]; void* backing_store = off_heap_backing_stores_[store_index->value()];
fta->set_external_pointer(backing_store); 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. // Check alignment.
DCHECK_EQ(0, Heap::GetFillToAlign(obj->address(), obj->RequiredAlignment())); DCHECK_EQ(0, Heap::GetFillToAlign(obj->address(), obj->RequiredAlignment()));

View File

@ -57,6 +57,8 @@
namespace v8 { namespace v8 {
namespace internal { namespace internal {
enum CodeCacheType { kLazy, kEager, kAfterExecute };
void DisableLazyDeserialization() { void DisableLazyDeserialization() {
// UNINITIALIZED tests do not set up the isolate sufficiently for lazy // UNINITIALIZED tests do not set up the isolate sufficiently for lazy
// deserialization to work. // deserialization to work.
@ -1838,8 +1840,8 @@ static void SerializerCodeEventListener(const v8::JitCodeEvent* event) {
} }
} }
v8::ScriptCompiler::CachedData* ProduceCache(const char* source, v8::ScriptCompiler::CachedData* ProduceCache(
bool eager = false) { const char* source, CodeCacheType cacheType = CodeCacheType::kLazy) {
v8::ScriptCompiler::CachedData* cache; v8::ScriptCompiler::CachedData* cache;
v8::Isolate::CreateParams create_params; v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); 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::Local<v8::String> source_str = v8_str(source);
v8::ScriptOrigin origin(v8_str("test")); v8::ScriptOrigin origin(v8_str("test"));
v8::ScriptCompiler::Source source(source_str, origin); v8::ScriptCompiler::Source source(source_str, origin);
v8::ScriptCompiler::CompileOptions options = v8::ScriptCompiler::CompileOptions options;
eager ? v8::ScriptCompiler::kProduceFullCodeCache switch (cacheType) {
: v8::ScriptCompiler::kProduceCodeCache; 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::Local<v8::UnboundScript> script =
v8::ScriptCompiler::CompileUnboundScript(isolate1, &source, options) v8::ScriptCompiler::CompileUnboundScript(isolate1, &source, options)
.ToLocalChecked(); .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() v8::Local<v8::Value> result = script->BindToCurrentContext()
->Run(isolate1->GetCurrentContext()) ->Run(isolate1->GetCurrentContext())
@ -1874,6 +1880,18 @@ v8::ScriptCompiler::CachedData* ProduceCache(const char* source,
result->ToString(isolate1->GetCurrentContext()).ToLocalChecked(); result->ToString(isolate1->GetCurrentContext()).ToLocalChecked();
CHECK(result_string->Equals(isolate1->GetCurrentContext(), v8_str("abcdef")) CHECK(result_string->Equals(isolate1->GetCurrentContext(), v8_str("abcdef"))
.FromJust()); .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(); isolate1->Dispose();
return cache; return cache;
@ -1936,7 +1954,8 @@ TEST(CodeSerializerIsolatesEager) {
" }" " }"
"}" "}"
"f()() + 'def'"; "f()() + 'def'";
v8::ScriptCompiler::CachedData* cache = ProduceCache(source, true); v8::ScriptCompiler::CachedData* cache =
ProduceCache(source, CodeCacheType::kEager);
v8::Isolate::CreateParams create_params; v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
@ -1974,6 +1993,68 @@ TEST(CodeSerializerIsolatesEager) {
isolate2->Dispose(); 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) { TEST(CodeSerializerFlagChange) {
const char* source = "function f() { return 'abc'; }; f() + 'def'"; const char* source = "function f() { return 'abc'; }; f() + 'def'";
v8::ScriptCompiler::CachedData* cache = ProduceCache(source); v8::ScriptCompiler::CachedData* cache = ProduceCache(source);

View 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();