void Heap::MarkCompactPrologue() {
+ // Empty eval caches
+ Heap::eval_cache_global_ = Heap::null_value();
+ Heap::eval_cache_non_global_ = Heap::null_value();
+
RegExpImpl::OldSpaceCollectionPrologue();
Top::MarkCompactPrologue();
ThreadManager::MarkCompactPrologue();
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();
+
return true;
}
}
+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));
V(Code, c_entry_debug_break_code) \
V(FixedArray, number_string_cache) \
V(FixedArray, single_character_string_cache) \
- V(FixedArray, natives_source_cache)
+ V(FixedArray, natives_source_cache) \
+ V(Object, eval_cache_global) \
+ V(Object, eval_cache_non_global)
#define ROOT_LIST(V) \
STRONG_ROOT_LIST(V) \
}
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);
static void RebuildRSets(LargeObjectSpace* space);
static const int kInitialSymbolTableSize = 2048;
+ static const int kInitialEvalCacheSize = 64;
friend class Factory;
friend class DisallowAllocationFailure;
int previous_marked_count_;
};
-
} } // namespace v8::internal
#endif // V8_HEAP_H_
}
+bool Object::IsEvalCache() {
+ return IsHashTable() &&
+ (this == Heap::eval_cache_global() ||
+ this == Heap::eval_cache_non_global());
+}
+
+
bool Object::IsPrimitive() {
return IsOddball() || IsNumber() || IsString();
}
CAST_ACCESSOR(DescriptorArray)
CAST_ACCESSOR(Dictionary)
CAST_ACCESSOR(SymbolTable)
+CAST_ACCESSOR(EvalCache)
CAST_ACCESSOR(String)
CAST_ACCESSOR(SeqString)
CAST_ACCESSOR(AsciiString)
// The NumberKey uses carries the uint32_t as key.
// This avoids allocation in HasProperty.
-class Dictionary::NumberKey : public Dictionary::Key {
+class NumberKey : public HashTableKey {
public:
explicit NumberKey(uint32_t number) {
number_ = number;
uint32_t number_;
};
+
// StringKey simply carries a string object as key.
-class Dictionary::StringKey : public Dictionary::Key {
+class StringKey : public HashTableKey {
public:
explicit StringKey(String* string) {
string_ = string;
}
- private:
bool IsMatch(Object* other) {
if (!other->IsString()) return false;
return string_->Equals(String::cast(other));
String* string_;
};
-// Utf8Key carries a vector of chars as key.
-class SymbolTable::Utf8Key : public SymbolTable::Key {
+// Utf8SymbolKey carries a vector of chars as key.
+class Utf8SymbolKey : public HashTableKey {
public:
- explicit Utf8Key(Vector<const char> string)
+ explicit Utf8SymbolKey(Vector<const char> string)
: string_(string), hash_(0) { }
bool IsMatch(Object* other) {
};
-// StringKey carries a string object as key.
-class SymbolTable::StringKey : public SymbolTable::Key {
+// SymbolKey carries a string/symbol object as key.
+class SymbolKey : public HashTableKey {
public:
- explicit StringKey(String* string) : string_(string) { }
+ explicit SymbolKey(String* string) : string_(string) { }
HashFunction GetHashFunction() {
return StringHash;
// Find entry for key otherwise return -1.
template <int prefix_size, int element_size>
-int HashTable<prefix_size, element_size>::FindEntry(Key* key) {
+int HashTable<prefix_size, element_size>::FindEntry(HashTableKey* key) {
uint32_t nof = NumberOfElements();
if (nof == 0) return -1; // Bail out if empty.
template<int prefix_size, int element_size>
-Object* HashTable<prefix_size, element_size>::EnsureCapacity(int n, Key* key) {
+Object* HashTable<prefix_size, element_size>::EnsureCapacity(
+ int n, HashTableKey* key) {
int capacity = Capacity();
int nof = NumberOfElements() + n;
// Make sure 20% is free
template class HashTable<2, 3>;
+// Force instantiation of EvalCache's base class
+template class HashTable<0, 2>;
+
+
Object* SymbolTable::LookupString(String* string, Object** s) {
- StringKey key(string);
+ SymbolKey key(string);
return LookupKey(&key, s);
}
Object* SymbolTable::LookupSymbol(Vector<const char> str, Object** s) {
- Utf8Key key(str);
+ Utf8SymbolKey key(str);
return LookupKey(&key, s);
}
-Object* SymbolTable::LookupKey(Key* key, Object** s) {
+Object* SymbolTable::LookupKey(HashTableKey* key, Object** s) {
int entry = FindEntry(key);
// Symbol already in table.
}
+Object* EvalCache::Lookup(String* src) {
+ StringKey key(src);
+ int entry = FindEntry(&key);
+ if (entry != -1) {
+ return get(EntryToIndex(entry) + 1);
+ } else {
+ return Heap::undefined_value();
+ }
+}
+
+
+Object* EvalCache::Put(String* src, Object* value) {
+ StringKey key(src);
+ Object* obj = EnsureCapacity(1, &key);
+ if (obj->IsFailure()) return obj;
+
+ EvalCache* cache = reinterpret_cast<EvalCache*>(obj);
+ int entry = cache->FindInsertionEntry(src, key.Hash());
+ cache->set(EntryToIndex(entry), src);
+ cache->set(EntryToIndex(entry) + 1, value);
+ cache->ElementAdded();
+ return cache;
+}
+
+
Object* Dictionary::Allocate(int at_least_space_for) {
Object* obj = DictionaryBase::Allocate(at_least_space_for);
// Initialize the next enumeration index.
}
-Object* Dictionary::EnsureCapacity(int n, Key* key) {
+Object* Dictionary::EnsureCapacity(int n, HashTableKey* key) {
// Check whether there are enough enumeration indices to add n elements.
if (key->IsStringKey() &&
!PropertyDetails::IsValidIndex(NextEnumerationIndex() + n)) {
}
-Object* Dictionary::AtPut(Key* key, Object* value) {
+Object* Dictionary::AtPut(HashTableKey* key, Object* value) {
int entry = FindEntry(key);
// If the entry is present set the value;
}
-Object* Dictionary::Add(Key* key, Object* value, PropertyDetails details) {
+Object* Dictionary::Add(HashTableKey* key, Object* value,
+ PropertyDetails details) {
// Check whether the dictionary should be extended.
Object* obj = EnsureCapacity(1, key);
if (obj->IsFailure()) return obj;
inline bool IsHashTable();
inline bool IsDictionary();
inline bool IsSymbolTable();
+ inline bool IsEvalCache();
inline bool IsPrimitive();
inline bool IsGlobalObject();
inline bool IsJSGlobalObject();
// table. The prefix size indicates an amount of memory in the
// beginning of the backing storage that can be used for non-element
// information by subclasses.
+
+// HashTableKey is an abstract superclass keys.
+class HashTableKey {
+ public:
+ // Returns whether the other object matches this key.
+ virtual bool IsMatch(Object* other) = 0;
+ typedef uint32_t (*HashFunction)(Object* obj);
+ // Returns the hash function used for this key.
+ virtual HashFunction GetHashFunction() = 0;
+ // Returns the hash value for this key.
+ virtual uint32_t Hash() = 0;
+ // Returns the key object for storing into the dictionary.
+ // If allocations fails a failure object is returned.
+ virtual Object* GetObject() = 0;
+ virtual bool IsStringKey() = 0;
+ // Required.
+ virtual ~HashTableKey() {}
+};
+
+
template<int prefix_size, int element_size>
class HashTable: public FixedArray {
public:
// Casting.
static inline HashTable* cast(Object* obj);
- // Key is an abstract superclass keys.
- class Key {
- public:
- // Returns whether the other object matches this key.
- virtual bool IsMatch(Object* other) = 0;
- typedef uint32_t (*HashFunction)(Object* obj);
- // Returns the hash function used for this key.
- virtual HashFunction GetHashFunction() = 0;
- // Returns the hash value for this key.
- virtual uint32_t Hash() = 0;
- // Returns the key object for storing into the dictionary.
- // If allocations fails a failure object is returned.
- virtual Object* GetObject() = 0;
- virtual bool IsStringKey() = 0;
- // Required.
- virtual ~Key() {}
- };
-
// Compute the probe offset (quadratic probing).
INLINE(static uint32_t GetProbeOffset(uint32_t n)) {
return (n + n * n) >> 1;
protected:
// Find entry for key otherwise return -1.
- int FindEntry(Key* key);
+ int FindEntry(HashTableKey* key);
// Find the entry at which to insert element with the given key that
// has the given hash value.
}
// Ensure enough space for n additional elements.
- Object* EnsureCapacity(int n, Key* key);
+ Object* EnsureCapacity(int n, HashTableKey* key);
};
static inline SymbolTable* cast(Object* obj);
private:
- Object* LookupKey(Key* key, Object** s);
- class Utf8Key; // Key based on utf8 string.
- class StringKey; // Key based on String*.
+ Object* LookupKey(HashTableKey* key, Object** s);
DISALLOW_IMPLICIT_CONSTRUCTORS(SymbolTable);
};
+// EvalCache for caching eval'ed string and function.
+//
+// The cache is cleaned up during a mark-compact GC.
+class EvalCache: 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);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(EvalCache);
+};
+
+
// Dictionary for keeping properties and elements in slow case.
//
// One element in the prefix is used for storing non-element
static Object* Allocate(int at_least_space_for);
// Ensure enough space for n additional elements.
- Object* EnsureCapacity(int n, Key* key);
+ Object* EnsureCapacity(int n, HashTableKey* key);
#ifdef DEBUG
void Print();
private:
// Generic at put operation.
- Object* AtPut(Key* key, Object* value);
+ Object* AtPut(HashTableKey* key, Object* value);
- Object* Add(Key* key, Object* value, PropertyDetails details);
+ Object* Add(HashTableKey* key, Object* value, PropertyDetails details);
// Add entry to dictionary.
void AddEntry(Object* key,
static const int kMaxNumberKeyIndex = kPrefixStartIndex;
static const int kNextEnumnerationIndexIndex = kMaxNumberKeyIndex + 1;
- class NumberKey; // Key containing uint32_t.
- class StringKey; // Key containing String*.
-
DISALLOW_IMPLICIT_CONSTRUCTORS(Dictionary);
};
}
// Compile eval() source.
+ bool is_global_context = context->IsGlobalContext();
Handle<String> source(String::cast(args[0]));
- Handle<JSFunction> boilerplate =
- Compiler::CompileEval(context->IsGlobalContext(), source);
- if (boilerplate.is_null()) return Failure::Exception();
+ 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));
+ }
+
Handle<JSFunction> fun =
Factory::NewFunctionFromBoilerplate(boilerplate, context);
return *fun;
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) \
/* Amount of evaled source code. */ \
SC(total_eval_size, V8.TotalEvalSize) \
/* Amount of loaded source code. */ \