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..1968b3f11680210a88d2f81241e8d84ea9872fea 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, ownProperties, accessorPropertiesOnly, undefined, false); |
+ 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,7 +351,6 @@ InjectedScript.prototype = { |
descriptor.enumerable = false; |
if ("symbol" in descriptor) |
descriptor.symbol = this._wrapObject(descriptor.symbol, objectGroupName); |
- push(descriptors, descriptor); |
} |
return descriptors; |
}, |
@@ -377,60 +374,77 @@ InjectedScript.prototype = { |
* @param {!Object} object |
* @param {boolean=} ownProperties |
* @param {boolean=} accessorPropertiesOnly |
- * @param {?Array.<string>=} propertyNamesOnly |
+ * @param {?Array<string>=} propertyNamesOnly |
+ * @param {boolean=} forPreview |
+ * @param {!function(): boolean=} isSpaceAvailable |
+ * @param {!function(!Array<!Object>, !Object)=} customPush |
+ * @return {!Array<!Object>} |
*/ |
- _propertyDescriptors: function*(object, ownProperties, accessorPropertiesOnly, propertyNamesOnly) |
+ _propertyDescriptors: function(object, ownProperties, accessorPropertiesOnly, propertyNamesOnly, forPreview, isSpaceAvailable, customPush) |
{ |
+ var descriptors = []; |
+ descriptors.__proto__ = null; |
+ isSpaceAvailable = isSpaceAvailable || function() { return true; }; |
+ customPush = customPush || push; |
+ if (!isSpaceAvailable()) |
+ return descriptors; |
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 |
*/ |
- 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) { |
+ if (!isSpaceAvailable()) |
+ return; |
+ 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) |
+ if (accessorPropertiesOnly || forPreview) |
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 +452,81 @@ InjectedScript.prototype = { |
descriptor.isOwn = true; |
if (isSymbol(property)) |
descriptor.symbol = property; |
- yield descriptor; |
+ if (forPreview) { |
+ // Ignore __proto__ property. |
+ if (name === "__proto__") |
+ continue; |
+ |
+ // Ignore length property of array. |
+ if ((subtype === "array" || subtype === "typedarray") && name === "length") |
+ continue; |
+ |
+ // Ignore size property of map, set. |
+ if ((subtype === "map" || subtype === "set") && name === "size") |
+ continue; |
+ |
+ // Never preview prototype properties. |
+ if (!descriptor.isOwn) |
+ continue; |
+ |
+ // Ignore computed properties unless they have getters. |
+ if (!("value" in descriptor) && !descriptor.get) |
+ continue; |
+ } |
+ customPush(descriptors, nullifyObjectProto(descriptor)); |
kozy
2017/02/23 23:27:15
let's use regular push and pass callback to functi
luoe
2017/02/24 00:46:01
After our offline discussion, I've removed isSpace
|
} |
} |
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; |
+ process(o, [name]); |
+ if (!isSpaceAvailable()) |
+ return descriptors; |
break; |
} |
- if (ownProperties) |
+ if (ownProperties || forPreview) |
break; |
} |
} |
- return; |
- } |
- |
- /** |
- * @param {number} length |
- */ |
- function* arrayIndexNames(length) |
- { |
- for (var i = 0; i < length; ++i) |
- yield "" + i; |
- } |
- |
- var skipGetOwnPropertyNames; |
- try { |
- skipGetOwnPropertyNames = InjectedScriptHost.subtype(object) === "typedarray" && object.length > 500000; |
- } catch (e) { |
+ return descriptors; |
} |
- 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 { |
+ var objectLength = o.length; |
+ var skipGetOwnPropertyNames = subtype === "typedarray" && objectLength > 500000; |
+ if (skipGetOwnPropertyNames && o === object) { |
+ process(o, undefined, objectLength); |
+ } else { |
+ // First call Object.keys() to enforce ordering of the property descriptors. |
+ process(o, Object.keys(o)); |
+ if (isSpaceAvailable()) |
+ process(o, Object.getOwnPropertyNames(o)); |
+ } |
+ if (isSpaceAvailable() && Object.getOwnPropertySymbols) |
+ process(o, Object.getOwnPropertySymbols(o)); |
+ if (ownProperties) { |
+ var proto = this._objectPrototype(o); |
+ if (isSpaceAvailable() && proto && !accessorPropertiesOnly) { |
+ var descriptor = { name: "__proto__", value: proto, writable: true, configurable: true, enumerable: false, isOwn: true, __proto__: null }; |
+ customPush(descriptors, descriptor); |
+ } |
+ } |
+ } 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 || forPreview) |
break; |
- } |
} |
+ return descriptors; |
}, |
/** |
@@ -863,7 +892,7 @@ InjectedScript.RemoteObject.prototype = { |
{ |
var preview = this._createEmptyPreview(); |
var firstLevelKeysCount = firstLevelKeys ? firstLevelKeys.length : 0; |
- |
+ var overflow = false; |
var propertiesThreshold = { |
properties: isTable ? 1000 : max(5, firstLevelKeysCount), |
indexes: isTable ? 1000 : max(100, firstLevelKeysCount), |
@@ -871,11 +900,7 @@ InjectedScript.RemoteObject.prototype = { |
}; |
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, undefined, undefined, firstLevelKeys, true, isSpaceAvailable, customPush); |
// Add internal properties to preview. |
var rawInternalProperties = InjectedScriptHost.getInternalProperties(object) || []; |
@@ -886,64 +911,59 @@ 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 |
- }); |
+ }; |
+ customPush(descriptors, internalPropertyDescriptor); |
} |
- this._appendPropertyDescriptors(preview, internalProperties, propertiesThreshold, secondLevelKeys, isTable); |
+ this._appendPropertyPreviewDescriptors(preview, descriptors, secondLevelKeys, isTable); |
if (this.subtype === "map" || this.subtype === "set" || this.subtype === "iterator") |
this._appendEntriesPreview(entries, preview, skipEntriesPreview); |
} catch (e) {} |
+ preview.overflow = preview.overflow || overflow; |
return preview; |
+ |
+ /** |
+ * @return {boolean} |
+ */ |
+ function isSpaceAvailable() { |
+ return propertiesThreshold.indexes >= 0 && propertiesThreshold.properties >= 0; |
+ } |
+ |
+ /** |
+ * @param {!Array<!Object>} descriptors |
+ * @param {!Object} descriptor |
+ */ |
+ function customPush(descriptors, descriptor) { |
+ if (toString(descriptor.name >>> 0) === descriptor.name) |
+ propertiesThreshold.indexes--; |
+ else |
+ propertiesThreshold.properties--; |
+ if (!isSpaceAvailable()) |
+ overflow = true; |
+ else |
+ push(descriptors, descriptor); |
+ } |
}, |
/** |
* @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) |
+ _appendPropertyPreviewDescriptors: function(preview, descriptors, secondLevelKeys, isTable) |
{ |
- for (var descriptor of descriptors) { |
- if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0) |
- break; |
- if (!descriptor || descriptor.wasThrown) |
- continue; |
- |
+ for (var i = 0; i < descriptors.length; ++i) { |
+ var descriptor = descriptors[i]; |
var name = descriptor.name; |
- |
- // Ignore __proto__ property. |
- if (name === "__proto__") |
- continue; |
- |
- // Ignore length property of array. |
- if ((this.subtype === "array" || this.subtype === "typedarray") && name === "length") |
- continue; |
- |
- // Ignore size property of map, set. |
- if ((this.subtype === "map" || this.subtype === "set") && name === "size") |
- continue; |
- |
- // Never preview prototype properties. |
- if (!descriptor.isOwn) |
- continue; |
- |
- // 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; |
- } |
- |
var value = descriptor.value; |
var type = typeof value; |
@@ -951,9 +971,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 +987,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 +1007,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); |
} |
}, |