Commit bdeb0de8 authored by yurys's avatar yurys Committed by Commit bot

Provide accessor for object internal properties that doesn't require debugger to be active

Some of the DevTools' clients need to inspect JS objects without enabling debugger. This CL allows to inspect object's internal properties without enabling debugger and instantiating debug context.

Note that now debug context can be created lazily if v8::Debug::GetDebugContext is called when there is no debug listener. This is fragile and has already resulted in some subtle error. I'm going to fix that in a separate CL.

BUG=chromium:481845
LOG=Y

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

Cr-Commit-Position: refs/heads/master@{#28362}
parent 0c80fdc6
...@@ -260,6 +260,16 @@ class V8_EXPORT Debug { ...@@ -260,6 +260,16 @@ class V8_EXPORT Debug {
* unexpectedly used. LiveEdit is enabled by default. * unexpectedly used. LiveEdit is enabled by default.
*/ */
static void SetLiveEditEnabled(Isolate* isolate, bool enable); static void SetLiveEditEnabled(Isolate* isolate, bool enable);
/**
* Returns array of internal properties specific to the value type. Result has
* the following
* format: [<name>, <value>,...,<name>, <value>]. Result array will be
* allocated in the current
* context.
*/
static MaybeLocal<Array> GetInternalProperties(Isolate* isolate,
Local<Value> value);
}; };
......
...@@ -6090,16 +6090,7 @@ Local<Object> Array::CloneElementAt(uint32_t index) { ...@@ -6090,16 +6090,7 @@ Local<Object> Array::CloneElementAt(uint32_t index) {
bool Value::IsPromise() const { bool Value::IsPromise() const {
auto self = Utils::OpenHandle(this); auto self = Utils::OpenHandle(this);
if (!self->IsJSObject()) return false; return i::Object::IsPromise(self);
auto js_object = i::Handle<i::JSObject>::cast(self);
// Promises can't have access checks.
if (js_object->map()->is_access_check_needed()) return false;
auto isolate = js_object->GetIsolate();
// TODO(dcarney): this should just be read from the symbol registry so as not
// to be context dependent.
auto key = isolate->promise_status();
// Shouldn't be possible to throw here.
return i::JSObject::HasRealNamedProperty(js_object, key).FromJust();
} }
...@@ -7394,6 +7385,18 @@ void Debug::SetLiveEditEnabled(Isolate* isolate, bool enable) { ...@@ -7394,6 +7385,18 @@ void Debug::SetLiveEditEnabled(Isolate* isolate, bool enable) {
} }
MaybeLocal<Array> Debug::GetInternalProperties(Isolate* v8_isolate,
Local<Value> value) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ENTER_V8(isolate);
i::Handle<i::Object> val = Utils::OpenHandle(*value);
i::Handle<i::JSArray> result;
if (!i::Runtime::GetInternalProperties(isolate, val).ToHandle(&result))
return MaybeLocal<Array>();
return Utils::ToLocal(result);
}
Handle<String> CpuProfileNode::GetFunctionName() const { Handle<String> CpuProfileNode::GetFunctionName() const {
i::Isolate* isolate = i::Isolate::Current(); i::Isolate* isolate = i::Isolate::Current();
const i::ProfileNode* node = reinterpret_cast<const i::ProfileNode*>(this); const i::ProfileNode* node = reinterpret_cast<const i::ProfileNode*>(this);
......
...@@ -1611,6 +1611,7 @@ void Genesis::InstallNativeFunctions() { ...@@ -1611,6 +1611,7 @@ void Genesis::InstallNativeFunctions() {
to_complete_property_descriptor); to_complete_property_descriptor);
INSTALL_NATIVE(Symbol, "$promiseStatus", promise_status); INSTALL_NATIVE(Symbol, "$promiseStatus", promise_status);
INSTALL_NATIVE(Symbol, "$promiseValue", promise_value);
INSTALL_NATIVE(JSFunction, "$promiseCreate", promise_create); INSTALL_NATIVE(JSFunction, "$promiseCreate", promise_create);
INSTALL_NATIVE(JSFunction, "$promiseResolve", promise_resolve); INSTALL_NATIVE(JSFunction, "$promiseResolve", promise_resolve);
INSTALL_NATIVE(JSFunction, "$promiseReject", promise_reject); INSTALL_NATIVE(JSFunction, "$promiseReject", promise_reject);
......
...@@ -157,6 +157,7 @@ enum BindingFlags { ...@@ -157,6 +157,7 @@ enum BindingFlags {
V(ERROR_MESSAGE_FOR_CODE_GEN_FROM_STRINGS_INDEX, Object, \ V(ERROR_MESSAGE_FOR_CODE_GEN_FROM_STRINGS_INDEX, Object, \
error_message_for_code_gen_from_strings) \ error_message_for_code_gen_from_strings) \
V(PROMISE_STATUS_INDEX, Symbol, promise_status) \ V(PROMISE_STATUS_INDEX, Symbol, promise_status) \
V(PROMISE_VALUE_INDEX, Symbol, promise_value) \
V(PROMISE_CREATE_INDEX, JSFunction, promise_create) \ V(PROMISE_CREATE_INDEX, JSFunction, promise_create) \
V(PROMISE_RESOLVE_INDEX, JSFunction, promise_resolve) \ V(PROMISE_RESOLVE_INDEX, JSFunction, promise_resolve) \
V(PROMISE_REJECT_INDEX, JSFunction, promise_reject) \ V(PROMISE_REJECT_INDEX, JSFunction, promise_reject) \
...@@ -393,6 +394,7 @@ class Context: public FixedArray { ...@@ -393,6 +394,7 @@ class Context: public FixedArray {
RUN_MICROTASKS_INDEX, RUN_MICROTASKS_INDEX,
ENQUEUE_MICROTASK_INDEX, ENQUEUE_MICROTASK_INDEX,
PROMISE_STATUS_INDEX, PROMISE_STATUS_INDEX,
PROMISE_VALUE_INDEX,
PROMISE_CREATE_INDEX, PROMISE_CREATE_INDEX,
PROMISE_RESOLVE_INDEX, PROMISE_RESOLVE_INDEX,
PROMISE_REJECT_INDEX, PROMISE_REJECT_INDEX,
......
...@@ -904,57 +904,12 @@ ObjectMirror.prototype.toText = function() { ...@@ -904,57 +904,12 @@ ObjectMirror.prototype.toText = function() {
* @return {Array} array (possibly empty) of InternalProperty instances * @return {Array} array (possibly empty) of InternalProperty instances
*/ */
ObjectMirror.GetInternalProperties = function(value) { ObjectMirror.GetInternalProperties = function(value) {
if (IS_STRING_WRAPPER(value) || IS_NUMBER_WRAPPER(value) || var properties = %DebugGetInternalProperties(value);
IS_BOOLEAN_WRAPPER(value)) { var result = [];
var primitiveValue = %_ValueOf(value); for (var i = 0; i < properties.length; i += 2) {
return [new InternalPropertyMirror("[[PrimitiveValue]]", primitiveValue)]; result.push(new InternalPropertyMirror(properties[i], properties[i + 1]));
} else if (IS_FUNCTION(value)) {
var bindings = %BoundFunctionGetBindings(value);
var result = [];
if (bindings && IS_ARRAY(bindings)) {
result.push(new InternalPropertyMirror("[[TargetFunction]]",
bindings[0]));
result.push(new InternalPropertyMirror("[[BoundThis]]", bindings[1]));
var boundArgs = [];
for (var i = 2; i < bindings.length; i++) {
boundArgs.push(bindings[i]);
}
result.push(new InternalPropertyMirror("[[BoundArgs]]", boundArgs));
}
return result;
} else if (IS_MAP_ITERATOR(value) || IS_SET_ITERATOR(value)) {
var details = IS_MAP_ITERATOR(value) ? %MapIteratorDetails(value)
: %SetIteratorDetails(value);
var kind;
switch (details[2]) {
case 1: kind = "keys"; break;
case 2: kind = "values"; break;
case 3: kind = "entries"; break;
}
var result = [
new InternalPropertyMirror("[[IteratorHasMore]]", details[0]),
new InternalPropertyMirror("[[IteratorIndex]]", details[1])
];
if (kind) {
result.push(new InternalPropertyMirror("[[IteratorKind]]", kind));
}
return result;
} else if (IS_GENERATOR(value)) {
return [
new InternalPropertyMirror("[[GeneratorStatus]]",
GeneratorGetStatus_(value)),
new InternalPropertyMirror("[[GeneratorFunction]]",
%GeneratorGetFunction(value)),
new InternalPropertyMirror("[[GeneratorReceiver]]",
%GeneratorGetReceiver(value))
];
} else if (ObjectIsPromise(value)) {
return [
new InternalPropertyMirror("[[PromiseStatus]]", PromiseGetStatus_(value)),
new InternalPropertyMirror("[[PromiseValue]]", PromiseGetValue_(value))
];
} }
return []; return result;
} }
......
...@@ -112,6 +112,20 @@ bool Object::IsCallable() const { ...@@ -112,6 +112,20 @@ bool Object::IsCallable() const {
} }
bool Object::IsPromise(Handle<Object> object) {
if (!object->IsJSObject()) return false;
auto js_object = Handle<JSObject>::cast(object);
// Promises can't have access checks.
if (js_object->map()->is_access_check_needed()) return false;
auto isolate = js_object->GetIsolate();
// TODO(dcarney): this should just be read from the symbol registry so as not
// to be context dependent.
auto key = isolate->promise_status();
// Shouldn't be possible to throw here.
return JSObject::HasRealNamedProperty(js_object, key).FromJust();
}
MaybeHandle<Object> Object::GetProperty(LookupIterator* it) { MaybeHandle<Object> Object::GetProperty(LookupIterator* it) {
for (; it->IsFound(); it->Next()) { for (; it->IsFound(); it->Next()) {
switch (it->state()) { switch (it->state()) {
......
...@@ -1056,6 +1056,7 @@ class Object { ...@@ -1056,6 +1056,7 @@ class Object {
INLINE(bool IsOrderedHashSet() const); INLINE(bool IsOrderedHashSet() const);
INLINE(bool IsOrderedHashMap() const); INLINE(bool IsOrderedHashMap() const);
bool IsCallable() const; bool IsCallable() const;
static bool IsPromise(Handle<Object> object);
// Oddball testing. // Oddball testing.
INLINE(bool IsUndefined() const); INLINE(bool IsUndefined() const);
......
...@@ -101,6 +101,156 @@ static Handle<Object> DebugGetProperty(LookupIterator* it, ...@@ -101,6 +101,156 @@ static Handle<Object> DebugGetProperty(LookupIterator* it,
} }
static Handle<Object> DebugGetProperty(Handle<Object> object,
Handle<Name> name) {
LookupIterator it(object, name);
return DebugGetProperty(&it);
}
template <class IteratorType>
static MaybeHandle<JSArray> GetIteratorInternalProperties(
Isolate* isolate, Handle<IteratorType> object) {
Factory* factory = isolate->factory();
Handle<IteratorType> iterator = Handle<IteratorType>::cast(object);
RUNTIME_ASSERT_HANDLIFIED(iterator->kind()->IsSmi(), JSArray);
const char* kind = NULL;
switch (Smi::cast(iterator->kind())->value()) {
case IteratorType::kKindKeys:
kind = "keys";
break;
case IteratorType::kKindValues:
kind = "values";
break;
case IteratorType::kKindEntries:
kind = "entries";
break;
default:
RUNTIME_ASSERT_HANDLIFIED(false, JSArray);
}
Handle<FixedArray> result = factory->NewFixedArray(2 * 3);
result->set(0, *factory->NewStringFromAsciiChecked("[[IteratorHasMore]]"));
result->set(1, isolate->heap()->ToBoolean(iterator->HasMore()));
result->set(2, *factory->NewStringFromAsciiChecked("[[IteratorIndex]]"));
result->set(3, iterator->index());
result->set(4, *factory->NewStringFromAsciiChecked("[[IteratorKind]]"));
result->set(5, *factory->NewStringFromAsciiChecked(kind));
return factory->NewJSArrayWithElements(result);
}
MaybeHandle<JSArray> Runtime::GetInternalProperties(Isolate* isolate,
Handle<Object> object) {
Factory* factory = isolate->factory();
if (object->IsJSFunction()) {
Handle<JSFunction> function = Handle<JSFunction>::cast(object);
if (function->shared()->bound()) {
RUNTIME_ASSERT_HANDLIFIED(function->function_bindings()->IsFixedArray(),
JSArray);
Handle<FixedArray> bindings(function->function_bindings());
Handle<FixedArray> result = factory->NewFixedArray(2 * 3);
result->set(0, *factory->NewStringFromAsciiChecked("[[TargetFunction]]"));
result->set(1, bindings->get(JSFunction::kBoundFunctionIndex));
result->set(2, *factory->NewStringFromAsciiChecked("[[BoundThis]]"));
result->set(3, bindings->get(JSFunction::kBoundThisIndex));
Handle<FixedArray> arguments = factory->NewFixedArray(
bindings->length() - JSFunction::kBoundArgumentsStartIndex);
bindings->CopyTo(
JSFunction::kBoundArgumentsStartIndex, *arguments, 0,
bindings->length() - JSFunction::kBoundArgumentsStartIndex);
result->set(4, *factory->NewStringFromAsciiChecked("[[BoundArgs]]"));
result->set(5, *factory->NewJSArrayWithElements(arguments));
return factory->NewJSArrayWithElements(result);
}
} else if (object->IsJSMapIterator()) {
Handle<JSMapIterator> iterator = Handle<JSMapIterator>::cast(object);
return GetIteratorInternalProperties(isolate, iterator);
} else if (object->IsJSSetIterator()) {
Handle<JSSetIterator> iterator = Handle<JSSetIterator>::cast(object);
return GetIteratorInternalProperties(isolate, iterator);
} else if (object->IsJSGeneratorObject()) {
Handle<JSGeneratorObject> generator =
Handle<JSGeneratorObject>::cast(object);
const char* status = "suspended";
if (generator->is_closed()) {
status = "closed";
} else if (generator->is_executing()) {
status = "running";
} else {
DCHECK(generator->is_suspended());
}
Handle<FixedArray> result = factory->NewFixedArray(2 * 3);
result->set(0, *factory->NewStringFromAsciiChecked("[[GeneratorStatus]]"));
result->set(1, *factory->NewStringFromAsciiChecked(status));
result->set(2,
*factory->NewStringFromAsciiChecked("[[GeneratorFunction]]"));
result->set(3, generator->function());
result->set(4,
*factory->NewStringFromAsciiChecked("[[GeneratorReceiver]]"));
result->set(5, generator->receiver());
return factory->NewJSArrayWithElements(result);
} else if (Object::IsPromise(object)) {
Handle<JSObject> promise = Handle<JSObject>::cast(object);
Handle<Object> status_obj =
DebugGetProperty(promise, isolate->promise_status());
RUNTIME_ASSERT_HANDLIFIED(status_obj->IsSmi(), JSArray);
const char* status = "rejected";
int status_val = Handle<Smi>::cast(status_obj)->value();
switch (status_val) {
case +1:
status = "resolved";
break;
case 0:
status = "pending";
break;
default:
DCHECK_EQ(-1, status_val);
}
Handle<FixedArray> result = factory->NewFixedArray(2 * 2);
result->set(0, *factory->NewStringFromAsciiChecked("[[PromiseStatus]]"));
result->set(1, *factory->NewStringFromAsciiChecked(status));
Handle<Object> value_obj =
DebugGetProperty(promise, isolate->promise_value());
result->set(2, *factory->NewStringFromAsciiChecked("[[PromiseValue]]"));
result->set(3, *value_obj);
return factory->NewJSArrayWithElements(result);
} else if (object->IsJSValue()) {
Handle<JSValue> js_value = Handle<JSValue>::cast(object);
Handle<FixedArray> result = factory->NewFixedArray(2);
result->set(0, *factory->NewStringFromAsciiChecked("[[PrimitiveValue]]"));
result->set(1, js_value->value());
return factory->NewJSArrayWithElements(result);
}
return factory->NewJSArray(0);
}
RUNTIME_FUNCTION(Runtime_DebugGetInternalProperties) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(Object, obj, 0);
Handle<JSArray> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result, Runtime::GetInternalProperties(isolate, obj));
return *result;
}
// Get debugger related details for an object property, in the following format: // Get debugger related details for an object property, in the following format:
// 0: Property value // 0: Property value
// 1: Property details // 1: Property details
......
...@@ -134,6 +134,7 @@ namespace internal { ...@@ -134,6 +134,7 @@ namespace internal {
F(DebugBreak, 0, 1) \ F(DebugBreak, 0, 1) \
F(SetDebugEventListener, 2, 1) \ F(SetDebugEventListener, 2, 1) \
F(ScheduleBreak, 0, 1) \ F(ScheduleBreak, 0, 1) \
F(DebugGetInternalProperties, 1, 1) \
F(DebugGetPropertyDetails, 2, 1) \ F(DebugGetPropertyDetails, 2, 1) \
F(DebugGetProperty, 2, 1) \ F(DebugGetProperty, 2, 1) \
F(DebugPropertyTypeFromDetails, 1, 1) \ F(DebugPropertyTypeFromDetails, 1, 1) \
...@@ -854,6 +855,9 @@ class Runtime : public AllStatic { ...@@ -854,6 +855,9 @@ class Runtime : public AllStatic {
Handle<Object> key, Handle<Object> value); Handle<Object> key, Handle<Object> value);
static bool WeakCollectionDelete(Handle<JSWeakCollection> weak_collection, static bool WeakCollectionDelete(Handle<JSWeakCollection> weak_collection,
Handle<Object> key); Handle<Object> key);
static MaybeHandle<JSArray> GetInternalProperties(Isolate* isolate,
Handle<Object>);
}; };
......
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