}
-
void LabelCollector::AddLabel(Label* label) {
// Add the label to the collector, but discard duplicates.
int length = labels_->length();
// therefore context dependent.
class ObjectLiteralDeferred: public DeferredCode {
public:
- ObjectLiteralDeferred(CodeGenerator* generator, ObjectLiteral* node)
+ ObjectLiteralDeferred(CodeGenerator* generator,
+ ObjectLiteral* node)
: DeferredCode(generator), node_(node) {
set_comment("[ ObjectLiteralDeferred");
}
void Ia32CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
Comment cmnt(masm_, "[ ObjectLiteral");
-
ObjectLiteralDeferred* deferred = new ObjectLiteralDeferred(this, node);
// Retrieve the literal array and check the allocated entry.
// Push the new cloned literal object as the result.
__ push(eax);
+
for (int i = 0; i < node->properties()->length(); i++) {
ObjectLiteral::Property* property = node->properties()->at(i);
switch (property->kind()) {
V(EMPTY_SCRIPT_INDEX, Script, empty_script) \
V(SCRIPT_FUNCTION_INDEX, JSFunction, script_function) \
V(CONTEXT_EXTENSION_FUNCTION_INDEX, JSFunction, context_extension_function) \
- V(OUT_OF_MEMORY_INDEX, Object, out_of_memory)
+ V(OUT_OF_MEMORY_INDEX, Object, out_of_memory) \
+ V(MAP_CACHE_INDEX, Object, map_cache)
// JSFunctions are pairs (context, function code), sometimes also called
// closures. A Context object is used to represent function contexts and
SCRIPT_FUNCTION_INDEX,
CONTEXT_EXTENSION_FUNCTION_INDEX,
OUT_OF_MEMORY_INDEX,
+ MAP_CACHE_INDEX,
GLOBAL_CONTEXT_SLOTS
};
// Store the object, regexp and array functions in the literals
// array prefix. These functions will be used when creating
// object, regexp and array literals in this function.
- literals->set(JSFunction::kLiteralObjectFunctionIndex,
- context->global_context()->object_function());
- literals->set(JSFunction::kLiteralRegExpFunctionIndex,
- context->global_context()->regexp_function());
- literals->set(JSFunction::kLiteralArrayFunctionIndex,
- context->global_context()->array_function());
+ literals->set(JSFunction::kLiteralGlobalContextIndex,
+ context->global_context());
}
result->set_literals(*literals);
ASSERT(!result->IsBoilerplate());
}
+Handle<JSObject> Factory::NewJSObjectFromMap(Handle<Map> map) {
+ CALL_HEAP_FUNCTION(Heap::AllocateJSObjectFromMap(*map, NOT_TENURED),
+ JSObject);
+}
+
+
Handle<JSObject> Factory::NewObjectLiteral(int expected_number_of_properties) {
Handle<Map> map = Handle<Map>(Top::object_function()->initial_map());
map = Factory::CopyMap(map);
}
+Handle<MapCache> Factory::NewMapCache(int at_least_space_for) {
+ CALL_HEAP_FUNCTION(MapCache::Allocate(at_least_space_for), MapCache);
+}
+
+
+static Object* UpdateMapCacheWith(Context* context,
+ FixedArray* keys,
+ Map* map) {
+ Object* result = MapCache::cast(context->map_cache())->Put(keys, map);
+ if (!result->IsFailure()) context->set_map_cache(MapCache::cast(result));
+ return result;
+}
+
+
+Handle<MapCache> Factory::AddToMapCache(Handle<Context> context,
+ Handle<FixedArray> keys,
+ Handle<Map> map) {
+ CALL_HEAP_FUNCTION(UpdateMapCacheWith(*context, *keys, *map), MapCache);
+}
+
+
+Handle<Map> Factory::ObjectLiteralMapFromCache(Handle<Context> context,
+ Handle<FixedArray> keys) {
+ if (context->map_cache()->IsUndefined()) {
+ // Allocate the new map cache for the global context.
+ Handle<MapCache> new_cache = NewMapCache(24);
+ context->set_map_cache(*new_cache);
+ }
+ // Check to see whether there is a maching element in the cache.
+ Handle<MapCache> cache =
+ Handle<MapCache>(MapCache::cast(context->map_cache()));
+ Handle<Object> result = Handle<Object>(cache->Lookup(*keys));
+ if (result->IsMap()) return Handle<Map>::cast(result);
+ // Create a new map and add it to the cache.
+ Handle<Map> map =
+ CopyMap(Handle<Map>(context->object_function()->initial_map()));
+ AddToMapCache(context, keys, map);
+ return Handle<Map>(map);
+}
+
+
void Factory::ConfigureInstance(Handle<FunctionTemplateInfo> desc,
Handle<JSObject> instance,
bool* pending_exception) {
static Handle<JSObject> NewJSObject(Handle<JSFunction> constructor,
PretenureFlag pretenure = NOT_TENURED);
+ // JS objects are pretenured when allocated by the bootstrapper and
+ // runtime.
+ static Handle<JSObject> NewJSObjectFromMap(Handle<Map> map);
+
// Allocate a JS object representing an object literal. The object is
// pretenured (allocated directly in the old generation).
static Handle<JSObject> NewObjectLiteral(int expected_number_of_properties);
static Handle<DebugInfo> NewDebugInfo(Handle<SharedFunctionInfo> shared);
+
+ // Return a map using the map cache in the global context.
+ // The key the an ordered set of property names.
+ static Handle<Map> ObjectLiteralMapFromCache(Handle<Context> context,
+ Handle<FixedArray> keys);
+
private:
static Handle<JSFunction> NewFunctionHelper(Handle<String> name,
Handle<Object> prototype);
static Handle<JSFunction> BaseNewFunctionFromBoilerplate(
Handle<JSFunction> boilerplate,
Handle<Map> function_map);
+
+ // Create a new map cache.
+ static Handle<MapCache> NewMapCache(int at_least_space_for);
+
+ // Update the map cache in the global context with (keys, map)
+ static Handle<MapCache> AddToMapCache(Handle<Context> context,
+ Handle<FixedArray> keys,
+ Handle<Map> map);
};
DEFINE_bool(cleanup_caches_in_maps_at_gc, true,
"Flush code caches in maps during mark compact cycle.")
+DEFINE_bool(canonicalize_object_literal_maps, true,
+ "Canonicalize maps for object literals.")
+
// mksnapshot.cc
DEFINE_bool(h, false, "print this message")
}
+bool Object::IsMapCache() {
+ return IsHashTable();
+}
+
+
bool Object::IsPrimitive() {
return IsOddball() || IsNumber() || IsString();
}
CAST_ACCESSOR(Dictionary)
CAST_ACCESSOR(SymbolTable)
CAST_ACCESSOR(CompilationCacheTable)
+CAST_ACCESSOR(MapCache)
CAST_ACCESSOR(String)
CAST_ACCESSOR(SeqString)
CAST_ACCESSOR(AsciiString)
}
+Context* JSFunction::GlobalContextFromLiterals(FixedArray* literals) {
+ return Context::cast(literals->get(JSFunction::kLiteralGlobalContextIndex));
+}
+
+
void Oddball::OddballIterateBody(ObjectVisitor* v) {
// Assumes all Object* members are contiguously allocated!
IteratePointers(v, kToStringOffset, kToNumberOffset + kPointerSize);
Object* obj = Allocate(nof * 2);
if (obj->IsFailure()) return obj;
- HashTable* dict = HashTable::cast(obj);
- WriteBarrierMode mode = dict->GetWriteBarrierMode();
+ HashTable* table = HashTable::cast(obj);
+ WriteBarrierMode mode = table->GetWriteBarrierMode();
// Copy prefix to new array.
for (int i = kPrefixStartIndex; i < kPrefixStartIndex + prefix_size; i++) {
- dict->set(i, get(i), mode);
+ table->set(i, get(i), mode);
}
// Rehash the elements.
uint32_t (*Hash)(Object* key) = key->GetHashFunction();
Object* key = get(from_index);
if (IsKey(key)) {
uint32_t insertion_index =
- EntryToIndex(dict->FindInsertionEntry(key, Hash(key)));
+ EntryToIndex(table->FindInsertionEntry(key, Hash(key)));
for (int j = 0; j < element_size; j++) {
- dict->set(insertion_index + j, get(from_index + j), mode);
+ table->set(insertion_index + j, get(from_index + j), mode);
}
}
}
- dict->SetNumberOfElements(NumberOfElements());
- return dict;
+ table->SetNumberOfElements(NumberOfElements());
+ return table;
}
}
+// SymbolsKey used for HashTable where key is array of symbols.
+class SymbolsKey : public HashTableKey {
+ public:
+ explicit SymbolsKey(FixedArray* symbols) {
+ symbols_ = symbols;
+ }
+
+ bool IsMatch(Object* other) {
+ if (!other->IsFixedArray()) return false;
+ FixedArray* o = FixedArray::cast(other);
+ int len = symbols_->length();
+ if (o->length() != len) return false;
+ for (int i = 0; i < len; i++) {
+ if (o->get(i) != symbols_->get(i)) return false;
+ }
+ return true;
+ }
+
+ uint32_t Hash() { return SymbolsHash(symbols_); }
+
+ HashFunction GetHashFunction() { return SymbolsHash; }
+
+ Object* GetObject() { return symbols_; }
+
+ static uint32_t SymbolsHash(Object* obj) {
+ FixedArray* symbols_ = FixedArray::cast(obj);
+ int len = symbols_->length();
+ uint32_t hash = 0;
+ for (int i = 0; i < len; i++) {
+ hash ^= String::cast(symbols_->get(i))->Hash();
+ }
+ return hash;
+ }
+
+ bool IsStringKey() { return false; }
+
+ FixedArray* symbols_;
+};
+
+Object* MapCache::Lookup(FixedArray* array) {
+ SymbolsKey key(array);
+ int entry = FindEntry(&key);
+ if (entry != -1) {
+ return get(EntryToIndex(entry) + 1);
+ } else {
+ return Heap::undefined_value();
+ }
+}
+
+
+Object* MapCache::Put(FixedArray* array, Map* value) {
+ SymbolsKey key(array);
+ Object* obj = EnsureCapacity(1, &key);
+ if (obj->IsFailure()) return obj;
+
+ MapCache* cache = reinterpret_cast<MapCache*>(obj);
+ int entry = cache->FindInsertionEntry(array, key.Hash());
+ cache->set(EntryToIndex(entry), array);
+ 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.
inline bool IsDictionary();
inline bool IsSymbolTable();
inline bool IsCompilationCacheTable();
+ inline bool IsMapCache();
inline bool IsPrimitive();
inline bool IsGlobalObject();
inline bool IsJSGlobalObject();
};
+// MapCache.
+//
+// Maps keys that are a fixed array of symbols to a map.
+// Used for canonicalize maps for object literals.
+class MapCache: public HashTable<0, 2> {
+ public:
+ // Find cached value for a string key, otherwise return null.
+ Object* Lookup(FixedArray* key);
+ Object* Put(FixedArray* key, Map* value);
+ static inline MapCache* cast(Object* obj);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(MapCache);
+};
+
+
// Dictionary for keeping properties and elements in slow case.
//
// One element in the prefix is used for storing non-element
// Returns the number of allocated literals.
int NumberOfLiterals();
+ // Retrieve the global context from a function's literal array.
+ static Context* GlobalContextFromLiterals(FixedArray* literals);
+
// Layout descriptors.
static const int kPrototypeOrInitialMapOffset = JSObject::kHeaderSize;
static const int kSharedFunctionInfoOffset =
static const int kSize = kLiteralsOffset + kPointerSize;
// Layout of the literals array.
- static const int kLiteralsPrefixSize = 3;
- static const int kLiteralObjectFunctionIndex = 0;
- static const int kLiteralRegExpFunctionIndex = 1;
- static const int kLiteralArrayFunctionIndex = 2;
-
+ static const int kLiteralsPrefixSize = 1;
+ static const int kLiteralGlobalContextIndex = 0;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(JSFunction);
};
NEW(ObjectLiteral::Property(key, value));
// Count CONSTANT or COMPUTED properties to maintain the enumeration order.
- if (IsBoilerplateProperty(property))
- number_of_boilerplate_properties++;
+ if (IsBoilerplateProperty(property)) number_of_boilerplate_properties++;
properties.Add(property);
// TODO(1240767): Consider allowing trailing comma.
}
+static Handle<Map> ComputeObjectLiteralMap(
+ Handle<Context> context,
+ Handle<FixedArray> constant_properties,
+ bool &is_result_from_cache) {
+ if (FLAG_canonicalize_object_literal_maps) {
+ // First find prefix of consecutive symbol keys.
+ int number_of_properties = constant_properties->length()/2;
+ int number_of_symbol_keys = 0;
+ while ((number_of_symbol_keys < number_of_properties) &&
+ (constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
+ number_of_symbol_keys++;
+ }
+ // Based on the number of prefix symbols key we decide whether
+ // to use the map cache in the global context.
+ const int kMaxKeys = 10;
+ if ((number_of_symbol_keys == number_of_properties)
+ && (number_of_symbol_keys < kMaxKeys)) {
+ // Create the fixed array with the key.
+ Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
+ for (int i = 0; i < number_of_symbol_keys; i++) {
+ keys->set(i, constant_properties->get(i*2));
+ }
+ is_result_from_cache = true;
+ return Factory::ObjectLiteralMapFromCache(context, keys);
+ }
+ }
+ is_result_from_cache = false;
+ return Handle<Map>(context->object_function()->initial_map());
+}
+
+
static Object* Runtime_CreateObjectLiteralBoilerplate(Arguments args) {
HandleScope scope;
ASSERT(args.length() == 3);
Handle<FixedArray> literals = args.at<FixedArray>(0);
int literals_index = Smi::cast(args[1])->value();
Handle<FixedArray> constant_properties = args.at<FixedArray>(2);
+ Handle<Context> context =
+ Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
+
+ bool is_result_from_cache;
+ Handle<Map> map = ComputeObjectLiteralMap(context,
+ constant_properties,
+ is_result_from_cache);
// Get the object function from the literals array. This is the
// object function from the context in which the function was
// created. We do not use the object function from the current
// global context because this might be the object function from
// another context which we should not have access to.
- const int kObjectFunIndex = JSFunction::kLiteralObjectFunctionIndex;
- Handle<JSFunction> constructor =
- Handle<JSFunction>(JSFunction::cast(literals->get(kObjectFunIndex)));
-
- Handle<JSObject> boilerplate = Factory::NewJSObject(constructor, TENURED);
-
+ Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
{ // Add the constant propeties to the boilerplate.
int length = constant_properties->length();
- OptimizedObjectForAddingMultipleProperties opt(boilerplate, true);
+ OptimizedObjectForAddingMultipleProperties opt(boilerplate,
+ !is_result_from_cache);
for (int index = 0; index < length; index +=2) {
Handle<Object> key(constant_properties->get(index+0));
Handle<Object> value(constant_properties->get(index+1));
ASSERT(args.length() == 2);
CONVERT_CHECKED(FixedArray, elements, args[0]);
CONVERT_CHECKED(FixedArray, literals, args[1]);
- const int kArrayFunIndex = JSFunction::kLiteralArrayFunctionIndex;
- JSFunction* constructor = JSFunction::cast(literals->get(kArrayFunIndex));
-
+ JSFunction* constructor =
+ JSFunction::GlobalContextFromLiterals(literals)->array_function();
// Create the JSArray.
Object* object = Heap::AllocateJSObject(constructor);
if (object->IsFailure()) return object;
// created. We do not use the RegExp function from the current
// global context because this might be the RegExp function from
// another context which we should not have access to.
- const int kRegexpFunIndex = JSFunction::kLiteralRegExpFunctionIndex;
Handle<JSFunction> constructor =
- Handle<JSFunction>(JSFunction::cast(literals->get(kRegexpFunIndex)));
-
+ Handle<JSFunction>(
+ JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
// Compute the regular expression literal.
bool has_pending_exception;
Handle<Object> regexp =
// Insert the object, regexp and array functions in the literals
// array prefix. These are the functions that will be used when
// creating object, regexp and array literals.
- literals->set(JSFunction::kLiteralObjectFunctionIndex,
- context->global_context()->object_function());
- literals->set(JSFunction::kLiteralRegExpFunctionIndex,
- context->global_context()->regexp_function());
- literals->set(JSFunction::kLiteralArrayFunctionIndex,
- context->global_context()->array_function());
+ literals->set(JSFunction::kLiteralGlobalContextIndex,
+ context->global_context());
}
target->set_literals(*literals);
}