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:
Mythri 2017-12-01 12:50:41 +00:00 committed by Commit Bot
parent e9c9322339
commit dae20b0df6
13 changed files with 315 additions and 117 deletions

View File

@ -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,

View File

@ -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) {

View File

@ -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
View File

@ -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);

View File

@ -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_;
};

View File

@ -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) {

View File

@ -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() {

View File

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

View File

@ -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);

View File

@ -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.

View File

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

View File

@ -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);

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