Commit 0dab442b authored by sgjesse@chromium.org's avatar sgjesse@chromium.org

Added handling of hidden prototype objects when collecting local properties...

Added handling of hidden prototype objects when collecting local properties for an object mirror. The property names provided by an object mirror now includes all properties from the object and any hidden prototypes merged together.

Changed the name of Runtime_GetPrototype to Runtime_DebugGetPrototype to indicate that it is a debugger related function and changed its implementation to do the correct __proto__ lookup.

Added some more information to the Map debug print.
Review URL: http://codereview.chromium.org/18658

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1126 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 74a17e90
......@@ -525,7 +525,7 @@ ObjectMirror.prototype.prototypeObject = function() {
ObjectMirror.prototype.protoObject = function() {
return MakeMirror(%GetPrototype(this.value_));
return MakeMirror(%DebugGetPrototype(this.value_));
};
......
......@@ -414,6 +414,24 @@ void Map::MapPrint() {
PrintF(" - type: %s\n", TypeToString(instance_type()));
PrintF(" - instance size: %d\n", instance_size());
PrintF(" - unused property fields: %d\n", unused_property_fields());
if (is_hidden_prototype()) {
PrintF(" - hidden_prototype\n");
}
if (has_named_interceptor()) {
PrintF(" - named_interceptor\n");
}
if (has_indexed_interceptor()) {
PrintF(" - indexed_interceptor\n");
}
if (is_undetectable()) {
PrintF(" - undetectable\n");
}
if (has_instance_call_handler()) {
PrintF(" - instance_call_handler\n");
}
if (is_access_check_needed()) {
PrintF(" - access_check_needed\n");
}
PrintF(" - instance descriptors: ");
instance_descriptors()->ShortPrint();
PrintF("\n - prototype: ");
......
......@@ -5695,10 +5695,10 @@ void FixedArray::SortPairs(FixedArray* smis) {
// Fill in the names of local properties into the supplied storage. The main
// purpose of this function is to provide reflection information for the object
// mirrors.
void JSObject::GetLocalPropertyNames(FixedArray* storage) {
ASSERT(storage->length() ==
NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE)));
int index = 0;
void JSObject::GetLocalPropertyNames(FixedArray* storage, int index) {
ASSERT(storage->length() >=
NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE)) -
index);
if (HasFastProperties()) {
for (DescriptorReader r(map()->instance_descriptors());
!r.eos();
......@@ -5707,7 +5707,7 @@ void JSObject::GetLocalPropertyNames(FixedArray* storage) {
storage->set(index++, r.GetKey());
}
}
ASSERT(storage->length() == index);
ASSERT(storage->length() >= index);
} else {
property_dictionary()->CopyKeysTo(storage);
}
......
......@@ -1298,8 +1298,9 @@ class JSObject: public HeapObject {
int NumberOfLocalProperties(PropertyAttributes filter);
// Returns the number of enumerable properties (ignoring interceptors).
int NumberOfEnumProperties();
// Fill in details for properties into storage.
void GetLocalPropertyNames(FixedArray* storage);
// Fill in details for properties into storage starting at the specified
// index.
void GetLocalPropertyNames(FixedArray* storage, int index);
// Returns the number of properties on this object filtering out properties
// with the specified attributes (ignoring interceptors).
......
......@@ -4536,6 +4536,21 @@ static Object* Runtime_Break(Arguments args) {
}
// Find the length of the prototype chain that is to to handled as one. If a
// prototype object is hidden it is to be viewed as part of the the object it
// is prototype for.
static int LocalPrototypeChainLength(JSObject* obj) {
int count = 1;
Object* proto = obj->GetPrototype();
while (proto->IsJSObject() &&
JSObject::cast(proto)->map()->is_hidden_prototype()) {
count++;
proto = JSObject::cast(proto)->GetPrototype();
}
return count;
}
static Object* DebugLookupResultValue(Object* receiver, LookupResult* result,
bool* caught_exception) {
Object* value;
......@@ -4611,6 +4626,13 @@ static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
CONVERT_ARG_CHECKED(JSObject, obj, 0);
CONVERT_ARG_CHECKED(String, name, 1);
// Skip the global proxy as it has no properties and always delegates to the
// real global object.
if (obj->IsJSGlobalProxy()) {
obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
}
// Check if the name is trivially convertible to an index and get the element
// if so.
uint32_t index;
......@@ -4621,9 +4643,22 @@ static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
return *Factory::NewJSArrayWithElements(details);
}
// Perform standard local lookup on the object.
// Find the number of objects making up this.
int length = LocalPrototypeChainLength(*obj);
// Try local lookup on each of the objects.
LookupResult result;
obj->LocalLookup(*name, &result);
Handle<JSObject> jsproto = obj;
for (int i = 0; i < length; i++) {
jsproto->LocalLookup(*name, &result);
if (result.IsProperty()) {
break;
}
if (i < length - 1) {
jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
}
}
if (result.IsProperty()) {
bool caught_exception = false;
Handle<Object> value(DebugLookupResultValue(*obj, &result,
......@@ -4676,12 +4711,43 @@ static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
}
CONVERT_ARG_CHECKED(JSObject, obj, 0);
// Skip the global proxy as it has no properties and always delegates to the
// real global object.
if (obj->IsJSGlobalProxy()) {
obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
}
int n = obj->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
Handle<FixedArray> names = Factory::NewFixedArray(n);
obj->GetLocalPropertyNames(*names);
// Find the number of objects making up this.
int length = LocalPrototypeChainLength(*obj);
// Find the number of local properties for each of the objects.
int* local_property_count = NewArray<int>(length);
int total_property_count = 0;
Handle<JSObject> jsproto = obj;
for (int i = 0; i < length; i++) {
int n;
n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
local_property_count[i] = n;
total_property_count += n;
if (i < length - 1) {
jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
}
}
// Allocate an array with storage for all the property names.
Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
// Get the property names.
jsproto = obj;
for (int i = 0; i < length; i++) {
jsproto->GetLocalPropertyNames(*names,
i == 0 ? 0 : local_property_count[i - 1]);
if (i < length - 1) {
jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
}
}
DeleteArray(local_property_count);
return *Factory::NewJSArrayWithElements(names);
}
......@@ -5809,12 +5875,15 @@ static Object* Runtime_DebugConstructedBy(Arguments args) {
}
static Object* Runtime_GetPrototype(Arguments args) {
// Find the effective prototype object as returned by __proto__.
// args[0]: the object to find the prototype for.
static Object* Runtime_DebugGetPrototype(Arguments args) {
ASSERT(args.length() == 1);
CONVERT_CHECKED(JSObject, obj, args[0]);
return obj->GetPrototype();
// Use the __proto__ accessor.
return Accessors::ObjectPrototype.getter(obj, NULL);
}
......
......@@ -244,7 +244,7 @@ namespace v8 { namespace internal {
F(DebugGetLoadedScripts, 0) \
F(DebugReferencedBy, 3) \
F(DebugConstructedBy, 2) \
F(GetPrototype, 1) \
F(DebugGetPrototype, 1) \
F(SystemBreak, 0) \
\
/* Literals */ \
......
......@@ -2749,6 +2749,101 @@ TEST(InterceptorPropertyMirror) {
}
TEST(HiddenPrototypePropertyMirror) {
// Create a V8 environment with debug access.
v8::HandleScope scope;
DebugLocalContext env;
env.ExposeDebug();
v8::Handle<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
t0->InstanceTemplate()->Set(v8::String::New("x"), v8::Number::New(0));
v8::Handle<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
t1->SetHiddenPrototype(true);
t1->InstanceTemplate()->Set(v8::String::New("y"), v8::Number::New(1));
v8::Handle<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
t2->SetHiddenPrototype(true);
t2->InstanceTemplate()->Set(v8::String::New("z"), v8::Number::New(2));
v8::Handle<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
t3->InstanceTemplate()->Set(v8::String::New("u"), v8::Number::New(3));
// Create object and set them on the global object.
v8::Handle<v8::Object> o0 = t0->GetFunction()->NewInstance();
env->Global()->Set(v8::String::New("o0"), o0);
v8::Handle<v8::Object> o1 = t1->GetFunction()->NewInstance();
env->Global()->Set(v8::String::New("o1"), o1);
v8::Handle<v8::Object> o2 = t2->GetFunction()->NewInstance();
env->Global()->Set(v8::String::New("o2"), o2);
v8::Handle<v8::Object> o3 = t3->GetFunction()->NewInstance();
env->Global()->Set(v8::String::New("o3"), o3);
// Get mirrors for the four objects.
CompileRun(
"o0_mirror = debug.MakeMirror(o0);"
"o1_mirror = debug.MakeMirror(o1);"
"o2_mirror = debug.MakeMirror(o2);"
"o3_mirror = debug.MakeMirror(o3)");
CHECK(CompileRun("o0_mirror instanceof debug.ObjectMirror")->BooleanValue());
CHECK(CompileRun("o1_mirror instanceof debug.ObjectMirror")->BooleanValue());
CHECK(CompileRun("o2_mirror instanceof debug.ObjectMirror")->BooleanValue());
CHECK(CompileRun("o3_mirror instanceof debug.ObjectMirror")->BooleanValue());
// Check that each object has one property.
CHECK_EQ(1, CompileRun(
"o0_mirror.propertyNames().length")->Int32Value());
CHECK_EQ(1, CompileRun(
"o1_mirror.propertyNames().length")->Int32Value());
CHECK_EQ(1, CompileRun(
"o2_mirror.propertyNames().length")->Int32Value());
CHECK_EQ(1, CompileRun(
"o3_mirror.propertyNames().length")->Int32Value());
// Set o1 as prototype for o0. o1 has the hidden prototype flag so all
// properties on o1 should be seen on o0.
o0->Set(v8::String::New("__proto__"), o1);
CHECK_EQ(2, CompileRun(
"o0_mirror.propertyNames().length")->Int32Value());
CHECK_EQ(0, CompileRun(
"o0_mirror.property('x').value().value()")->Int32Value());
CHECK_EQ(1, CompileRun(
"o0_mirror.property('y').value().value()")->Int32Value());
// Set o2 as prototype for o0 (it will end up after o1 as o1 has the hidden
// prototype flag. o2 also has the hidden prototype flag so all properties
// on o2 should be seen on o0 as well as properties on o1.
o0->Set(v8::String::New("__proto__"), o2);
CHECK_EQ(3, CompileRun(
"o0_mirror.propertyNames().length")->Int32Value());
CHECK_EQ(0, CompileRun(
"o0_mirror.property('x').value().value()")->Int32Value());
CHECK_EQ(1, CompileRun(
"o0_mirror.property('y').value().value()")->Int32Value());
CHECK_EQ(2, CompileRun(
"o0_mirror.property('z').value().value()")->Int32Value());
// Set o3 as prototype for o0 (it will end up after o1 and o2 as both o1 and
// o2 has the hidden prototype flag. o3 does not have the hidden prototype
// flag so properties on o3 should not be seen on o0 whereas the properties
// from o1 and o2 should still be seen on o0.
// Final prototype chain: o0 -> o1 -> o2 -> o3
// Hidden prototypes: ^^ ^^
o0->Set(v8::String::New("__proto__"), o3);
CHECK_EQ(3, CompileRun(
"o0_mirror.propertyNames().length")->Int32Value());
CHECK_EQ(1, CompileRun(
"o3_mirror.propertyNames().length")->Int32Value());
CHECK_EQ(0, CompileRun(
"o0_mirror.property('x').value().value()")->Int32Value());
CHECK_EQ(1, CompileRun(
"o0_mirror.property('y').value().value()")->Int32Value());
CHECK_EQ(2, CompileRun(
"o0_mirror.property('z').value().value()")->Int32Value());
CHECK(CompileRun("o0_mirror.property('u').isUndefined()")->BooleanValue());
// The prototype (__proto__) for o0 should be o3 as o1 and o2 are hidden.
CHECK(CompileRun("o0_mirror.protoObject() == o3_mirror")->BooleanValue());
}
// Multithreaded tests of JSON debugger protocol
// Support classes
......
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