Commit b5a72a16 authored by kasperl@chromium.org's avatar kasperl@chromium.org

Generalized the EvalCache into a CompilationCache and enabled

it for scripts too. In the context of Chromium, this should 
have a very positive impact on memory consumption for web apps
that run multiple tabs from the same domain with a lot of the
same JavaScript code.

For now, the cache retirement policy is really simple:
Whenever a mark-sweep collection is started we clear the
cache. This guarantees that this change will not have a
huge negative impact on memory consumption, but it may
not be ideal. We should consider a more sophisticated LRU
scheme.
Review URL: http://codereview.chromium.org/1933

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@270 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 6063e76c
......@@ -37,9 +37,9 @@ SOURCES = {
'all': [
'accessors.cc', 'allocation.cc', 'api.cc', 'assembler.cc', 'ast.cc',
'bootstrapper.cc', 'builtins.cc', 'checks.cc', 'code-stubs.cc',
'codegen.cc', 'compiler.cc', 'contexts.cc', 'conversions.cc',
'counters.cc', 'dateparser.cc', 'debug.cc', 'disassembler.cc',
'execution.cc', 'factory.cc', 'flags.cc', 'frames.cc',
'codegen.cc', 'compilation-cache.cc', 'compiler.cc', 'contexts.cc',
'conversions.cc', 'counters.cc', 'dateparser.cc', 'debug.cc',
'disassembler.cc', 'execution.cc', 'factory.cc', 'flags.cc', 'frames.cc',
'global-handles.cc', 'handles.cc', 'hashmap.cc', 'heap.cc', 'ic.cc',
'jsregexp.cc', 'log.cc', 'mark-compact.cc', 'messages.cc', 'objects.cc',
'parser.cc', 'property.cc', 'rewriter.cc', 'runtime.cc', 'scanner.cc',
......@@ -125,7 +125,7 @@ def ConfigureObjectFiles():
source_objs = context.ConfigureObject(env, source_files)
non_snapshot_files = [jscre_obj, dtoa_obj, source_objs]
# Create snapshot if necessary.
empty_snapshot_obj = context.ConfigureObject(env, 'snapshot-empty.cc')
if context.use_snapshot:
......
// Copyright 2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "v8.h"
#include "compilation-cache.h"
namespace v8 { namespace internal {
enum {
NUMBER_OF_ENTRY_KINDS = CompilationCache::EVAL_CONTEXTUAL + 1
};
// Keep separate tables for the different entry kinds.
static Object* tables[NUMBER_OF_ENTRY_KINDS] = { 0, };
static Handle<CompilationCacheTable> AllocateTable(int size) {
CALL_HEAP_FUNCTION(CompilationCacheTable::Allocate(size),
CompilationCacheTable);
}
static Handle<CompilationCacheTable> GetTable(CompilationCache::Entry entry) {
Handle<CompilationCacheTable> result;
if (tables[entry]->IsUndefined()) {
static const int kInitialCacheSize = 64;
result = AllocateTable(kInitialCacheSize);
tables[entry] = *result;
} else {
CompilationCacheTable* table = CompilationCacheTable::cast(tables[entry]);
result = Handle<CompilationCacheTable>(table);
}
return result;
}
// We only re-use a cached function for some script source code if the
// script originates from the same places. This is to avoid issues
// when reporting errors, etc.
static bool HasOrigin(Handle<JSFunction> boilerplate,
Handle<Object> name,
int line_offset,
int column_offset) {
Handle<Script> script =
Handle<Script>(Script::cast(boilerplate->shared()->script()));
// If the script name isn't set, the boilerplate script should have
// an undefined name to have the same origin.
if (name.is_null()) {
return script->name()->IsUndefined();
}
// Do the fast bailout checks first.
if (line_offset != script->line_offset()->value()) return false;
if (column_offset != script->column_offset()->value()) return false;
// Check that both names are strings. If not, no match.
if (!name->IsString() || !script->name()->IsString()) return false;
// Compare the two name strings for equality.
return String::cast(*name)->Equals(String::cast(script->name()));
}
static Handle<JSFunction> Lookup(Handle<String> source,
CompilationCache::Entry entry) {
Handle<CompilationCacheTable> table = GetTable(entry);
Object* result = table->Lookup(*source);
if (result->IsJSFunction()) {
return Handle<JSFunction>(JSFunction::cast(result));
} else {
return Handle<JSFunction>::null();
}
}
Handle<JSFunction> CompilationCache::LookupScript(Handle<String> source,
Handle<Object> name,
int line_offset,
int column_offset) {
Handle<JSFunction> result = Lookup(source, SCRIPT);
if (result.is_null()) {
Counters::compilation_cache_misses.Increment();
} else if (HasOrigin(result, name, line_offset, column_offset)) {
Counters::compilation_cache_hits.Increment();
} else {
result = Handle<JSFunction>::null();
Counters::compilation_cache_misses.Increment();
}
return result;
}
Handle<JSFunction> CompilationCache::LookupEval(Handle<String> source,
Entry entry) {
ASSERT(entry == EVAL_GLOBAL || entry == EVAL_CONTEXTUAL);
Handle<JSFunction> result = Lookup(source, entry);
if (result.is_null()) {
Counters::compilation_cache_misses.Increment();
} else {
Counters::compilation_cache_hits.Increment();
}
return result;
}
void CompilationCache::Associate(Handle<String> source,
Entry entry,
Handle<JSFunction> boilerplate) {
ASSERT(boilerplate->IsBoilerplate());
Handle<CompilationCacheTable> table = GetTable(entry);
CALL_HEAP_FUNCTION_VOID(table->Put(*source, *boilerplate));
}
void CompilationCache::Clear() {
for (int i = 0; i < NUMBER_OF_ENTRY_KINDS; i++) {
tables[i] = Heap::undefined_value();
}
}
void CompilationCache::Iterate(ObjectVisitor* v) {
v->VisitPointers(&tables[0], &tables[NUMBER_OF_ENTRY_KINDS]);
}
} } // namespace v8::internal
// Copyright 2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_COMPILATION_CACHE_H_
#define V8_COMPILATION_CACHE_H_
namespace v8 { namespace internal {
// The compilation cache keeps function boilerplates for compiled
// scripts and evals. The boilerplates are looked up using the source
// string as the key.
class CompilationCache {
public:
// The same source code string has different compiled code for
// scripts and evals. Internally, we use separate caches to avoid
// getting the wrong kind of entry when looking up.
enum Entry {
SCRIPT,
EVAL_GLOBAL,
EVAL_CONTEXTUAL
};
// Finds the script function boilerplate for a source
// string. Returns an empty handle if the cache doesn't contain a
// script for the given source string with the right origin.
static Handle<JSFunction> LookupScript(Handle<String> source,
Handle<Object> name,
int line_offset,
int column_offset);
// Finds the function boilerplate for a source string for
// eval. Returns an empty handle if the cache doesn't contain a
// script for the given source string.
static Handle<JSFunction> LookupEval(Handle<String> source,
Entry entry);
// Associate the (source, kind) pair to the boilerplate. This may
// overwrite an existing mapping.
static void Associate(Handle<String> source,
Entry entry,
Handle<JSFunction> boilerplate);
// Clear the cache - also used to initialize the cache at startup.
static void Clear();
// GC support.
static void Iterate(ObjectVisitor* v);
// Notify the cache that a mark-sweep garbage collection is about to
// take place. This is used to retire entries from the cache to
// avoid keeping them alive too long without using them. For now, we
// just clear the cache but we should consider are more
// sophisticated LRU scheme.
static void MarkCompactPrologue() { Clear(); }
};
} } // namespace v8::internal
#endif // V8_COMPILATION_CACHE_H_
......@@ -29,6 +29,7 @@
#include "bootstrapper.h"
#include "codegen-inl.h"
#include "compilation-cache.h"
#include "compiler.h"
#include "debug.h"
#include "scopes.h"
......@@ -170,27 +171,44 @@ Handle<JSFunction> Compiler::Compile(Handle<String> source,
// The VM is in the COMPILER state until exiting this function.
VMState state(COMPILER);
ScriptDataImpl* pre_data = input_pre_data;
if (pre_data == NULL && source->length() >= FLAG_min_preparse_length) {
Access<SafeStringInputBuffer> buf(&safe_string_input_buffer);
buf->Reset(source.location());
pre_data = PreParse(buf.value(), extension);
// Do a lookup in the compilation cache but not for extensions.
Handle<JSFunction> result;
if (extension == NULL) {
result = CompilationCache::LookupScript(source,
script_name,
line_offset,
column_offset);
}
// Create a script object describing the script to be compiled.
Handle<Script> script = Factory::NewScript(source);
if (!script_name.is_null()) {
script->set_name(*script_name);
script->set_line_offset(Smi::FromInt(line_offset));
script->set_column_offset(Smi::FromInt(column_offset));
if (result.is_null()) {
// No cache entry found. Do pre-parsing and compile the script.
ScriptDataImpl* pre_data = input_pre_data;
if (pre_data == NULL && source->length() >= FLAG_min_preparse_length) {
Access<SafeStringInputBuffer> buf(&safe_string_input_buffer);
buf->Reset(source.location());
pre_data = PreParse(buf.value(), extension);
}
// Create a script object describing the script to be compiled.
Handle<Script> script = Factory::NewScript(source);
if (!script_name.is_null()) {
script->set_name(*script_name);
script->set_line_offset(Smi::FromInt(line_offset));
script->set_column_offset(Smi::FromInt(column_offset));
}
// Compile the function and add it to the cache.
result = MakeFunction(true, false, script, extension, pre_data);
if (extension == NULL && !result.is_null()) {
CompilationCache::Associate(source, CompilationCache::SCRIPT, result);
}
// Get rid of the pre-parsing data (if necessary).
if (input_pre_data == NULL && pre_data != NULL) {
delete pre_data;
}
}
Handle<JSFunction> result =
MakeFunction(true, false, script, extension, pre_data);
if (input_pre_data == NULL && pre_data != NULL)
delete pre_data;
return result;
}
......@@ -202,10 +220,22 @@ Handle<JSFunction> Compiler::CompileEval(bool is_global,
// The VM is in the COMPILER state until exiting this function.
VMState state(COMPILER);
// Create a script object describing the script to be compiled.
Handle<Script> script = Factory::NewScript(source);
return MakeFunction(is_global, true, script, NULL, NULL);
CompilationCache::Entry entry = is_global
? CompilationCache::EVAL_GLOBAL
: CompilationCache::EVAL_CONTEXTUAL;
// Do a lookup in the compilation cache; if the entry is not there,
// invoke the compiler and add the result to the cache.
Handle<JSFunction> result = CompilationCache::LookupEval(source, entry);
if (result.is_null()) {
// Create a script object describing the script to be compiled.
Handle<Script> script = Factory::NewScript(source);
result = MakeFunction(is_global, true, script, NULL, NULL);
if (!result.is_null()) {
CompilationCache::Associate(source, entry, result);
}
}
return result;
}
......
......@@ -56,34 +56,6 @@ DECLARE_bool(gc_greedy);
}
// Don't use the following names: __object__, __failure__.
#define CALL_HEAP_FUNCTION_VOID(FUNCTION_CALL) \
GC_GREEDY_CHECK(); \
Object* __object__ = FUNCTION_CALL; \
if (__object__->IsFailure()) { \
if (__object__->IsRetryAfterGC()) { \
Failure* __failure__ = Failure::cast(__object__); \
if (!Heap::CollectGarbage(__failure__->requested(), \
__failure__->allocation_space())) { \
/* TODO(1181417): Fix this. */ \
V8::FatalProcessOutOfMemory("Handles"); \
} \
__object__ = FUNCTION_CALL; \
if (__object__->IsFailure()) { \
if (__object__->IsRetryAfterGC()) { \
/* TODO(1181417): Fix this. */ \
V8::FatalProcessOutOfMemory("Handles"); \
} \
return; \
} \
} else { \
return; \
} \
}
Handle<FixedArray> AddKeysFromJSArray(Handle<FixedArray> content,
Handle<JSArray> array) {
CALL_HEAP_FUNCTION(content->AddKeysFromJSArray(*array), FixedArray);
......
......@@ -196,6 +196,32 @@ OldSpace* Heap::TargetSpace(HeapObject* object) {
} while (false)
// Don't use the following names: __object__, __failure__.
#define CALL_HEAP_FUNCTION_VOID(FUNCTION_CALL) \
GC_GREEDY_CHECK(); \
Object* __object__ = FUNCTION_CALL; \
if (__object__->IsFailure()) { \
if (__object__->IsRetryAfterGC()) { \
Failure* __failure__ = Failure::cast(__object__); \
if (!Heap::CollectGarbage(__failure__->requested(), \
__failure__->allocation_space())) { \
/* TODO(1181417): Fix this. */ \
V8::FatalProcessOutOfMemory("Handles"); \
} \
__object__ = FUNCTION_CALL; \
if (__object__->IsFailure()) { \
if (__object__->IsRetryAfterGC()) { \
/* TODO(1181417): Fix this. */ \
V8::FatalProcessOutOfMemory("Handles"); \
} \
return; \
} \
} else { \
return; \
} \
}
#ifdef DEBUG
inline bool Heap::allow_allocation(bool new_state) {
......
......@@ -31,6 +31,7 @@
#include "api.h"
#include "bootstrapper.h"
#include "codegen-inl.h"
#include "compilation-cache.h"
#include "debug.h"
#include "global-handles.h"
#include "jsregexp.h"
......@@ -447,10 +448,7 @@ void Heap::MarkCompact(GCTracer* tracer) {
void Heap::MarkCompactPrologue() {
// Empty eval caches
Heap::eval_cache_global_ = Heap::null_value();
Heap::eval_cache_non_global_ = Heap::null_value();
CompilationCache::MarkCompactPrologue();
RegExpImpl::OldSpaceCollectionPrologue();
Top::MarkCompactPrologue();
ThreadManager::MarkCompactPrologue();
......@@ -1208,9 +1206,8 @@ bool Heap::CreateInitialObjects() {
if (obj->IsFailure()) return false;
natives_source_cache_ = FixedArray::cast(obj);
// Initialized eval cache to null value.
eval_cache_global_ = null_value();
eval_cache_non_global_ = null_value();
// Initialize compilation cache.
CompilationCache::Clear();
return true;
}
......@@ -2279,34 +2276,6 @@ Object* Heap::LookupSymbol(String* string) {
}
Object* Heap::LookupEvalCache(bool is_global_context, String* src) {
Object* cache = is_global_context ?
eval_cache_global_ : eval_cache_non_global_;
return cache == null_value() ?
null_value() : EvalCache::cast(cache)->Lookup(src);
}
Object* Heap::PutInEvalCache(bool is_global_context, String* src,
JSFunction* value) {
Object** cache_ptr = is_global_context ?
&eval_cache_global_ : &eval_cache_non_global_;
if (*cache_ptr == null_value()) {
Object* obj = EvalCache::Allocate(kInitialEvalCacheSize);
if (obj->IsFailure()) return false;
*cache_ptr = obj;
}
Object* new_cache =
EvalCache::cast(*cache_ptr)->Put(src, value);
if (new_cache->IsFailure()) return new_cache;
*cache_ptr = new_cache;
return value;
}
#ifdef DEBUG
void Heap::ZapFromSpace() {
ASSERT(HAS_HEAP_OBJECT_TAG(kFromSpaceZapValue));
......@@ -2417,6 +2386,8 @@ void Heap::IterateStrongRoots(ObjectVisitor* v) {
SYNCHRONIZE_TAG("top");
Debug::Iterate(v);
SYNCHRONIZE_TAG("debug");
CompilationCache::Iterate(v);
SYNCHRONIZE_TAG("compilationcache");
// Iterate over local handles in handle scopes.
HandleScopeImplementer::Iterate(v);
......
......@@ -122,9 +122,8 @@ namespace v8 { namespace internal {
V(Code, c_entry_debug_break_code) \
V(FixedArray, number_string_cache) \
V(FixedArray, single_character_string_cache) \
V(FixedArray, natives_source_cache) \
V(Object, eval_cache_global) \
V(Object, eval_cache_non_global)
V(FixedArray, natives_source_cache)
#define ROOT_LIST(V) \
STRONG_ROOT_LIST(V) \
......@@ -531,28 +530,6 @@ class Heap : public AllStatic {
}
static Object* LookupSymbol(String* str);
// EvalCache caches function boilerplates for compiled scripts
// from 'eval' function.
// Source string is used as the key, and compiled function
// boilerplate as value. Because the same source has different
// compiled code in global or local context, we use separate
// caches for global and local contexts.
// Caches are cleared before mark-compact/mark-sweep GC's.
// Finds the function boilerplate of a source string.
// It returns a JSFunction object if found in the cache.
// The first parameter specifies whether the code is
// compiled in a global context.
static Object* LookupEvalCache(bool is_global_context, String* src);
// Put a source string and its compiled function boilerplate
// in the eval cache. The cache may expand, and returns failure
// if it cannot expand the cache, otherwise the value is returned.
// The first parameter specifies whether the boilerplate is
// compiled in a global context.
static Object* PutInEvalCache(bool is_global_context,
String* src, JSFunction* value);
// Compute the matching symbol map for a string if possible.
// NULL is returned if string is in new space or not flattened.
static Map* SymbolMapForString(String* str);
......
......@@ -314,10 +314,8 @@ bool Object::IsSymbolTable() {
}
bool Object::IsEvalCache() {
return IsHashTable() &&
(this == Heap::eval_cache_global() ||
this == Heap::eval_cache_non_global());
bool Object::IsCompilationCacheTable() {
return IsHashTable();
}
......@@ -1096,7 +1094,7 @@ CAST_ACCESSOR(FixedArray)
CAST_ACCESSOR(DescriptorArray)
CAST_ACCESSOR(Dictionary)
CAST_ACCESSOR(SymbolTable)
CAST_ACCESSOR(EvalCache)
CAST_ACCESSOR(CompilationCacheTable)
CAST_ACCESSOR(String)
CAST_ACCESSOR(SeqString)
CAST_ACCESSOR(AsciiString)
......
......@@ -5576,7 +5576,7 @@ Object* SymbolTable::LookupKey(HashTableKey* key, Object** s) {
}
Object* EvalCache::Lookup(String* src) {
Object* CompilationCacheTable::Lookup(String* src) {
StringKey key(src);
int entry = FindEntry(&key);
if (entry != -1) {
......@@ -5587,12 +5587,13 @@ Object* EvalCache::Lookup(String* src) {
}
Object* EvalCache::Put(String* src, Object* value) {
Object* CompilationCacheTable::Put(String* src, Object* value) {
StringKey key(src);
Object* obj = EnsureCapacity(1, &key);
if (obj->IsFailure()) return obj;
EvalCache* cache = reinterpret_cast<EvalCache*>(obj);
CompilationCacheTable* cache =
reinterpret_cast<CompilationCacheTable*>(obj);
int entry = cache->FindInsertionEntry(src, key.Hash());
cache->set(EntryToIndex(entry), src);
cache->set(EntryToIndex(entry) + 1, value);
......
......@@ -614,7 +614,7 @@ class Object BASE_EMBEDDED {
inline bool IsHashTable();
inline bool IsDictionary();
inline bool IsSymbolTable();
inline bool IsEvalCache();
inline bool IsCompilationCacheTable();
inline bool IsPrimitive();
inline bool IsGlobalObject();
inline bool IsJSGlobalObject();
......@@ -1818,19 +1818,16 @@ class SymbolTable: public HashTable<0, 1> {
};
// EvalCache for caching eval'ed string and function.
//
// The cache is cleaned up during a mark-compact GC.
class EvalCache: public HashTable<0, 2> {
class CompilationCacheTable: public HashTable<0, 2> {
public:
// Find cached value for a string key, otherwise return null.
Object* Lookup(String* src);
Object* Put(String* src, Object* value);
static inline EvalCache* cast(Object* obj);
static inline CompilationCacheTable* cast(Object* obj);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(EvalCache);
DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheTable);
};
......
......@@ -3364,6 +3364,7 @@ static Object* Runtime_EvalReceiver(Arguments args) {
static Object* Runtime_CompileString(Arguments args) {
HandleScope scope;
ASSERT(args.length() == 2);
CONVERT_ARG_CHECKED(String, source, 0);
bool contextual = args[1]->IsTrue();
RUNTIME_ASSERT(contextual || args[1]->IsFalse());
......@@ -3380,27 +3381,12 @@ static Object* Runtime_CompileString(Arguments args) {
context = Handle<Context>(Top::context()->global_context());
}
// Compile eval() source.
bool is_global_context = context->IsGlobalContext();
Handle<String> source(String::cast(args[0]));
Object* obj = Heap::LookupEvalCache(is_global_context, *source);
if (obj->IsFailure()) return obj;
Handle<JSFunction> boilerplate;
if (!obj->IsJSFunction()) {
Counters::eval_cache_misses.Increment();
boilerplate = Compiler::CompileEval(is_global_context, source);
if (boilerplate.is_null()) return Failure::Exception();
Object* obj =
Heap::PutInEvalCache(is_global_context, *source, *boilerplate);
if (obj->IsFailure()) return obj;
} else {
Counters::eval_cache_hits.Increment();
boilerplate = Handle<JSFunction>(JSFunction::cast(obj));
}
// Compile source string.
bool is_global = context->IsGlobalContext();
Handle<JSFunction> boilerplate =
Compiler::CompileEval(is_global, source);
if (boilerplate.is_null()) return Failure::Exception();
Handle<JSFunction> fun =
Factory::NewFunctionFromBoilerplate(boilerplate, context);
return *fun;
......
......@@ -71,8 +71,8 @@ namespace v8 { namespace internal {
SC(call_normal_stubs, V8.CallNormalStubs) \
SC(call_megamorphic_stubs, V8.CallMegamorphicStubs) \
SC(arguments_adaptors, V8.ArgumentsAdaptors) \
SC(eval_cache_hits, V8.EvalCacheHits) \
SC(eval_cache_misses, V8.EvalCacheMisses) \
SC(compilation_cache_hits, V8.CompilationCacheHits) \
SC(compilation_cache_misses, V8.CompilationCacheMisses) \
/* Amount of evaled source code. */ \
SC(total_eval_size, V8.TotalEvalSize) \
/* Amount of loaded source code. */ \
......
......@@ -4880,7 +4880,7 @@ THREADED_TEST(TryCatchSourceInfo) {
"\n"
"Foo();\n");
v8::Handle<v8::Script> script =
v8::Script::Compile(source, v8::String::New("test.js"));
v8::Script::Compile(source, v8::String::New("test.js"));
v8::TryCatch try_catch;
v8::Handle<v8::Value> result = script->Run();
CHECK(result.IsEmpty());
......@@ -4897,3 +4897,20 @@ THREADED_TEST(TryCatchSourceInfo) {
v8::String::AsciiValue name(message->GetScriptResourceName());
CHECK_EQ("test.js", *name);
}
THREADED_TEST(CompilationCache) {
v8::HandleScope scope;
LocalContext context;
v8::Handle<v8::String> source0 = v8::String::New("1234");
v8::Handle<v8::String> source1 = v8::String::New("1234");
v8::Handle<v8::Script> script0 =
v8::Script::Compile(source0, v8::String::New("test.js"));
v8::Handle<v8::Script> script1 =
v8::Script::Compile(source1, v8::String::New("test.js"));
v8::Handle<v8::Script> script2 =
v8::Script::Compile(source0); // different origin
CHECK_EQ(1234, script0->Run()->Int32Value());
CHECK_EQ(1234, script1->Run()->Int32Value());
CHECK_EQ(1234, script2->Run()->Int32Value());
}
......@@ -380,6 +380,14 @@
RelativePath="..\..\src\codegen.h"
>
</File>
<File
RelativePath="..\..\src\compilation-cache.cc"
>
</File>
<File
RelativePath="..\..\src\compilation-cache.h"
>
</File>
<File
RelativePath="..\..\src\compiler.cc"
>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment