Commit 60dafcaa authored by jkummerow's avatar jkummerow Committed by Commit bot

Add infrastructure to keep track of references to prototypes.

There are no users of this infrastructure yet, so it's behind an off-by-default flag.

Review URL: https://codereview.chromium.org/768633002

Cr-Commit-Position: refs/heads/master@{#25829}
parent aed5d734
......@@ -362,7 +362,7 @@ static void SetObjectPrototype(Handle<JSObject> object, Handle<Object> proto) {
// object.__proto__ = proto;
Handle<Map> old_map = Handle<Map>(object->map());
Handle<Map> new_map = Map::Copy(old_map, "SetObjectPrototype");
new_map->set_prototype(*proto);
new_map->SetPrototype(proto, FAST_PROTOTYPE);
JSObject::MigrateToMap(object, new_map);
}
......@@ -493,6 +493,8 @@ Handle<JSFunction> Genesis::CreateEmptyFunction(Isolate* isolate) {
Handle<String> object_name = factory->Object_string();
Handle<JSObject> object_function_prototype;
{ // --- O b j e c t ---
Handle<JSFunction> object_fun = factory->NewFunction(object_name);
int unused = JSObject::kInitialGlobalObjectUnusedPropertiesCount;
......@@ -507,20 +509,20 @@ Handle<JSFunction> Genesis::CreateEmptyFunction(Isolate* isolate) {
native_context()->set_object_function(*object_fun);
// Allocate a new prototype for the object function.
Handle<JSObject> prototype = factory->NewJSObject(
isolate->object_function(),
TENURED);
Handle<Map> map =
Map::Copy(handle(prototype->map()), "EmptyObjectPrototype");
object_function_prototype =
factory->NewJSObject(isolate->object_function(), TENURED);
Handle<Map> map = Map::Copy(handle(object_function_prototype->map()),
"EmptyObjectPrototype");
map->set_is_prototype_map(true);
prototype->set_map(*map);
object_function_prototype->set_map(*map);
native_context()->set_initial_object_prototype(*prototype);
native_context()->set_initial_object_prototype(*object_function_prototype);
// For bootstrapping set the array prototype to be the same as the object
// prototype, otherwise the missing initial_array_prototype will cause
// assertions during startup.
native_context()->set_initial_array_prototype(*prototype);
Accessors::FunctionSetPrototype(object_fun, prototype).Assert();
native_context()->set_initial_array_prototype(*object_function_prototype);
Accessors::FunctionSetPrototype(object_fun, object_function_prototype)
.Assert();
}
// Allocate the empty function as the prototype for function ECMAScript
......@@ -535,8 +537,7 @@ Handle<JSFunction> Genesis::CreateEmptyFunction(Isolate* isolate) {
Handle<Map> empty_function_map =
CreateFunctionMap(FUNCTION_WITHOUT_PROTOTYPE);
DCHECK(!empty_function_map->is_dictionary_map());
empty_function_map->set_prototype(
native_context()->object_function()->prototype());
empty_function_map->SetPrototype(object_function_prototype);
empty_function_map->set_is_prototype_map(true);
empty_function->set_map(*empty_function_map);
......@@ -550,10 +551,10 @@ Handle<JSFunction> Genesis::CreateEmptyFunction(Isolate* isolate) {
empty_function->shared()->DontAdaptArguments();
// Set prototypes for the function maps.
native_context()->sloppy_function_map()->set_prototype(*empty_function);
native_context()->sloppy_function_without_prototype_map()->
set_prototype(*empty_function);
sloppy_function_map_writable_prototype_->set_prototype(*empty_function);
native_context()->sloppy_function_map()->SetPrototype(empty_function);
native_context()->sloppy_function_without_prototype_map()->SetPrototype(
empty_function);
sloppy_function_map_writable_prototype_->SetPrototype(empty_function);
return empty_function;
}
......@@ -655,7 +656,7 @@ Handle<Map> Genesis::CreateStrictFunctionMap(
Handle<Map> map = factory()->NewMap(JS_FUNCTION_TYPE, JSFunction::kSize);
SetStrictFunctionInstanceDescriptor(map, function_mode);
map->set_function_with_prototype(IsFunctionModeWithPrototype(function_mode));
map->set_prototype(*empty_function);
map->SetPrototype(empty_function);
return map;
}
......@@ -1093,7 +1094,7 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> global_object,
// RegExp prototype object is itself a RegExp.
Handle<Map> proto_map = Map::Copy(initial_map, "RegExpPrototype");
proto_map->set_prototype(native_context()->initial_object_prototype());
DCHECK(proto_map->prototype() == *isolate->initial_object_prototype());
Handle<JSObject> proto = factory->NewJSObjectFromMap(proto_map);
proto->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex,
heap->false_value());
......@@ -1105,7 +1106,7 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> global_object,
Smi::FromInt(0),
SKIP_WRITE_BARRIER); // It's a Smi.
proto_map->set_is_prototype_map(true);
initial_map->set_prototype(*proto);
initial_map->SetPrototype(proto);
factory->SetRegExpIrregexpData(Handle<JSRegExp>::cast(proto),
JSRegExp::IRREGEXP, factory->empty_string(),
JSRegExp::Flags(0), 0);
......@@ -1290,7 +1291,9 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> global_object,
// @@iterator method is added later.
map->set_function_with_prototype(true);
map->set_prototype(native_context()->object_function()->prototype());
DCHECK_EQ(native_context()->object_function()->prototype(),
*isolate->initial_object_prototype());
map->SetPrototype(isolate->initial_object_prototype());
map->set_pre_allocated_property_fields(1);
map->set_inobject_properties(1);
......@@ -1936,7 +1939,7 @@ bool Genesis::InstallNatives() {
// maps in the native context.
Handle<Map> generator_function_map =
Map::Copy(sloppy_function_map_writable_prototype_, "GeneratorFunction");
generator_function_map->set_prototype(*generator_function_prototype);
generator_function_map->SetPrototype(generator_function_prototype);
native_context()->set_sloppy_generator_function_map(
*generator_function_map);
......@@ -1969,13 +1972,13 @@ bool Genesis::InstallNatives() {
Handle<Map> strict_generator_function_map =
Map::Copy(strict_function_map, "StrictGeneratorFunction");
// "arguments" and "caller" already poisoned.
strict_generator_function_map->set_prototype(*generator_function_prototype);
strict_generator_function_map->SetPrototype(generator_function_prototype);
native_context()->set_strict_generator_function_map(
*strict_generator_function_map);
Handle<JSFunction> object_function(native_context()->object_function());
Handle<Map> generator_object_prototype_map = Map::Create(isolate(), 0);
generator_object_prototype_map->set_prototype(*generator_object_prototype);
generator_object_prototype_map->SetPrototype(generator_object_prototype);
native_context()->set_generator_object_prototype_map(
*generator_object_prototype_map);
}
......@@ -2076,7 +2079,7 @@ bool Genesis::InstallNatives() {
// Set prototype on map.
initial_map->set_non_instance_prototype(false);
initial_map->set_prototype(*array_prototype);
initial_map->SetPrototype(array_prototype);
// Update map with length accessor from Array and add "index" and "input".
Map::EnsureDescriptorSlack(initial_map, 3);
......
......@@ -1878,7 +1878,7 @@ Handle<JSProxy> Factory::NewJSProxy(Handle<Object> handler,
// TODO(rossberg): Once we optimize proxies, think about a scheme to share
// maps. Will probably depend on the identity of the handler object, too.
Handle<Map> map = NewMap(JS_PROXY_TYPE, JSProxy::kSize);
map->set_prototype(*prototype);
map->SetPrototype(prototype);
// Allocate the proxy object.
Handle<JSProxy> result = New<JSProxy>(map, NEW_SPACE);
......@@ -1897,7 +1897,7 @@ Handle<JSProxy> Factory::NewJSFunctionProxy(Handle<Object> handler,
// TODO(rossberg): Once we optimize proxies, think about a scheme to share
// maps. Will probably depend on the identity of the handler object, too.
Handle<Map> map = NewMap(JS_FUNCTION_PROXY_TYPE, JSFunctionProxy::kSize);
map->set_prototype(*prototype);
map->SetPrototype(prototype);
// Allocate the proxy object.
Handle<JSFunctionProxy> result = New<JSFunctionProxy>(map, NEW_SPACE);
......@@ -1922,7 +1922,7 @@ void Factory::ReinitializeJSProxy(Handle<JSProxy> proxy, InstanceType type,
int size_difference = proxy->map()->instance_size() - map->instance_size();
DCHECK(size_difference >= 0);
map->set_prototype(proxy->map()->prototype());
map->SetPrototype(handle(proxy->map()->prototype(), proxy->GetIsolate()));
// Allocate the backing storage for the properties.
int prop_size = map->InitialPropertiesLength();
......
......@@ -645,6 +645,9 @@ DEFINE_INT(random_seed, 0,
"(0, the default, means to use system random).")
// objects.cc
DEFINE_BOOL(trace_weak_arrays, false, "trace WeakFixedArray usage")
DEFINE_BOOL(track_prototype_users, false,
"keep track of which maps refer to a given prototype object")
DEFINE_BOOL(use_verbose_printer, true, "allows verbose printing")
#if TRACE_MAPS
DEFINE_BOOL(trace_maps, false, "trace map creation")
......
......@@ -284,6 +284,7 @@ namespace internal {
V(frozen_symbol) \
V(nonexistent_symbol) \
V(elements_transition_symbol) \
V(prototype_users_symbol) \
V(observed_symbol) \
V(uninitialized_symbol) \
V(megamorphic_symbol) \
......
......@@ -802,14 +802,26 @@ static MayAccessDecision MayAccessPreCheck(Isolate* isolate,
}
bool Isolate::IsInternallyUsedPropertyName(Handle<Object> name) {
return name.is_identical_to(factory()->hidden_string()) ||
name.is_identical_to(factory()->prototype_users_symbol());
}
bool Isolate::IsInternallyUsedPropertyName(Object* name) {
return name == heap()->hidden_string() ||
name == heap()->prototype_users_symbol();
}
bool Isolate::MayNamedAccess(Handle<JSObject> receiver,
Handle<Object> key,
v8::AccessType type) {
DCHECK(receiver->IsJSGlobalProxy() || receiver->IsAccessCheckNeeded());
// Skip checks for hidden properties access. Note, we do not
// Skip checks for internally used properties. Note, we do not
// require existence of a context in this case.
if (key.is_identical_to(factory()->hidden_string())) return true;
if (IsInternallyUsedPropertyName(key)) return true;
// Check for compatibility between the security tokens in the
// current lexical context and the accessed object.
......
......@@ -762,6 +762,8 @@ class Isolate {
bool MayIndexedAccess(Handle<JSObject> receiver,
uint32_t index,
v8::AccessType type);
bool IsInternallyUsedPropertyName(Handle<Object> name);
bool IsInternallyUsedPropertyName(Object* name);
void SetFailedAccessCheckCallback(v8::FailedAccessCheckCallback callback);
void ReportFailedAccessCheck(Handle<JSObject> receiver, v8::AccessType type);
......
......@@ -127,7 +127,7 @@ void LookupIterator::PrepareTransitionToDataProperty(
// observable.
Handle<JSObject> receiver = GetStoreTarget();
if (!name().is_identical_to(isolate()->factory()->hidden_string()) &&
if (!isolate()->IsInternallyUsedPropertyName(name()) &&
!receiver->map()->is_extensible()) {
return;
}
......
......@@ -177,7 +177,8 @@ class LookupIterator FINAL BASE_EMBEDDED {
static Configuration ComputeConfiguration(
Configuration configuration, Handle<Name> name) {
if (name->IsOwn()) {
return static_cast<Configuration>(configuration & HIDDEN);
return static_cast<Configuration>(configuration &
HIDDEN_SKIP_INTERCEPTOR);
} else {
return configuration;
}
......
......@@ -707,6 +707,7 @@ TYPE_CHECKER(JSContextExtensionObject, JS_CONTEXT_EXTENSION_OBJECT_TYPE)
TYPE_CHECKER(Map, MAP_TYPE)
TYPE_CHECKER(FixedArray, FIXED_ARRAY_TYPE)
TYPE_CHECKER(FixedDoubleArray, FIXED_DOUBLE_ARRAY_TYPE)
TYPE_CHECKER(WeakFixedArray, FIXED_ARRAY_TYPE)
TYPE_CHECKER(ConstantPoolArray, CONSTANT_POOL_ARRAY_TYPE)
......@@ -2368,6 +2369,39 @@ void FixedDoubleArray::FillWithHoles(int from, int to) {
}
Object* WeakFixedArray::Get(int index) const {
Object* raw = FixedArray::cast(this)->get(index + kFirstIndex);
if (raw->IsSmi()) return raw;
return WeakCell::cast(raw)->value();
}
bool WeakFixedArray::IsEmptySlot(int index) const {
DCHECK(index < Length());
return Get(index)->IsSmi();
}
void WeakFixedArray::clear(int index) {
FixedArray::cast(this)->set(index + kFirstIndex, Smi::FromInt(0));
}
int WeakFixedArray::Length() const {
return FixedArray::cast(this)->length() - kFirstIndex;
}
int WeakFixedArray::last_used_index() const {
return Smi::cast(FixedArray::cast(this)->get(kLastUsedIndexIndex))->value();
}
void WeakFixedArray::set_last_used_index(int index) {
FixedArray::cast(this)->set(kLastUsedIndexIndex, Smi::FromInt(index));
}
void ConstantPoolArray::NumberOfEntries::increment(Type type) {
DCHECK(type < NUMBER_OF_TYPES);
element_counts_[type]++;
......@@ -3364,6 +3398,7 @@ CAST_ACCESSOR(Struct)
CAST_ACCESSOR(Symbol)
CAST_ACCESSOR(UnseededNumberDictionary)
CAST_ACCESSOR(WeakCell)
CAST_ACCESSOR(WeakFixedArray)
CAST_ACCESSOR(WeakHashTable)
......
This diff is collapsed.
......@@ -88,6 +88,7 @@
// - ScopeInfo
// - TransitionArray
// - ScriptContextTable
// - WeakFixedArray
// - FixedDoubleArray
// - ExternalArray
// - ExternalUint8ClampedArray
......@@ -943,6 +944,7 @@ template <class C> inline bool Is(Object* obj);
V(DependentCode) \
V(FixedArray) \
V(FixedDoubleArray) \
V(WeakFixedArray) \
V(ConstantPoolArray) \
V(Context) \
V(ScriptContextTable) \
......@@ -1821,6 +1823,10 @@ class JSObject: public JSReceiver {
static void OptimizeAsPrototype(Handle<JSObject> object,
PrototypeOptimizationMode mode);
static void ReoptimizeIfPrototype(Handle<JSObject> object);
static void RegisterPrototypeUser(Handle<JSObject> prototype,
Handle<HeapObject> user);
static void UnregisterPrototypeUser(Handle<JSObject> prototype,
Handle<HeapObject> user);
// Retrieve interceptors.
InterceptorInfo* GetNamedInterceptor();
......@@ -2608,6 +2614,45 @@ class FixedDoubleArray: public FixedArrayBase {
};
class WeakFixedArray : public FixedArray {
public:
enum SearchForDuplicates { kAlwaysAdd, kAddIfNotFound };
// If |maybe_array| is not a WeakFixedArray, a fresh one will be allocated.
static Handle<WeakFixedArray> Add(
Handle<Object> maybe_array, Handle<HeapObject> value,
SearchForDuplicates search_for_duplicates = kAlwaysAdd);
void Remove(Handle<HeapObject> value);
inline Object* Get(int index) const;
inline int Length() const;
DECLARE_CAST(WeakFixedArray)
private:
static const int kLastUsedIndexIndex = 0;
static const int kFirstIndex = 1;
static Handle<WeakFixedArray> Allocate(
Isolate* isolate, int size, Handle<WeakFixedArray> initialize_from);
static void Set(Handle<WeakFixedArray> array, int index,
Handle<HeapObject> value);
inline void clear(int index);
inline bool IsEmptySlot(int index) const;
inline int last_used_index() const;
inline void set_last_used_index(int index);
// Disallow inherited setters.
void set(int index, Smi* value);
void set(int index, Object* value);
void set(int index, Object* value, WriteBarrierMode mode);
DISALLOW_IMPLICIT_CONSTRUCTORS(WeakFixedArray);
};
// ConstantPoolArray describes a fixed-sized array containing constant pool
// entries.
//
......@@ -5926,6 +5971,11 @@ class Map: public HeapObject {
// [prototype]: implicit prototype object.
DECL_ACCESSORS(prototype, Object)
// TODO(jkummerow): make set_prototype private.
void SetPrototype(Handle<Object> prototype,
PrototypeOptimizationMode proto_mode = FAST_PROTOTYPE);
bool ShouldRegisterAsPrototypeUser(Handle<JSObject> prototype);
bool CanUseOptimizationsBasedOnPrototypeRegistry();
// [constructor]: points back to the function responsible for this map.
DECL_ACCESSORS(constructor, Object)
......@@ -6257,10 +6307,11 @@ class Map: public HeapObject {
// the original map. That way we can transition to the same map if the same
// prototype is set, rather than creating a new map every time. The
// transitions are in the form of a map where the keys are prototype objects
// and the values are the maps the are transitioned to.
// and the values are the maps they transition to.
static const int kMaxCachedPrototypeTransitions = 256;
static Handle<Map> TransitionToPrototype(Handle<Map> map,
Handle<Object> prototype);
Handle<Object> prototype,
PrototypeOptimizationMode mode);
static const int kMaxPreAllocatedPropertyFields = 255;
......
......@@ -98,7 +98,7 @@ RUNTIME_FUNCTION(Runtime_DefineClass) {
Handle<Map> map =
isolate->factory()->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
map->set_prototype(*prototype_parent);
map->SetPrototype(prototype_parent);
map->set_constructor(*constructor);
Handle<JSObject> prototype = isolate->factory()->NewJSObjectFromMap(map);
......
......@@ -2169,7 +2169,7 @@ static Handle<JSObject> NewJSObjectWithNullProto(Isolate* isolate) {
isolate->factory()->NewJSObject(isolate->object_function());
Handle<Map> new_map =
Map::Copy(Handle<Map>(result->map()), "ObjectWithNullProto");
new_map->set_prototype(*isolate->factory()->null_value());
new_map->SetPrototype(isolate->factory()->null_value());
JSObject::MigrateToMap(result, new_map);
return result;
}
......
......@@ -1010,31 +1010,27 @@ RUNTIME_FUNCTION(Runtime_GetOwnPropertyNames) {
Handle<JSObject> jsproto =
Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
jsproto->GetOwnPropertyNames(*names, next_copy_index, filter);
if (i > 0) {
// Names from hidden prototypes may already have been added
// for inherited function template instances. Count the duplicates
// and stub them out; the final copy pass at the end ignores holes.
for (int j = next_copy_index;
j < next_copy_index + own_property_count[i]; j++) {
Object* name_from_hidden_proto = names->get(j);
// Names from hidden prototypes may already have been added
// for inherited function template instances. Count the duplicates
// and stub them out; the final copy pass at the end ignores holes.
for (int j = next_copy_index; j < next_copy_index + own_property_count[i];
j++) {
Object* name_from_hidden_proto = names->get(j);
if (isolate->IsInternallyUsedPropertyName(name_from_hidden_proto)) {
hidden_strings++;
} else {
for (int k = 0; k < next_copy_index; k++) {
if (names->get(k) != isolate->heap()->hidden_string()) {
Object* name = names->get(k);
if (name_from_hidden_proto == name) {
names->set(j, isolate->heap()->hidden_string());
hidden_strings++;
break;
}
Object* name = names->get(k);
if (name_from_hidden_proto == name) {
names->set(j, isolate->heap()->hidden_string());
hidden_strings++;
break;
}
}
}
}
next_copy_index += own_property_count[i];
// Hidden properties only show up if the filter does not skip strings.
if ((filter & STRING) == 0 && JSObject::HasHiddenProperties(jsproto)) {
hidden_strings++;
}
iter.Advance();
}
}
......@@ -1047,7 +1043,7 @@ RUNTIME_FUNCTION(Runtime_GetOwnPropertyNames) {
int dest_pos = 0;
for (int i = 0; i < total_property_count; i++) {
Object* name = old_names->get(i);
if (name == isolate->heap()->hidden_string()) {
if (isolate->IsInternallyUsedPropertyName(name)) {
hidden_strings--;
continue;
}
......
......@@ -3280,11 +3280,9 @@ Operand LCodeGen::BuildFastArrayOperand(
return Operand(elements_pointer_reg,
(constant_value << shift_size) + offset);
} else {
// Take the tag bit into account while computing the shift size.
if (key_representation.IsSmi() && (shift_size >= 1)) {
DCHECK(SmiValuesAre31Bits());
shift_size -= kSmiTagSize;
}
// Guaranteed by ArrayInstructionInterface::KeyedAccessIndexRequirement().
DCHECK(key_representation.IsInteger32());
ScaleFactor scale_factor = static_cast<ScaleFactor>(shift_size);
return Operand(elements_pointer_reg,
ToRegister(key),
......
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