Index: src/inspector/injected-script-source.js |
diff --git a/src/inspector/injected-script-source.js b/src/inspector/injected-script-source.js |
index b52277a8ebec017a6deb3d3a590a8d66ae47f2ab..cc85c0b7f03ff378dc613cab59abced3a3badc31 100644 |
--- a/src/inspector/injected-script-source.js |
+++ b/src/inspector/injected-script-source.js |
@@ -335,12 +335,10 @@ InjectedScript.prototype = { |
object = object.object; |
} |
- var descriptors = []; |
- var iter = this._propertyDescriptors(object, ownProperties, accessorPropertiesOnly, undefined); |
// Go over properties, wrap object values. |
- for (var descriptor of iter) { |
- if (subtype === "internal#scopeList" && descriptor.name === "length") |
- continue; |
+ var descriptors = this._propertyDescriptors(object, addPropertyIfNeeded, ownProperties, accessorPropertiesOnly); |
+ for (var i = 0; i < descriptors.length; ++i) { |
+ var descriptor = descriptors[i]; |
if ("get" in descriptor) |
descriptor.get = this._wrapObject(descriptor.get, objectGroupName); |
if ("set" in descriptor) |
@@ -353,9 +351,18 @@ InjectedScript.prototype = { |
descriptor.enumerable = false; |
if ("symbol" in descriptor) |
descriptor.symbol = this._wrapObject(descriptor.symbol, objectGroupName); |
- push(descriptors, descriptor); |
} |
return descriptors; |
+ |
+ /** |
+ * @param {!Array<!Object>} descriptors |
+ * @param {!Object} descriptor |
+ * @return {boolean} |
+ */ |
+ function addPropertyIfNeeded(descriptors, descriptor) { |
+ push(descriptors, descriptor); |
+ return true; |
+ } |
}, |
/** |
@@ -375,62 +382,72 @@ InjectedScript.prototype = { |
/** |
* @param {!Object} object |
+ * @param {!function(!Array<!Object>, !Object)} addPropertyIfNeeded |
* @param {boolean=} ownProperties |
* @param {boolean=} accessorPropertiesOnly |
- * @param {?Array.<string>=} propertyNamesOnly |
+ * @param {?Array<string>=} propertyNamesOnly |
+ * @return {!Array<!Object>} |
*/ |
- _propertyDescriptors: function*(object, ownProperties, accessorPropertiesOnly, propertyNamesOnly) |
+ _propertyDescriptors: function(object, addPropertyIfNeeded, ownProperties, accessorPropertiesOnly, propertyNamesOnly) |
{ |
+ var descriptors = []; |
+ descriptors.__proto__ = null; |
var propertyProcessed = { __proto__: null }; |
+ var subtype = InjectedScriptHost.subtype(object); |
/** |
- * @param {?Object} o |
- * @param {!Iterable<string|symbol|number>|!Array<string|number|symbol>} properties |
+ * @param {!Object} o |
+ * @param {!Array<string|number|symbol>=} properties |
+ * @param {number=} objectLength |
+ * @return {boolean} |
*/ |
- function* process(o, properties) |
+ function process(o, properties, objectLength) |
{ |
- for (var property of properties) { |
+ // When properties is not provided, iterate over the object's indices. |
+ var length = properties ? properties.length : objectLength; |
+ for (var i = 0; i < length; ++i) { |
+ var property = properties ? properties[i] : ("" + i); |
+ if (propertyProcessed[property]) |
+ continue; |
+ propertyProcessed[property] = true; |
var name; |
if (isSymbol(property)) |
name = /** @type {string} */ (injectedScript._describe(property)); |
else |
name = typeof property === "number" ? ("" + property) : /** @type {string} */(property); |
- if (propertyProcessed[property]) |
+ if (subtype === "internal#scopeList" && name === "length") |
continue; |
+ var descriptor; |
try { |
- propertyProcessed[property] = true; |
- var descriptor = nullifyObjectProto(Object.getOwnPropertyDescriptor(o, property)); |
- if (descriptor) { |
- if (accessorPropertiesOnly && !("get" in descriptor || "set" in descriptor)) |
- continue; |
- if ("get" in descriptor && "set" in descriptor && name != "__proto__" && InjectedScriptHost.formatAccessorsAsProperties(object, descriptor.get) && !doesAttributeHaveObservableSideEffectOnGet(object, name)) { |
- descriptor.value = object[property]; |
- descriptor.isOwn = true; |
- delete descriptor.get; |
- delete descriptor.set; |
- } |
- } else { |
- // Not all bindings provide proper descriptors. Fall back to the writable, configurable property. |
- if (accessorPropertiesOnly) |
- continue; |
- try { |
- descriptor = { name: name, value: o[property], writable: false, configurable: false, enumerable: false, __proto__: null }; |
- if (o === object) |
- descriptor.isOwn = true; |
- yield descriptor; |
- } catch (e) { |
- // Silent catch. |
- } |
+ descriptor = Object.getOwnPropertyDescriptor(o, property); |
+ var isAccessorProperty = descriptor && ("get" in descriptor || "set" in descriptor); |
+ if (accessorPropertiesOnly && !isAccessorProperty) |
continue; |
+ if (descriptor && "get" in descriptor && "set" in descriptor && name !== "__proto__" && |
+ InjectedScriptHost.formatAccessorsAsProperties(object, descriptor.get) && |
+ !doesAttributeHaveObservableSideEffectOnGet(object, name)) { |
+ descriptor.value = object[property]; |
+ descriptor.isOwn = true; |
+ delete descriptor.get; |
+ delete descriptor.set; |
} |
} catch (e) { |
if (accessorPropertiesOnly) |
continue; |
- var descriptor = { __proto__: null }; |
- descriptor.value = e; |
- descriptor.wasThrown = true; |
+ descriptor = { value: e, wasThrown: true }; |
+ } |
+ |
+ // Not all bindings provide proper descriptors. Fall back to the non-configurable, non-enumerable, |
+ // non-writable property. |
+ if (!descriptor) { |
+ try { |
+ descriptor = { value: o[property], writable: false }; |
+ } catch (e) { |
+ // Silent catch. |
+ continue; |
+ } |
} |
descriptor.name = name; |
@@ -438,66 +455,72 @@ InjectedScript.prototype = { |
descriptor.isOwn = true; |
if (isSymbol(property)) |
descriptor.symbol = property; |
- yield descriptor; |
+ descriptor = nullifyObjectProto(descriptor); |
+ if (!addPropertyIfNeeded(descriptors, descriptor)) |
+ return false; |
} |
+ return true; |
} |
if (propertyNamesOnly) { |
for (var i = 0; i < propertyNamesOnly.length; ++i) { |
var name = propertyNamesOnly[i]; |
- for (var o = object; this._isDefined(o); o = this._objectPrototype(o)) { |
+ for (var o = object; this._isDefined(o); o = this._objectPrototype(/** @type {!Object} */ (o))) { |
+ o = /** @type {!Object} */ (o); |
if (InjectedScriptHost.objectHasOwnProperty(o, name)) { |
- for (var descriptor of process(o, [name])) |
- yield descriptor; |
+ if (!process(o, [name])) |
+ return descriptors; |
break; |
} |
if (ownProperties) |
break; |
} |
} |
- return; |
- } |
- |
- /** |
- * @param {number} length |
- */ |
- function* arrayIndexNames(length) |
- { |
- for (var i = 0; i < length; ++i) |
- yield "" + i; |
+ return descriptors; |
} |
var skipGetOwnPropertyNames; |
try { |
- skipGetOwnPropertyNames = InjectedScriptHost.subtype(object) === "typedarray" && object.length > 500000; |
+ skipGetOwnPropertyNames = subtype === "typedarray" && object.length > 500000; |
} catch (e) { |
} |
- for (var o = object; this._isDefined(o); o = this._objectPrototype(o)) { |
+ for (var o = object; this._isDefined(o); o = this._objectPrototype(/** @type {!Object} */ (o))) { |
+ o = /** @type {!Object} */ (o); |
if (InjectedScriptHost.subtype(o) === "proxy") |
continue; |
- if (skipGetOwnPropertyNames && o === object) { |
- // Avoid OOM crashes from getting all own property names of a large TypedArray. |
- for (var descriptor of process(o, arrayIndexNames(o.length))) |
- yield descriptor; |
- } else { |
- // First call Object.keys() to enforce ordering of the property descriptors. |
- for (var descriptor of process(o, Object.keys(/** @type {!Object} */ (o)))) |
- yield descriptor; |
- for (var descriptor of process(o, Object.getOwnPropertyNames(/** @type {!Object} */ (o)))) |
- yield descriptor; |
- } |
- if (Object.getOwnPropertySymbols) { |
- for (var descriptor of process(o, Object.getOwnPropertySymbols(/** @type {!Object} */ (o)))) |
- yield descriptor; |
+ |
+ try { |
+ if (skipGetOwnPropertyNames && o === object) { |
+ if (!process(o, undefined, o.length)) |
+ return descriptors; |
+ } else { |
+ // First call Object.keys() to enforce ordering of the property descriptors. |
+ if (!process(o, Object.keys(o))) |
+ return descriptors; |
+ if (!process(o, Object.getOwnPropertyNames(o))) |
+ return descriptors; |
+ } |
+ if (Object.getOwnPropertySymbols) { |
+ if (!process(o, Object.getOwnPropertySymbols(o))) |
+ return descriptors; |
+ } |
+ |
+ if (ownProperties) { |
+ var proto = this._objectPrototype(o); |
+ if (proto && !accessorPropertiesOnly) { |
+ var descriptor = { name: "__proto__", value: proto, writable: true, configurable: true, enumerable: false, isOwn: true, __proto__: null }; |
+ if (!addPropertyIfNeeded(descriptors, descriptor)) |
+ return descriptors; |
+ } |
+ } |
+ } catch (e) { |
} |
- if (ownProperties) { |
- var proto = this._objectPrototype(o); |
- if (proto && !accessorPropertiesOnly) |
- yield { name: "__proto__", value: proto, writable: true, configurable: true, enumerable: false, isOwn: true, __proto__: null }; |
+ |
+ if (ownProperties) |
break; |
- } |
} |
+ return descriptors; |
}, |
/** |
@@ -863,19 +886,15 @@ InjectedScript.RemoteObject.prototype = { |
{ |
var preview = this._createEmptyPreview(); |
var firstLevelKeysCount = firstLevelKeys ? firstLevelKeys.length : 0; |
- |
var propertiesThreshold = { |
properties: isTable ? 1000 : max(5, firstLevelKeysCount), |
indexes: isTable ? 1000 : max(100, firstLevelKeysCount), |
__proto__: null |
}; |
+ var subtype = this.subtype; |
try { |
- var descriptors = injectedScript._propertyDescriptors(object, undefined, undefined, firstLevelKeys); |
- |
- this._appendPropertyDescriptors(preview, descriptors, propertiesThreshold, secondLevelKeys, isTable); |
- if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0) |
- return preview; |
+ var descriptors = injectedScript._propertyDescriptors(object, addPropertyIfNeeded, false /* ownProperties */, undefined /* accessorPropertiesOnly */, firstLevelKeys); |
// Add internal properties to preview. |
var rawInternalProperties = InjectedScriptHost.getInternalProperties(object) || []; |
@@ -886,64 +905,80 @@ InjectedScript.RemoteObject.prototype = { |
entries = /** @type {!Array<*>} */(rawInternalProperties[i + 1]); |
continue; |
} |
- push(internalProperties, { |
+ var internalPropertyDescriptor = { |
name: rawInternalProperties[i], |
value: rawInternalProperties[i + 1], |
isOwn: true, |
enumerable: true, |
__proto__: null |
- }); |
+ }; |
+ if (!addPropertyIfNeeded(descriptors, internalPropertyDescriptor)) |
+ break; |
} |
- this._appendPropertyDescriptors(preview, internalProperties, propertiesThreshold, secondLevelKeys, isTable); |
+ this._appendPropertyPreviewDescriptors(preview, descriptors, secondLevelKeys, isTable); |
- if (this.subtype === "map" || this.subtype === "set" || this.subtype === "iterator") |
+ if (subtype === "map" || subtype === "set" || subtype === "iterator") |
this._appendEntriesPreview(entries, preview, skipEntriesPreview); |
} catch (e) {} |
return preview; |
- }, |
- |
- /** |
- * @param {!RuntimeAgent.ObjectPreview} preview |
- * @param {!Array.<*>|!Iterable.<*>} descriptors |
- * @param {!Object} propertiesThreshold |
- * @param {?Array.<string>=} secondLevelKeys |
- * @param {boolean=} isTable |
- */ |
- _appendPropertyDescriptors: function(preview, descriptors, propertiesThreshold, secondLevelKeys, isTable) |
- { |
- for (var descriptor of descriptors) { |
- if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0) |
- break; |
- if (!descriptor || descriptor.wasThrown) |
- continue; |
- var name = descriptor.name; |
+ /** |
+ * @param {!Array<!Object>} descriptors |
+ * @param {!Object} descriptor |
+ * @return {boolean} |
+ */ |
+ function addPropertyIfNeeded(descriptors, descriptor) { |
+ if (descriptor.wasThrown) |
+ return true; |
// Ignore __proto__ property. |
- if (name === "__proto__") |
- continue; |
+ if (descriptor.name === "__proto__") |
+ return true; |
// Ignore length property of array. |
- if ((this.subtype === "array" || this.subtype === "typedarray") && name === "length") |
- continue; |
+ if ((subtype === "array" || subtype === "typedarray") && descriptor.name === "length") |
+ return true; |
// Ignore size property of map, set. |
- if ((this.subtype === "map" || this.subtype === "set") && name === "size") |
- continue; |
+ if ((subtype === "map" || subtype === "set") && descriptor.name === "size") |
+ return true; |
// Never preview prototype properties. |
if (!descriptor.isOwn) |
- continue; |
+ return true; |
// Ignore computed properties unless they have getters. |
- if (!("value" in descriptor)) { |
- if (descriptor.get) |
- this._appendPropertyPreview(preview, { name: name, type: "accessor", __proto__: null }, propertiesThreshold); |
- continue; |
+ if (!("value" in descriptor) && !descriptor.get) |
+ return true; |
+ |
+ if (toString(descriptor.name >>> 0) === descriptor.name) |
+ propertiesThreshold.indexes--; |
+ else |
+ propertiesThreshold.properties--; |
+ |
+ var canContinue = propertiesThreshold.indexes >= 0 && propertiesThreshold.properties >= 0; |
+ if (!canContinue) { |
+ preview.overflow = true; |
+ return false; |
} |
+ push(descriptors, descriptor); |
+ return true; |
+ } |
+ }, |
+ /** |
+ * @param {!RuntimeAgent.ObjectPreview} preview |
+ * @param {!Array.<*>|!Iterable.<*>} descriptors |
+ * @param {?Array.<string>=} secondLevelKeys |
+ * @param {boolean=} isTable |
+ */ |
+ _appendPropertyPreviewDescriptors: function(preview, descriptors, secondLevelKeys, isTable) |
+ { |
+ for (var i = 0; i < descriptors.length; ++i) { |
+ var descriptor = descriptors[i]; |
+ var name = descriptor.name; |
var value = descriptor.value; |
var type = typeof value; |
@@ -951,9 +986,15 @@ InjectedScript.RemoteObject.prototype = { |
if (type === "undefined" && injectedScript._isHTMLAllCollection(value)) |
type = "object"; |
+ // Ignore computed properties unless they have getters. |
+ if (descriptor.get && !("value" in descriptor)) { |
+ push(preview.properties, { name: name, type: "accessor", __proto__: null }); |
+ continue; |
+ } |
+ |
// Render own properties. |
if (value === null) { |
- this._appendPropertyPreview(preview, { name: name, type: "object", subtype: "null", value: "null", __proto__: null }, propertiesThreshold); |
+ push(preview.properties, { name: name, type: "object", subtype: "null", value: "null", __proto__: null }); |
continue; |
} |
@@ -961,7 +1002,7 @@ InjectedScript.RemoteObject.prototype = { |
if (InjectedScript.primitiveTypes[type]) { |
if (type === "string" && value.length > maxLength) |
value = this._abbreviateString(value, maxLength, true); |
- this._appendPropertyPreview(preview, { name: name, type: type, value: toStringDescription(value), __proto__: null }, propertiesThreshold); |
+ push(preview.properties, { name: name, type: type, value: toStringDescription(value), __proto__: null }); |
continue; |
} |
@@ -981,24 +1022,6 @@ InjectedScript.RemoteObject.prototype = { |
description = this._abbreviateString(/** @type {string} */ (injectedScript._describe(value)), maxLength, subtype === "regexp"); |
property.value = description; |
} |
- this._appendPropertyPreview(preview, property, propertiesThreshold); |
- } |
- }, |
- |
- /** |
- * @param {!RuntimeAgent.ObjectPreview} preview |
- * @param {!Object} property |
- * @param {!Object} propertiesThreshold |
- */ |
- _appendPropertyPreview: function(preview, property, propertiesThreshold) |
- { |
- if (toString(property.name >>> 0) === property.name) |
- propertiesThreshold.indexes--; |
- else |
- propertiesThreshold.properties--; |
- if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0) { |
- preview.overflow = true; |
- } else { |
push(preview.properties, property); |
} |
}, |