Commit 6e17719e authored by kozyatinskiy's avatar kozyatinskiy Committed by Commit bot

[inspector] introduced v8::debug::EntriesPreview for inspector

- entries preview available even if debugger agent is disabled,
- less deprecated mirrors usage in debugger-script.js
- no usage of debugger context - zero probability of leaking it.
- better test coverage.

BUG=v8:5510
R=yangguo@chromium.org,jgruber@chromium.org,alph@chromium.org,luoe@chromium.org

Review-Url: https://codereview.chromium.org/2672213002
Cr-Commit-Position: refs/heads/master@{#42978}
parent a5298c6f
...@@ -7140,15 +7140,14 @@ Maybe<bool> Map::Delete(Local<Context> context, Local<Value> key) { ...@@ -7140,15 +7140,14 @@ Maybe<bool> Map::Delete(Local<Context> context, Local<Value> key) {
return Just(result->IsTrue(isolate)); return Just(result->IsTrue(isolate));
} }
namespace {
Local<Array> Map::AsArray() const { i::Handle<i::JSArray> MapAsArray(i::Isolate* isolate, i::Object* table_obj,
i::Handle<i::JSMap> obj = Utils::OpenHandle(this); int offset, int kind) {
i::Isolate* isolate = obj->GetIsolate();
i::Factory* factory = isolate->factory(); i::Factory* factory = isolate->factory();
LOG_API(isolate, Map, AsArray); i::Handle<i::OrderedHashMap> table(i::OrderedHashMap::cast(table_obj));
ENTER_V8(isolate); if (offset >= table->NumberOfElements()) return factory->NewJSArray(0);
i::Handle<i::OrderedHashMap> table(i::OrderedHashMap::cast(obj->table())); int length = (table->NumberOfElements() - offset) *
int length = table->NumberOfElements() * 2; (kind == i::JSMapIterator::kKindEntries ? 2 : 1);
i::Handle<i::FixedArray> result = factory->NewFixedArray(length); i::Handle<i::FixedArray> result = factory->NewFixedArray(length);
int result_index = 0; int result_index = 0;
{ {
...@@ -7158,15 +7157,30 @@ Local<Array> Map::AsArray() const { ...@@ -7158,15 +7157,30 @@ Local<Array> Map::AsArray() const {
for (int i = 0; i < capacity; ++i) { for (int i = 0; i < capacity; ++i) {
i::Object* key = table->KeyAt(i); i::Object* key = table->KeyAt(i);
if (key == the_hole) continue; if (key == the_hole) continue;
result->set(result_index++, key); if (offset-- > 0) continue;
result->set(result_index++, table->ValueAt(i)); if (kind == i::JSMapIterator::kKindEntries ||
kind == i::JSMapIterator::kKindKeys) {
result->set(result_index++, key);
}
if (kind == i::JSMapIterator::kKindEntries ||
kind == i::JSMapIterator::kKindValues) {
result->set(result_index++, table->ValueAt(i));
}
} }
} }
DCHECK_EQ(result_index, result->length()); DCHECK_EQ(result_index, result->length());
DCHECK_EQ(result_index, length); DCHECK_EQ(result_index, length);
i::Handle<i::JSArray> result_array = return factory->NewJSArrayWithElements(result, i::FAST_ELEMENTS, length);
factory->NewJSArrayWithElements(result, i::FAST_ELEMENTS, length); }
return Utils::ToLocal(result_array); } // namespace
Local<Array> Map::AsArray() const {
i::Handle<i::JSMap> obj = Utils::OpenHandle(this);
i::Isolate* isolate = obj->GetIsolate();
LOG_API(isolate, Map, AsArray);
ENTER_V8(isolate);
return Utils::ToLocal(
MapAsArray(isolate, obj->table(), 0, i::JSMapIterator::kKindEntries));
} }
...@@ -7232,15 +7246,13 @@ Maybe<bool> Set::Delete(Local<Context> context, Local<Value> key) { ...@@ -7232,15 +7246,13 @@ Maybe<bool> Set::Delete(Local<Context> context, Local<Value> key) {
return Just(result->IsTrue(isolate)); return Just(result->IsTrue(isolate));
} }
namespace {
Local<Array> Set::AsArray() const { i::Handle<i::JSArray> SetAsArray(i::Isolate* isolate, i::Object* table_obj,
i::Handle<i::JSSet> obj = Utils::OpenHandle(this); int offset) {
i::Isolate* isolate = obj->GetIsolate();
i::Factory* factory = isolate->factory(); i::Factory* factory = isolate->factory();
LOG_API(isolate, Set, AsArray); i::Handle<i::OrderedHashSet> table(i::OrderedHashSet::cast(table_obj));
ENTER_V8(isolate); int length = table->NumberOfElements() - offset;
i::Handle<i::OrderedHashSet> table(i::OrderedHashSet::cast(obj->table())); if (length <= 0) return factory->NewJSArray(0);
int length = table->NumberOfElements();
i::Handle<i::FixedArray> result = factory->NewFixedArray(length); i::Handle<i::FixedArray> result = factory->NewFixedArray(length);
int result_index = 0; int result_index = 0;
{ {
...@@ -7250,14 +7262,22 @@ Local<Array> Set::AsArray() const { ...@@ -7250,14 +7262,22 @@ Local<Array> Set::AsArray() const {
for (int i = 0; i < capacity; ++i) { for (int i = 0; i < capacity; ++i) {
i::Object* key = table->KeyAt(i); i::Object* key = table->KeyAt(i);
if (key == the_hole) continue; if (key == the_hole) continue;
if (offset-- > 0) continue;
result->set(result_index++, key); result->set(result_index++, key);
} }
} }
DCHECK_EQ(result_index, result->length()); DCHECK_EQ(result_index, result->length());
DCHECK_EQ(result_index, length); DCHECK_EQ(result_index, length);
i::Handle<i::JSArray> result_array = return factory->NewJSArrayWithElements(result, i::FAST_ELEMENTS, length);
factory->NewJSArrayWithElements(result, i::FAST_ELEMENTS, length); }
return Utils::ToLocal(result_array); } // namespace
Local<Array> Set::AsArray() const {
i::Handle<i::JSSet> obj = Utils::OpenHandle(this);
i::Isolate* isolate = obj->GetIsolate();
LOG_API(isolate, Set, AsArray);
ENTER_V8(isolate);
return Utils::ToLocal(SetAsArray(isolate, obj->table(), 0));
} }
...@@ -9354,6 +9374,46 @@ int debug::EstimatedValueSize(Isolate* v8_isolate, v8::Local<v8::Value> value) { ...@@ -9354,6 +9374,46 @@ int debug::EstimatedValueSize(Isolate* v8_isolate, v8::Local<v8::Value> value) {
return i::Handle<i::HeapObject>::cast(object)->Size(); return i::Handle<i::HeapObject>::cast(object)->Size();
} }
v8::MaybeLocal<v8::Array> debug::EntriesPreview(Isolate* v8_isolate,
v8::Local<v8::Value> value,
bool* is_key_value) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ENTER_V8(isolate);
if (value->IsMap()) {
*is_key_value = true;
return value.As<Map>()->AsArray();
}
if (value->IsSet()) {
*is_key_value = false;
return value.As<Set>()->AsArray();
}
i::Handle<i::Object> object = Utils::OpenHandle(*value);
if (object->IsJSWeakCollection()) {
*is_key_value = object->IsJSWeakMap();
return Utils::ToLocal(i::JSWeakCollection::GetEntries(
i::Handle<i::JSWeakCollection>::cast(object), 0));
}
if (object->IsJSMapIterator()) {
i::Handle<i::JSMapIterator> iterator =
i::Handle<i::JSMapIterator>::cast(object);
int iterator_kind = i::Smi::cast(iterator->kind())->value();
*is_key_value = iterator_kind == i::JSMapIterator::kKindEntries;
if (!iterator->HasMore()) return v8::Array::New(v8_isolate);
return Utils::ToLocal(MapAsArray(isolate, iterator->table(),
i::Smi::cast(iterator->index())->value(),
iterator_kind));
}
if (object->IsJSSetIterator()) {
i::Handle<i::JSSetIterator> it = i::Handle<i::JSSetIterator>::cast(object);
*is_key_value = false;
if (!it->HasMore()) return v8::Array::New(v8_isolate);
return Utils::ToLocal(
SetAsArray(isolate, it->table(), i::Smi::cast(it->index())->value()));
}
return v8::MaybeLocal<v8::Array>();
}
Local<String> CpuProfileNode::GetFunctionName() const { Local<String> CpuProfileNode::GetFunctionName() const {
const i::ProfileNode* node = reinterpret_cast<const i::ProfileNode*>(this); const i::ProfileNode* node = reinterpret_cast<const i::ProfileNode*>(this);
i::Isolate* isolate = node->isolate(); i::Isolate* isolate = node->isolate();
......
...@@ -178,6 +178,10 @@ void ResetBlackboxedStateCache(Isolate* isolate, ...@@ -178,6 +178,10 @@ void ResetBlackboxedStateCache(Isolate* isolate,
int EstimatedValueSize(Isolate* isolate, v8::Local<v8::Value> value); int EstimatedValueSize(Isolate* isolate, v8::Local<v8::Value> value);
v8::MaybeLocal<v8::Array> EntriesPreview(Isolate* isolate,
v8::Local<v8::Value> value,
bool* is_key_value);
} // namespace debug } // namespace debug
} // namespace v8 } // namespace v8
......
...@@ -126,24 +126,6 @@ DebuggerScript.getGeneratorObjectLocation = function(object) ...@@ -126,24 +126,6 @@ DebuggerScript.getGeneratorObjectLocation = function(object)
return null; return null;
} }
/**
* @param {Object} object
* @return {!Array<!{value: *}>|undefined}
*/
DebuggerScript.getCollectionEntries = function(object)
{
var mirror = MakeMirror(object);
if (mirror.isMap())
return /** @type {!MapMirror} */(mirror).entries();
if (mirror.isSet() || mirror.isIterator()) {
var result = [];
var values = mirror.isSet() ? /** @type {!SetMirror} */(mirror).values() : /** @type {!IteratorMirror} */(mirror).preview();
for (var i = 0; i < values.length; ++i)
result.push({ value: values[i] });
return result;
}
}
/** /**
* @param {!ExecutionState} execState * @param {!ExecutionState} execState
* @param {!BreakpointInfo} info * @param {!BreakpointInfo} info
......
...@@ -251,16 +251,6 @@ Mirror.prototype.isFunction = function() {} ...@@ -251,16 +251,6 @@ Mirror.prototype.isFunction = function() {}
/** @return {boolean} */ /** @return {boolean} */
Mirror.prototype.isGenerator = function() {} Mirror.prototype.isGenerator = function() {}
/** @return {boolean} */
Mirror.prototype.isMap = function() {}
/** @return {boolean} */
Mirror.prototype.isSet = function() {}
/** @return {boolean} */
Mirror.prototype.isIterator = function() {}
/** /**
* @interface * @interface
* @extends {Mirror} * @extends {Mirror}
...@@ -310,46 +300,6 @@ FunctionMirror.prototype.context = function() {} ...@@ -310,46 +300,6 @@ FunctionMirror.prototype.context = function() {}
*/ */
function UnresolvedFunctionMirror(value) {} function UnresolvedFunctionMirror(value) {}
/**
* @interface
* @extends {ObjectMirror}
*/
function MapMirror () {}
/**
* @param {number=} limit
* @return {!Array<!{key: *, value: *}>}
*/
MapMirror.prototype.entries = function(limit) {}
/**
* @interface
* @extends {ObjectMirror}
*/
function SetMirror () {}
/**
* @param {number=} limit
* @return {!Array<*>}
*/
SetMirror.prototype.values = function(limit) {}
/**
* @interface
* @extends {ObjectMirror}
*/
function IteratorMirror () {}
/**
* @param {number=} limit
* @return {!Array<*>}
*/
IteratorMirror.prototype.preview = function(limit) {}
/** /**
* @interface * @interface
* @extends {ObjectMirror} * @extends {ObjectMirror}
......
...@@ -42,6 +42,44 @@ V8DebuggerAgentImpl* agentForScript(V8InspectorImpl* inspector, ...@@ -42,6 +42,44 @@ V8DebuggerAgentImpl* agentForScript(V8InspectorImpl* inspector,
return inspector->enabledDebuggerAgentForGroup(contextGroupId); return inspector->enabledDebuggerAgentForGroup(contextGroupId);
} }
v8::MaybeLocal<v8::Array> collectionsEntries(v8::Local<v8::Context> context,
v8::Local<v8::Value> value) {
v8::Isolate* isolate = context->GetIsolate();
v8::Local<v8::Array> entries;
bool isKeyValue = false;
if (!v8::debug::EntriesPreview(isolate, value, &isKeyValue).ToLocal(&entries))
return v8::MaybeLocal<v8::Array>();
v8::Local<v8::Array> wrappedEntries = v8::Array::New(isolate);
CHECK(!isKeyValue || wrappedEntries->Length() % 2 == 0);
if (!wrappedEntries->SetPrototype(context, v8::Null(isolate))
.FromMaybe(false))
return v8::MaybeLocal<v8::Array>();
for (uint32_t i = 0; i < entries->Length(); i += isKeyValue ? 2 : 1) {
v8::Local<v8::Value> item;
if (!entries->Get(context, i).ToLocal(&item)) continue;
v8::Local<v8::Value> value;
if (isKeyValue && !entries->Get(context, i + 1).ToLocal(&value)) continue;
v8::Local<v8::Object> wrapper = v8::Object::New(isolate);
if (!wrapper->SetPrototype(context, v8::Null(isolate)).FromMaybe(false))
continue;
createDataProperty(
context, wrapper,
toV8StringInternalized(isolate, isKeyValue ? "key" : "value"), item);
if (isKeyValue) {
createDataProperty(context, wrapper,
toV8StringInternalized(isolate, "value"), value);
}
createDataProperty(context, wrappedEntries, wrappedEntries->Length(),
wrapper);
}
if (!markArrayEntriesAsInternal(context, wrappedEntries,
V8InternalValueType::kEntry)) {
return v8::MaybeLocal<v8::Array>();
}
return wrappedEntries;
}
} // namespace } // namespace
static bool inLiveEditScope = false; static bool inLiveEditScope = false;
...@@ -266,15 +304,8 @@ bool V8Debugger::canBreakProgram() { ...@@ -266,15 +304,8 @@ bool V8Debugger::canBreakProgram() {
} }
void V8Debugger::breakProgram() { void V8Debugger::breakProgram() {
if (isPaused()) { // Don't allow nested breaks.
DCHECK(!m_runningNestedMessageLoop); if (isPaused()) return;
v8::Local<v8::Value> exception;
v8::Local<v8::Array> hitBreakpoints;
handleProgramBreak(m_pausedContext, m_executionState, exception,
hitBreakpoints);
return;
}
if (!canBreakProgram()) return; if (!canBreakProgram()) return;
v8::HandleScope scope(m_isolate); v8::HandleScope scope(m_isolate);
...@@ -413,27 +444,14 @@ Response V8Debugger::setScriptSource( ...@@ -413,27 +444,14 @@ Response V8Debugger::setScriptSource(
} }
JavaScriptCallFrames V8Debugger::currentCallFrames(int limit) { JavaScriptCallFrames V8Debugger::currentCallFrames(int limit) {
if (!m_isolate->InContext()) return JavaScriptCallFrames(); if (!isPaused()) return JavaScriptCallFrames();
v8::Local<v8::Value> currentCallFramesV8; v8::Local<v8::Value> currentCallFramesV8;
if (m_executionState.IsEmpty()) { v8::Local<v8::Value> argv[] = {m_executionState,
v8::Local<v8::Function> currentCallFramesFunction = v8::Integer::New(m_isolate, limit)};
v8::Local<v8::Function>::Cast( if (!callDebuggerMethod("currentCallFrames", arraysize(argv), argv, true)
m_debuggerScript.Get(m_isolate) .ToLocal(&currentCallFramesV8)) {
->Get(debuggerContext(), return JavaScriptCallFrames();
toV8StringInternalized(m_isolate, "currentCallFrames"))
.ToLocalChecked());
if (!v8::debug::Call(debuggerContext(), currentCallFramesFunction,
v8::Integer::New(m_isolate, limit))
.ToLocal(&currentCallFramesV8))
return JavaScriptCallFrames();
} else {
v8::Local<v8::Value> argv[] = {m_executionState,
v8::Integer::New(m_isolate, limit)};
if (!callDebuggerMethod("currentCallFrames", arraysize(argv), argv, true)
.ToLocal(&currentCallFramesV8))
return JavaScriptCallFrames();
} }
DCHECK(!currentCallFramesV8.IsEmpty());
if (!currentCallFramesV8->IsArray()) return JavaScriptCallFrames(); if (!currentCallFramesV8->IsArray()) return JavaScriptCallFrames();
v8::Local<v8::Array> callFramesArray = currentCallFramesV8.As<v8::Array>(); v8::Local<v8::Array> callFramesArray = currentCallFramesV8.As<v8::Array>();
JavaScriptCallFrames callFrames; JavaScriptCallFrames callFrames;
...@@ -703,17 +721,13 @@ v8::MaybeLocal<v8::Array> V8Debugger::internalProperties( ...@@ -703,17 +721,13 @@ v8::MaybeLocal<v8::Array> V8Debugger::internalProperties(
v8::True(m_isolate)); v8::True(m_isolate));
} }
} }
if (!enabled()) return properties; v8::Local<v8::Array> entries;
if (value->IsMap() || value->IsWeakMap() || value->IsSet() || if (collectionsEntries(context, value).ToLocal(&entries)) {
value->IsWeakSet() || value->IsSetIterator() || value->IsMapIterator()) { createDataProperty(context, properties, properties->Length(),
v8::Local<v8::Value> entries = toV8StringInternalized(m_isolate, "[[Entries]]"));
collectionEntries(context, v8::Local<v8::Object>::Cast(value)); createDataProperty(context, properties, properties->Length(), entries);
if (entries->IsArray()) {
createDataProperty(context, properties, properties->Length(),
toV8StringInternalized(m_isolate, "[[Entries]]"));
createDataProperty(context, properties, properties->Length(), entries);
}
} }
if (!enabled()) return properties;
if (value->IsGeneratorObject()) { if (value->IsGeneratorObject()) {
v8::Local<v8::Value> location = v8::Local<v8::Value> location =
generatorObjectLocation(context, v8::Local<v8::Object>::Cast(value)); generatorObjectLocation(context, v8::Local<v8::Object>::Cast(value));
...@@ -744,43 +758,6 @@ v8::MaybeLocal<v8::Array> V8Debugger::internalProperties( ...@@ -744,43 +758,6 @@ v8::MaybeLocal<v8::Array> V8Debugger::internalProperties(
return properties; return properties;
} }
v8::Local<v8::Value> V8Debugger::collectionEntries(
v8::Local<v8::Context> context, v8::Local<v8::Object> object) {
if (!enabled()) {
UNREACHABLE();
return v8::Undefined(m_isolate);
}
v8::Local<v8::Value> argv[] = {object};
v8::Local<v8::Value> entriesValue;
if (!callDebuggerMethod("getCollectionEntries", 1, argv, true)
.ToLocal(&entriesValue) ||
!entriesValue->IsArray())
return v8::Undefined(m_isolate);
v8::Local<v8::Array> entries = entriesValue.As<v8::Array>();
v8::Local<v8::Array> copiedArray =
v8::Array::New(m_isolate, entries->Length());
if (!copiedArray->SetPrototype(context, v8::Null(m_isolate)).FromMaybe(false))
return v8::Undefined(m_isolate);
for (uint32_t i = 0; i < entries->Length(); ++i) {
v8::Local<v8::Value> item;
if (!entries->Get(debuggerContext(), i).ToLocal(&item))
return v8::Undefined(m_isolate);
v8::Local<v8::Value> copied;
if (!copyValueFromDebuggerContext(m_isolate, debuggerContext(), context,
item)
.ToLocal(&copied))
return v8::Undefined(m_isolate);
if (!createDataProperty(context, copiedArray, i, copied).FromMaybe(false))
return v8::Undefined(m_isolate);
}
if (!markArrayEntriesAsInternal(context,
v8::Local<v8::Array>::Cast(copiedArray),
V8InternalValueType::kEntry))
return v8::Undefined(m_isolate);
return copiedArray;
}
v8::Local<v8::Value> V8Debugger::generatorObjectLocation( v8::Local<v8::Value> V8Debugger::generatorObjectLocation(
v8::Local<v8::Context> context, v8::Local<v8::Object> object) { v8::Local<v8::Context> context, v8::Local<v8::Object> object) {
if (!enabled()) { if (!enabled()) {
......
...@@ -113,8 +113,6 @@ class V8Debugger : public v8::debug::DebugDelegate { ...@@ -113,8 +113,6 @@ class V8Debugger : public v8::debug::DebugDelegate {
bool isPromiseRejection = false, bool isPromiseRejection = false,
bool isUncaught = false); bool isUncaught = false);
v8::Local<v8::Value> collectionEntries(v8::Local<v8::Context>,
v8::Local<v8::Object>);
v8::Local<v8::Value> generatorObjectLocation(v8::Local<v8::Context>, v8::Local<v8::Value> generatorObjectLocation(v8::Local<v8::Context>,
v8::Local<v8::Object>); v8::Local<v8::Object>);
v8::Local<v8::Value> functionLocation(v8::Local<v8::Context>, v8::Local<v8::Value> functionLocation(v8::Local<v8::Context>,
......
...@@ -18770,6 +18770,40 @@ bool JSWeakCollection::Delete(Handle<JSWeakCollection> weak_collection, ...@@ -18770,6 +18770,40 @@ bool JSWeakCollection::Delete(Handle<JSWeakCollection> weak_collection,
return was_present; return was_present;
} }
Handle<JSArray> JSWeakCollection::GetEntries(Handle<JSWeakCollection> holder,
int max_entries) {
Isolate* isolate = holder->GetIsolate();
Handle<ObjectHashTable> table(ObjectHashTable::cast(holder->table()));
if (max_entries == 0 || max_entries > table->NumberOfElements()) {
max_entries = table->NumberOfElements();
}
int values_per_entry = holder->IsJSWeakMap() ? 2 : 1;
Handle<FixedArray> entries =
isolate->factory()->NewFixedArray(max_entries * values_per_entry);
// Recompute max_values because GC could have removed elements from the table.
if (max_entries > table->NumberOfElements()) {
max_entries = table->NumberOfElements();
}
{
DisallowHeapAllocation no_gc;
int count = 0;
for (int i = 0;
count / values_per_entry < max_entries && i < table->Capacity(); i++) {
Handle<Object> key(table->KeyAt(i), isolate);
if (table->IsKey(isolate, *key)) {
entries->set(count++, *key);
if (values_per_entry > 1) {
Object* value = table->Lookup(key);
entries->set(count++, value);
}
}
}
DCHECK_EQ(max_entries * values_per_entry, count);
}
return isolate->factory()->NewJSArrayWithElements(entries);
}
// Check if there is a break point at this source position. // Check if there is a break point at this source position.
bool DebugInfo::HasBreakPoint(int source_position) { bool DebugInfo::HasBreakPoint(int source_position) {
// Get the break point info object for this code offset. // Get the break point info object for this code offset.
......
...@@ -10675,6 +10675,8 @@ class JSWeakCollection: public JSObject { ...@@ -10675,6 +10675,8 @@ class JSWeakCollection: public JSObject {
Handle<Object> value, int32_t hash); Handle<Object> value, int32_t hash);
static bool Delete(Handle<JSWeakCollection> collection, Handle<Object> key, static bool Delete(Handle<JSWeakCollection> collection, Handle<Object> key,
int32_t hash); int32_t hash);
static Handle<JSArray> GetEntries(Handle<JSWeakCollection> holder,
int max_entries);
static const int kTableOffset = JSObject::kHeaderSize; static const int kTableOffset = JSObject::kHeaderSize;
static const int kNextOffset = kTableOffset + kPointerSize; static const int kNextOffset = kTableOffset + kPointerSize;
......
...@@ -233,32 +233,7 @@ RUNTIME_FUNCTION(Runtime_GetWeakMapEntries) { ...@@ -233,32 +233,7 @@ RUNTIME_FUNCTION(Runtime_GetWeakMapEntries) {
CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, holder, 0); CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, holder, 0);
CONVERT_NUMBER_CHECKED(int, max_entries, Int32, args[1]); CONVERT_NUMBER_CHECKED(int, max_entries, Int32, args[1]);
CHECK(max_entries >= 0); CHECK(max_entries >= 0);
return *JSWeakCollection::GetEntries(holder, max_entries);
Handle<ObjectHashTable> table(ObjectHashTable::cast(holder->table()));
if (max_entries == 0 || max_entries > table->NumberOfElements()) {
max_entries = table->NumberOfElements();
}
Handle<FixedArray> entries =
isolate->factory()->NewFixedArray(max_entries * 2);
// Allocation can cause GC can delete weak elements. Reload.
if (max_entries > table->NumberOfElements()) {
max_entries = table->NumberOfElements();
}
{
DisallowHeapAllocation no_gc;
int count = 0;
for (int i = 0; count / 2 < max_entries && i < table->Capacity(); i++) {
Handle<Object> key(table->KeyAt(i), isolate);
if (table->IsKey(isolate, *key)) {
entries->set(count++, *key);
Object* value = table->Lookup(key);
entries->set(count++, value);
}
}
DCHECK_EQ(max_entries * 2, count);
}
return *isolate->factory()->NewJSArrayWithElements(entries);
} }
...@@ -348,26 +323,7 @@ RUNTIME_FUNCTION(Runtime_GetWeakSetValues) { ...@@ -348,26 +323,7 @@ RUNTIME_FUNCTION(Runtime_GetWeakSetValues) {
CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, holder, 0); CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, holder, 0);
CONVERT_NUMBER_CHECKED(int, max_values, Int32, args[1]); CONVERT_NUMBER_CHECKED(int, max_values, Int32, args[1]);
CHECK(max_values >= 0); CHECK(max_values >= 0);
return *JSWeakCollection::GetEntries(holder, max_values);
Handle<ObjectHashTable> table(ObjectHashTable::cast(holder->table()));
if (max_values == 0 || max_values > table->NumberOfElements()) {
max_values = table->NumberOfElements();
}
Handle<FixedArray> values = isolate->factory()->NewFixedArray(max_values);
// Recompute max_values because GC could have removed elements from the table.
if (max_values > table->NumberOfElements()) {
max_values = table->NumberOfElements();
}
{
DisallowHeapAllocation no_gc;
int count = 0;
for (int i = 0; count < max_values && i < table->Capacity(); i++) {
Object* key = table->KeyAt(i);
if (table->IsKey(isolate, key)) values->set(count++, key);
}
DCHECK_EQ(max_values, count);
}
return *isolate->factory()->NewJSArrayWithElements(values);
} }
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -163,23 +163,19 @@ expression: (new Map([[1,2]])).entries() ...@@ -163,23 +163,19 @@ expression: (new Map([[1,2]])).entries()
[[Entries]]: [[Entries]]:
[ [
[0] : { [0] : {
key : {
description : 1
overflow : false
properties : [
]
type : number
}
value : { value : {
description : Array(2) description : 2
overflow : false overflow : false
properties : [ properties : [
[0] : {
name : 0
type : number
value : 1
}
[1] : {
name : 1
type : number
value : 2
}
] ]
subtype : array type : number
type : object
} }
} }
] ]
...@@ -209,15 +205,13 @@ expression: (new Set([[1,2]])).entries() ...@@ -209,15 +205,13 @@ expression: (new Set([[1,2]])).entries()
properties : [ properties : [
[0] : { [0] : {
name : 0 name : 0
subtype : array type : number
type : object value : 1
value : Array(2)
} }
[1] : { [1] : {
name : 1 name : 1
subtype : array type : number
type : object value : 2
value : Array(2)
} }
] ]
subtype : array subtype : array
......
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
print('Checks internal [[Entries]] in Runtime.getProperties output');
Protocol.Runtime.enable();
InspectorTest.runTestSuite([
function maps(next) {
checkExpression('new Map([[1,2],[3,4]])')
.then(() => checkExpression('new Map()'))
.then(next);
},
function mapIterators(next) {
checkExpression('new Map([[1,2],[3,4]]).entries()')
.then(() => checkExpression('it = new Map([[1,2],[3,4]]).entries(); it.next(); it'))
.then(() => checkExpression('it = new Map([[1,2],[3,4]]).keys(); it.next(); it'))
.then(() => checkExpression('it = new Map([[1,2],[3,4]]).values(); it.next(); it'))
.then(() => checkExpression('it = new Map([[1,2],[3,4]]).entries(); it.next(); it.next(); it'))
.then(next);
},
function sets(next) {
checkExpression('new Set([1,2])')
.then(() => checkExpression('new Set()'))
.then(next);
},
function setIterators(next) {
checkExpression('new Set([1,2]).values()')
.then(() => checkExpression('it = new Set([1,2]).values(); it.next(); it'))
.then(() => checkExpression('it = new Set([1,2]).keys(); it.next(); it'))
.then(() => checkExpression('it = new Set([1,2]).entries(); it.next(); it'))
.then(() => checkExpression('it = new Set([1,2]).values(); it.next(); it.next(); it'))
.then(next);
},
function weakMaps(next) {
checkExpression('new WeakMap()')
.then(() => checkExpression('new WeakMap([[{ a: 2 }, 42]])'))
.then(next);
},
function weakSets(next) {
checkExpression('new WeakSet()')
.then(() => checkExpression('new WeakSet([{a:2}])'))
.then(next);
}
]);
function checkExpression(expression)
{
InspectorTest.log(`expression: ${expression}`);
var entriesObjectId;
return Protocol.Runtime.evaluate({ expression: expression })
.then(message => Protocol.Runtime.getProperties({ objectId: message.result.result.objectId }))
.then(message => message.result.internalProperties.filter(p => p.name === '[[Entries]]')[0])
.then(entries => entriesObjectId = entries.value.objectId)
.then(() => Protocol.Runtime.callFunctionOn({ objectId: entriesObjectId, functionDeclaration: 'function f() { return this; }', returnByValue: true }))
.then(message => InspectorTest.logMessage(message.result.result.value))
.then(() => Protocol.Runtime.getProperties({ objectId: entriesObjectId, ownProperties: true }))
.then(message => InspectorTest.logMessage(message));
}
Checks internal properties in Runtime.getProperties output
Running test: generatorFunction
expression: (function* foo() { yield 1 })
{
id : <messageId>
result : {
internalProperties : [
[0] : {
name : [[FunctionLocation]]
value : {
description : Object
subtype : internal#location
type : object
value : {
columnNumber : 14
lineNumber : 0
scriptId : <scriptId>
}
}
}
[1] : {
name : [[IsGenerator]]
value : {
type : boolean
value : true
}
}
[2] : {
name : [[Scopes]]
value : {
className : Array
description : Scopes[1]
objectId : <objectId>
subtype : internal#scopeList
type : object
}
}
]
}
}
Running test: regularFunction
expression: (function foo() {})
{
id : <messageId>
result : {
internalProperties : [
[0] : {
name : [[FunctionLocation]]
value : {
description : Object
subtype : internal#location
type : object
value : {
columnNumber : 13
lineNumber : 0
scriptId : <scriptId>
}
}
}
[1] : {
name : [[Scopes]]
value : {
className : Array
description : Scopes[1]
objectId : <objectId>
subtype : internal#scopeList
type : object
}
}
]
}
}
Running test: boxedObjects
expression: new Number(239)
{
id : <messageId>
result : {
internalProperties : [
[0] : {
name : [[PrimitiveValue]]
value : {
description : 239
type : number
value : 239
}
}
]
}
}
expression: new Boolean(false)
{
id : <messageId>
result : {
internalProperties : [
[0] : {
name : [[PrimitiveValue]]
value : {
type : boolean
value : false
}
}
]
}
}
expression: new String('abc')
{
id : <messageId>
result : {
internalProperties : [
[0] : {
name : [[PrimitiveValue]]
value : {
type : string
value : abc
}
}
]
}
}
expression: Object(Symbol(42))
{
id : <messageId>
result : {
internalProperties : [
[0] : {
name : [[PrimitiveValue]]
value : {
description : Symbol(42)
objectId : <objectId>
type : symbol
}
}
]
}
}
Running test: promise
expression: Promise.resolve(42)
{
id : <messageId>
result : {
internalProperties : [
[0] : {
name : [[PromiseStatus]]
value : {
type : string
value : resolved
}
}
[1] : {
name : [[PromiseValue]]
value : {
description : 42
type : number
value : 42
}
}
]
}
}
expression: new Promise(() => undefined)
{
id : <messageId>
result : {
internalProperties : [
[0] : {
name : [[PromiseStatus]]
value : {
type : string
value : pending
}
}
[1] : {
name : [[PromiseValue]]
value : {
type : undefined
}
}
]
}
}
Running test: generatorObject
expression: (function* foo() { yield 1 })()
{
id : <messageId>
result : {
internalProperties : [
[0] : {
name : [[GeneratorStatus]]
value : {
type : string
value : suspended
}
}
[1] : {
name : [[GeneratorFunction]]
value : {
className : GeneratorFunction
description : function* foo() { yield 1 }
objectId : <objectId>
type : function
}
}
[2] : {
name : [[GeneratorReceiver]]
value : {
className : global
description : global
objectId : <objectId>
type : object
}
}
[3] : {
name : [[GeneratorLocation]]
value : {
description : Object
subtype : internal#location
type : object
value : {
columnNumber : 14
lineNumber : 0
scriptId : <scriptId>
}
}
}
[4] : {
name : [[Scopes]]
value : {
className : Array
description : Scopes[2]
objectId : <objectId>
subtype : internal#scopeList
type : object
}
}
]
}
}
Running test: iteratorObject
expression: (new Map([[1,2]])).entries()
{
id : <messageId>
result : {
internalProperties : [
[0] : {
name : [[IteratorHasMore]]
value : {
type : boolean
value : true
}
}
[1] : {
name : [[IteratorIndex]]
value : {
description : 0
type : number
value : 0
}
}
[2] : {
name : [[IteratorKind]]
value : {
type : string
value : entries
}
}
[3] : {
name : [[Entries]]
value : {
className : Array
description : Array(1)
objectId : <objectId>
subtype : array
type : object
}
}
]
}
}
expression: (new Set([[1,2]])).entries()
{
id : <messageId>
result : {
internalProperties : [
[0] : {
name : [[IteratorHasMore]]
value : {
type : boolean
value : true
}
}
[1] : {
name : [[IteratorIndex]]
value : {
description : 0
type : number
value : 0
}
}
[2] : {
name : [[IteratorKind]]
value : {
type : string
value : entries
}
}
[3] : {
name : [[Entries]]
value : {
className : Array
description : Array(1)
objectId : <objectId>
subtype : array
type : object
}
}
]
}
}
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
print('Checks internal properties in Runtime.getProperties output');
Protocol.Runtime.enable();
Protocol.Debugger.enable();
InspectorTest.runTestSuite([
function generatorFunction(next) {
checkExpression('(function* foo() { yield 1 })').then(next);
},
function regularFunction(next) {
checkExpression('(function foo() {})').then(next);
},
function boxedObjects(next) {
checkExpression('new Number(239)')
.then(() => checkExpression('new Boolean(false)'))
.then(() => checkExpression('new String(\'abc\')'))
.then(() => checkExpression('Object(Symbol(42))'))
.then(next);
},
function promise(next) {
checkExpression('Promise.resolve(42)')
.then(() => checkExpression('new Promise(() => undefined)'))
.then(next);
},
function generatorObject(next) {
checkExpression('(function* foo() { yield 1 })()')
.then(next);
},
function iteratorObject(next) {
checkExpression('(new Map([[1,2]])).entries()')
.then(() => checkExpression('(new Set([[1,2]])).entries()'))
.then(next);
}
]);
function checkExpression(expression)
{
InspectorTest.log(`expression: ${expression}`);
return Protocol.Runtime.evaluate({ expression: expression })
.then(message => Protocol.Runtime.getProperties({ objectId: message.result.result.objectId }))
.then(message => { delete message.result.result; return message; })
.then(InspectorTest.logMessage);
}
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