}
+void StubCompiler::GenerateDictionaryLoadCallback(Register receiver,
+ Register name_reg,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Handle<AccessorInfo> callback,
+ Handle<String> name,
+ Label* miss) {
+ Register dictionary = scratch1;
+ Register index = scratch2;
+ __ ldr(dictionary, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
+
+ // Probe the dictionary.
+ Label probe_done;
+ StringDictionaryLookupStub::GeneratePositiveLookup(masm(),
+ miss,
+ &probe_done,
+ dictionary,
+ name_reg,
+ index, // Set if we hit.
+ scratch3);
+ __ bind(&probe_done);
+
+ // If probing finds an entry in the dictionary, check that the value is the
+ // callback.
+ const int kElementsStartOffset =
+ StringDictionary::kHeaderSize +
+ StringDictionary::kElementsStartIndex * kPointerSize;
+ const int kValueOffset = kElementsStartOffset + kPointerSize;
+ __ add(scratch1, dictionary, Operand(kValueOffset - kHeapObjectTag));
+ __ ldr(scratch3, MemOperand(scratch1, index, LSL, kPointerSizeLog2));
+ __ cmp(scratch3, Operand(callback));
+ __ b(ne, miss);
+}
+
+
void StubCompiler::GenerateLoadCallback(Handle<JSObject> object,
Handle<JSObject> holder,
Register receiver,
Register reg = CheckPrototypes(object, receiver, holder, scratch1,
scratch2, scratch3, name, miss);
+ if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) {
+ GenerateDictionaryLoadCallback(
+ receiver, name_reg, scratch1, scratch2, scratch3, callback, name, miss);
+ }
+
// Build AccessorInfo::args_ list on the stack and push property name below
// the exit frame to make GC aware of them and store pointers to them.
__ push(receiver);
}
js_global_function->initial_map()->set_is_hidden_prototype();
+ js_global_function->initial_map()->set_dictionary_map(true);
Handle<GlobalObject> inner_global =
factory()->NewGlobalObject(js_global_function);
if (inner_global_out != NULL) {
Handle<String> name = factory()->LookupAsciiSymbol("builtins");
builtins_fun->shared()->set_instance_class_name(*name);
+ builtins_fun->initial_map()->set_dictionary_map(true);
// Allocate the builtins object.
Handle<JSBuiltinsObject> builtins =
if (!maybe_obj->ToObject(&obj)) return false;
}
Map* global_context_map = Map::cast(obj);
+ global_context_map->set_dictionary_map(true);
global_context_map->set_visitor_id(StaticVisitorBase::kVisitGlobalContext);
set_global_context_map(global_context_map);
MaybeObject* Heap::AllocateGlobalObject(JSFunction* constructor) {
ASSERT(constructor->has_initial_map());
Map* map = constructor->initial_map();
+ ASSERT(map->is_dictionary_map());
// Make sure no field properties are described in the initial map.
// This guarantees us that normalizing the properties does not
Map* new_map;
MaybeObject* maybe_map = map->CopyDropDescriptors();
if (!maybe_map->To(&new_map)) return maybe_map;
+ new_map->set_dictionary_map(true);
// Set up the global object as a normalized object.
global->set_map(new_map);
break;
}
} else if (lookup.IsCacheable() &&
+ // For dicts the lookup on the map will fail, but the object may
+ // contain the property so we cannot generate a negative lookup
+ // (which would just be a map check and return undefined).
+ !map->is_dictionary_map() &&
PrototypeChainCanNeverResolve(map, name)) {
negative_lookups.Add(types->at(i), zone);
}
HInstruction* instr = NULL;
SmallMapList* types = expr->GetReceiverTypes();
- if (expr->IsMonomorphic()) {
- Handle<Map> map = types->first();
+ bool monomorphic = expr->IsMonomorphic();
+ Handle<Map> map;
+ if (monomorphic) {
+ map = types->first();
+ if (map->is_dictionary_map()) monomorphic = false;
+ }
+ if (monomorphic) {
Handle<AccessorPair> accessors;
Handle<JSObject> holder;
if (LookupAccessorPair(map, name, &accessors, &holder)) {
Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
Handle<Map> map;
HInstruction* load;
- if (prop->IsMonomorphic()) {
+ bool monomorphic = prop->IsMonomorphic();
+ if (monomorphic) {
map = prop->GetReceiverTypes()->first();
+ // We can't generate code for a monomorphic dict mode load so
+ // just pretend it is not monomorphic.
+ if (map->is_dictionary_map()) monomorphic = false;
+ }
+ if (monomorphic) {
Handle<AccessorPair> accessors;
Handle<JSObject> holder;
if (LookupAccessorPair(map, name, &accessors, &holder)) {
if (instr->HasObservableSideEffects()) AddSimulate(operation->id());
HInstruction* store;
- if (map.is_null()) {
+ if (!monomorphic) {
// If we don't know the monomorphic type, do a generic store.
CHECK_ALIVE(store = BuildStoreNamedGeneric(object, name, instr));
} else {
Property* expr,
Handle<Map> map) {
// Handle a load from a known field.
+ ASSERT(!map->is_dictionary_map());
LookupResult lookup(isolate());
map->LookupDescriptor(NULL, *name, &lookup);
if (lookup.IsField()) {
Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
SmallMapList* types = expr->GetReceiverTypes();
+ bool monomorphic = expr->IsMonomorphic();
+ Handle<Map> map;
if (expr->IsMonomorphic()) {
- Handle<Map> map = types->first();
+ map = types->first();
+ if (map->is_dictionary_map()) monomorphic = false;
+ }
+ if (monomorphic) {
Handle<AccessorPair> accessors;
Handle<JSObject> holder;
if (LookupAccessorPair(map, name, &accessors, &holder)) {
Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
Handle<Map> map;
HInstruction* load;
- if (prop->IsMonomorphic()) {
+ bool monomorphic = prop->IsMonomorphic();
+ if (monomorphic) {
map = prop->GetReceiverTypes()->first();
+ if (map->is_dictionary_map()) monomorphic = false;
+ }
+ if (monomorphic) {
Handle<AccessorPair> accessors;
Handle<JSObject> holder;
if (LookupAccessorPair(map, name, &accessors, &holder)) {
input = Pop();
HInstruction* store;
- if (map.is_null()) {
+ if (!monomorphic) {
// If we don't know the monomorphic type, do a generic store.
CHECK_ALIVE(store = BuildStoreNamedGeneric(object, name, after));
} else {
}
+void StubCompiler::GenerateDictionaryLoadCallback(Register receiver,
+ Register name_reg,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Handle<AccessorInfo> callback,
+ Handle<String> name,
+ Label* miss) {
+ Register dictionary = scratch1;
+ __ mov(dictionary, FieldOperand(receiver, JSObject::kPropertiesOffset));
+
+ // Probe the dictionary.
+ Label probe_done;
+ StringDictionaryLookupStub::GeneratePositiveLookup(masm(),
+ miss,
+ &probe_done,
+ dictionary,
+ name_reg,
+ scratch2,
+ scratch3);
+ __ bind(&probe_done);
+
+ // If probing finds an entry in the dictionary, scratch2 contains the
+ // index into the dictionary. Check that the value is the callback.
+ Register index = scratch2;
+ const int kElementsStartOffset =
+ StringDictionary::kHeaderSize +
+ StringDictionary::kElementsStartIndex * kPointerSize;
+ const int kValueOffset = kElementsStartOffset + kPointerSize;
+ __ mov(scratch3,
+ Operand(dictionary, index, times_4, kValueOffset - kHeapObjectTag));
+ __ cmp(scratch3, callback);
+ __ j(not_equal, miss);
+}
+
+
void StubCompiler::GenerateLoadCallback(Handle<JSObject> object,
Handle<JSObject> holder,
Register receiver,
Register reg = CheckPrototypes(object, receiver, holder, scratch1,
scratch2, scratch3, name, miss);
+ if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) {
+ GenerateDictionaryLoadCallback(
+ receiver, name_reg, scratch1, scratch2, scratch3, callback, name, miss);
+ }
+
// Insert additional parameters into the stack frame above return address.
ASSERT(!scratch3.is(reg));
__ pop(scratch3); // Get return address to place it below.
if (callback->IsAccessorInfo()) {
Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(callback);
if (v8::ToCData<Address>(info->getter()) == 0) return;
- if (!holder->HasFastProperties()) return;
if (!info->IsCompatibleReceiver(*receiver)) return;
code = isolate()->stub_cache()->ComputeLoadCallback(
name, receiver, holder, info);
Handle<Object> getter(Handle<AccessorPair>::cast(callback)->getter());
if (!getter->IsJSFunction()) return;
if (holder->IsGlobalObject()) return;
- if (!holder->HasFastProperties()) return;
code = isolate()->stub_cache()->ComputeLoadViaGetter(
name, receiver, holder, Handle<JSFunction>::cast(getter));
} else {
bool JSObject::HasFastProperties() {
+ ASSERT(properties()->IsDictionary() == map()->is_dictionary_map());
return !properties()->IsDictionary();
}
set_bit_field3(IsShared::update(bit_field3(), value));
}
+
bool Map::is_shared() {
return IsShared::decode(bit_field3());
}
+void Map::set_dictionary_map(bool value) {
+ set_bit_field3(DictionaryMap::update(bit_field3(), value));
+}
+
+
+bool Map::is_dictionary_map() {
+ return DictionaryMap::decode(bit_field3());
+}
+
+
JSFunction* Map::unchecked_constructor() {
return reinterpret_cast<JSFunction*>(READ_FIELD(this, kConstructorOffset));
}
MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+ ASSERT(new_map->is_dictionary_map());
set_map(new_map);
}
JSGlobalPropertyCell* cell =
fast->CopyNormalized(mode, SHARED_NORMALIZED_MAP);
if (!maybe_result->ToObject(&result)) return maybe_result;
}
+ ASSERT(Map::cast(result)->is_dictionary_map());
set(index, result);
isolate->counters()->normalized_maps()->Increment();
current_heap->isolate()->context()->global_context()->
normalized_map_cache()->Get(this, mode);
if (!maybe_map->To(&new_map)) return maybe_map;
+ ASSERT(new_map->is_dictionary_map());
// We have now successfully allocated all the necessary objects.
// Changes can now be made with the guarantee that all of them take effect.
Map* new_map;
MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+ ASSERT(new_map->is_dictionary_map());
set_map(new_map);
// When running crankshaft, changing the map is not enough. We
result->set_code_cache(code_cache());
result->set_is_shared(sharing == SHARED_NORMALIZED_MAP);
+ result->set_dictionary_map(true);
#ifdef DEBUG
if (FLAG_verify_heap && result->is_shared()) {
bit_field2() == other->bit_field2() &&
static_cast<uint32_t>(bit_field3()) ==
LastAddedBits::update(
- IsShared::update(other->bit_field3(), true),
+ IsShared::update(DictionaryMap::update(other->bit_field3(), true),
+ true),
kNoneAdded);
}
Map* new_map;
MaybeObject* maybe_new_map = obj->map()->CopyDropDescriptors();
if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+ new_map->set_dictionary_map(false);
if (instance_descriptor_length == 0) {
ASSERT_LE(unused_property_fields, inobject_props);
class IsShared: public BitField<bool, 0, 1> {};
class FunctionWithPrototype: public BitField<bool, 1, 1> {};
- class LastAddedBits: public BitField<int, 2, 11> {};
+ class DictionaryMap: public BitField<bool, 2, 1> {};
+ class LastAddedBits: public BitField<int, 3, 11> {};
// Tells whether the object in the prototype property will be used
// for instances created from this function. If the prototype
inline void set_is_shared(bool value);
inline bool is_shared();
+ // Tells whether the map is used for JSObjects in dictionary mode (ie
+ // normalized objects, ie objects for which HasFastProperties returns false).
+ // A map can never be used for both dictionary mode and fast mode JSObjects.
+ // False by default and for HeapObjects that are not JSObjects.
+ inline void set_dictionary_map(bool value);
+ inline bool is_dictionary_map();
+
// Tells whether the instance needs security checks when accessing its
// properties.
inline void set_is_access_check_needed(bool access_check_needed);
// Bit positions for bit field 3
static const int kIsShared = 0;
static const int kFunctionWithPrototype = 1;
+ static const int kDictionaryMap = 2;
typedef FixedBodyDescriptor<kPointerFieldsBeginOffset,
kPointerFieldsEndOffset,
Handle<String> name,
Label* miss);
+ void GenerateDictionaryLoadCallback(Register receiver,
+ Register name_reg,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Handle<AccessorInfo> callback,
+ Handle<String> name,
+ Label* miss);
+
void GenerateLoadConstant(Handle<JSObject> object,
Handle<JSObject> holder,
Register receiver,
if (map_or_code->IsMap()) return true;
if (map_or_code->IsCode()) {
Handle<Code> code = Handle<Code>::cast(map_or_code);
- return code->is_keyed_load_stub() &&
+ bool preliminary_checks = code->is_keyed_load_stub() &&
code->ic_state() == MONOMORPHIC &&
- Code::ExtractTypeFromFlags(code->flags()) == Code::NORMAL &&
- code->FindFirstMap() != NULL &&
- !CanRetainOtherContext(code->FindFirstMap(), *global_context_);
+ Code::ExtractTypeFromFlags(code->flags()) == Code::NORMAL;
+ if (!preliminary_checks) return false;
+ Map* map = code->FindFirstMap();
+ return map != NULL && !CanRetainOtherContext(map, *global_context_);
}
return false;
}
bool allow_growth =
Code::GetKeyedAccessGrowMode(code->extra_ic_state()) ==
ALLOW_JSARRAY_GROWTH;
- return code->is_keyed_store_stub() &&
+ bool preliminary_checks =
+ code->is_keyed_store_stub() &&
!allow_growth &&
code->ic_state() == MONOMORPHIC &&
- Code::ExtractTypeFromFlags(code->flags()) == Code::NORMAL &&
- code->FindFirstMap() != NULL &&
- !CanRetainOtherContext(code->FindFirstMap(), *global_context_);
+ Code::ExtractTypeFromFlags(code->flags()) == Code::NORMAL;
+ if (!preliminary_checks) return false;
+ Map* map = code->FindFirstMap();
+ return map != NULL && !CanRetainOtherContext(map, *global_context_);
}
return false;
}
}
+void StubCompiler::GenerateDictionaryLoadCallback(Register receiver,
+ Register name_reg,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Handle<AccessorInfo> callback,
+ Handle<String> name,
+ Label* miss) {
+ Register dictionary = scratch1;
+ __ movq(dictionary, FieldOperand(receiver, JSObject::kPropertiesOffset));
+
+ // Probe the dictionary.
+ Label probe_done;
+ StringDictionaryLookupStub::GeneratePositiveLookup(masm(),
+ miss,
+ &probe_done,
+ dictionary,
+ name_reg,
+ scratch2,
+ scratch3);
+ __ bind(&probe_done);
+
+ // If probing finds an entry in the dictionary, scratch2 contains the
+ // index into the dictionary. Check that the value is the callback.
+ Register index = scratch2;
+ const int kElementsStartOffset =
+ StringDictionary::kHeaderSize +
+ StringDictionary::kElementsStartIndex * kPointerSize;
+ const int kValueOffset = kElementsStartOffset + kPointerSize;
+ __ movq(scratch3,
+ Operand(dictionary, index, times_8, kValueOffset - kHeapObjectTag));
+ __ movq(scratch2, callback, RelocInfo::EMBEDDED_OBJECT);
+ __ cmpq(scratch3, scratch2);
+ __ j(not_equal, miss);
+}
+
+
void StubCompiler::GenerateLoadCallback(Handle<JSObject> object,
Handle<JSObject> holder,
Register receiver,
Register reg = CheckPrototypes(object, receiver, holder, scratch1,
scratch2, scratch3, name, miss);
+ if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) {
+ GenerateDictionaryLoadCallback(
+ receiver, name_reg, scratch1, scratch2, scratch3, callback, name, miss);
+ }
+
// Insert additional parameters into the stack frame above return address.
ASSERT(!scratch2.is(reg));
__ pop(scratch2); // Get return address to place it below.
}
-THREADED_TEST(Regress137002a) {
- i::FLAG_allow_natives_syntax = true;
- v8::HandleScope scope;
+static void Helper137002(bool do_store,
+ bool polymorphic,
+ bool remove_accessor) {
LocalContext context;
Local<ObjectTemplate> templ = ObjectTemplate::New();
templ->SetAccessor(v8_str("foo"),
// Turn monomorphic on slow object with native accessor, then turn
// polymorphic, finally optimize to create negative lookup and fail.
- CompileRun("function f(x) { return x.foo; }"
+ CompileRun(do_store ?
+ "function f(x) { x.foo = void 0; }" :
+ "function f(x) { return x.foo; }");
+ CompileRun("obj.y = void 0;"
"%OptimizeObjectForAddingMultipleProperties(obj, 1);"
"obj.__proto__ = null;"
- "f(obj); f(obj); f({});"
- "%OptimizeFunctionOnNextCall(f);"
- "var result = f(obj);");
- CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
+ "f(obj); f(obj);");
+ if (polymorphic) {
+ CompileRun("f({});");
+ }
+ CompileRun("obj.y = void 0;"
+ "%OptimizeFunctionOnNextCall(f);");
+ if (remove_accessor) {
+ CompileRun("delete obj.foo;");
+ }
+ CompileRun("var result = f(obj);");
+ if (do_store) {
+ CompileRun("result = obj.y;");
+ }
+ if (remove_accessor) {
+ CHECK(context->Global()->Get(v8_str("result"))->IsUndefined());
+ } else {
+ CHECK_EQ(do_store ? 23 : 42,
+ context->Global()->Get(v8_str("result"))->Int32Value());
+ }
+}
+
+
+THREADED_TEST(Regress137002a) {
+ i::FLAG_allow_natives_syntax = true;
+ i::FLAG_compilation_cache = false;
+ v8::HandleScope scope;
+ Helper137002(false, false, false);
+ Helper137002(false, false, true);
+ Helper137002(false, true, false);
+ Helper137002(false, true, true);
+ Helper137002(true, false, false);
+ Helper137002(true, false, true);
+ Helper137002(true, true, false);
+ Helper137002(true, true, true);
}