Generalized the EvalCache into a CompilationCache and enabled
authorkasperl@chromium.org <kasperl@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 11 Sep 2008 10:51:52 +0000 (10:51 +0000)
committerkasperl@chromium.org <kasperl@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 11 Sep 2008 10:51:52 +0000 (10:51 +0000)
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

15 files changed:
src/SConscript
src/compilation-cache.cc [new file with mode: 0644]
src/compilation-cache.h [new file with mode: 0644]
src/compiler.cc
src/handles.cc
src/heap-inl.h
src/heap.cc
src/heap.h
src/objects-inl.h
src/objects.cc
src/objects.h
src/runtime.cc
src/v8-counters.h
test/cctest/test-api.cc
tools/visual_studio/v8_base.vcproj

index cc39e22..725f935 100644 (file)
@@ -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:
diff --git a/src/compilation-cache.cc b/src/compilation-cache.cc
new file mode 100644 (file)
index 0000000..67549e5
--- /dev/null
@@ -0,0 +1,150 @@
+// 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
diff --git a/src/compilation-cache.h b/src/compilation-cache.h
new file mode 100644 (file)
index 0000000..a87c495
--- /dev/null
@@ -0,0 +1,85 @@
+// 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_
index 78772a1..496847d 100644 (file)
@@ -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;
 }
 
 
index 6c41a57..84d91df 100644 (file)
@@ -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);
index c03c22c..e84c6eb 100644 (file)
@@ -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) {
index 5548fe9..2f67da4 100644 (file)
@@ -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);
index 2fdcea2..bd16c88 100644 (file)
@@ -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);
index e690327..6e5081b 100644 (file)
@@ -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)
index 3f29c71..b2ace6e 100644 (file)
@@ -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);
index ba9de24..742e4f3 100644 (file)
@@ -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);
 };
 
 
index 5bab875..4eeab2a 100644 (file)
@@ -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;
index 76c2ec8..bd1f035 100644 (file)
@@ -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. */                            \
index f43a319..a79c866 100644 (file)
@@ -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());
+}
index b8856e1..80576f1 100644 (file)
                                >
                        </File>
                        <File
+                               RelativePath="..\..\src\compilation-cache.cc"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\..\src\compilation-cache.h"
+                               >
+                       </File>
+                       <File
                                RelativePath="..\..\src\compiler.cc"
                                >
                        </File>