Author: [email protected]
Date: Thu Jan 22 05:20:31 2009
New Revision: 1126
Modified:
branches/bleeding_edge/src/mirror-delay.js
branches/bleeding_edge/src/objects-debug.cc
branches/bleeding_edge/src/objects.cc
branches/bleeding_edge/src/objects.h
branches/bleeding_edge/src/runtime.cc
branches/bleeding_edge/src/runtime.h
branches/bleeding_edge/test/cctest/test-debug.cc
Log:
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
Modified: branches/bleeding_edge/src/mirror-delay.js
==============================================================================
--- branches/bleeding_edge/src/mirror-delay.js (original)
+++ branches/bleeding_edge/src/mirror-delay.js Thu Jan 22 05:20:31 2009
@@ -525,7 +525,7 @@
ObjectMirror.prototype.protoObject = function() {
- return MakeMirror(%GetPrototype(this.value_));
+ return MakeMirror(%DebugGetPrototype(this.value_));
};
Modified: branches/bleeding_edge/src/objects-debug.cc
==============================================================================
--- branches/bleeding_edge/src/objects-debug.cc (original)
+++ branches/bleeding_edge/src/objects-debug.cc Thu Jan 22 05:20:31 2009
@@ -414,6 +414,24 @@
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: ");
Modified: branches/bleeding_edge/src/objects.cc
==============================================================================
--- branches/bleeding_edge/src/objects.cc (original)
+++ branches/bleeding_edge/src/objects.cc Thu Jan 22 05:20:31 2009
@@ -5695,10 +5695,10 @@
// 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 @@
storage->set(index++, r.GetKey());
}
}
- ASSERT(storage->length() == index);
+ ASSERT(storage->length() >= index);
} else {
property_dictionary()->CopyKeysTo(storage);
}
Modified: branches/bleeding_edge/src/objects.h
==============================================================================
--- branches/bleeding_edge/src/objects.h (original)
+++ branches/bleeding_edge/src/objects.h Thu Jan 22 05:20:31 2009
@@ -1298,8 +1298,9 @@
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).
Modified: branches/bleeding_edge/src/runtime.cc
==============================================================================
--- branches/bleeding_edge/src/runtime.cc (original)
+++ branches/bleeding_edge/src/runtime.cc Thu Jan 22 05:20:31 2009
@@ -4536,6 +4536,21 @@
}
+// 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 @@
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 @@
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 @@
}
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_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);
}
Modified: branches/bleeding_edge/src/runtime.h
==============================================================================
--- branches/bleeding_edge/src/runtime.h (original)
+++ branches/bleeding_edge/src/runtime.h Thu Jan 22 05:20:31 2009
@@ -244,7 +244,7 @@
F(DebugGetLoadedScripts, 0) \
F(DebugReferencedBy, 3) \
F(DebugConstructedBy, 2) \
- F(GetPrototype, 1) \
+ F(DebugGetPrototype, 1) \
F(SystemBreak, 0) \
\
/* Literals */ \
Modified: branches/bleeding_edge/test/cctest/test-debug.cc
==============================================================================
--- branches/bleeding_edge/test/cctest/test-debug.cc (original)
+++ branches/bleeding_edge/test/cctest/test-debug.cc Thu Jan 22 05:20:31
2009
@@ -2749,6 +2749,101 @@
}
+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
--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---