Chromium Code Reviews| Index: src/inspector/injected-script-source.js |
| diff --git a/src/inspector/injected-script-source.js b/src/inspector/injected-script-source.js |
| index d702e1a41795fc71b95d29325e5bdaf175f1cede..858d5c2944777313434fe071b09abd20de8f8234 100644 |
| --- a/src/inspector/injected-script-source.js |
| +++ b/src/inspector/injected-script-source.js |
| @@ -214,6 +214,47 @@ InjectedScript.closureTypes["global"] = "Global"; |
| InjectedScript.closureTypes["eval"] = "Eval"; |
| InjectedScript.closureTypes["module"] = "Module"; |
| +/** |
| + * @interface |
| + */ |
| +InjectedScript.AdderWithThreshold = function () {}; |
| +InjectedScript.AdderWithThreshold.prototype = { |
| + /** |
| + * @param {!Array<!Object>} descriptors |
| + * @param {!Object} descriptor |
| + */ |
| + add: function (descriptors, descriptor) {}, |
| + |
| + /** |
| + * @return {boolean} |
| + */ |
| + isSpaceAvailable: function () {} |
| +}; |
| + |
| +/** |
| + * @implements {InjectedScript.AdderWithThreshold} |
| + * @constructor |
| + */ |
| +InjectedScript.defaultPropertyAdder = function () {}; |
| +InjectedScript.defaultPropertyAdder.prototype = { |
|
kozy
2017/02/21 19:01:55
We try to avoid using any complex JS construction
luoe
2017/02/21 21:53:51
Yes, I think a customPush() will also need to be p
|
| + /** |
| + * @override |
| + * @param {!Array<!Object>} descriptors |
| + * @param {!Object} descriptor |
| + */ |
| + add: function (descriptors, descriptor) { |
| + push(descriptors, descriptor); |
| + }, |
| + |
| + /** |
| + * @override |
| + * @return {boolean} |
| + */ |
| + isSpaceAvailable: function () { |
| + return true; |
| + } |
| +}; |
| + |
| InjectedScript.prototype = { |
| /** |
| * @param {*} object |
| @@ -335,10 +376,11 @@ 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) { |
| + var adder = new InjectedScript.defaultPropertyAdder(); |
| + var descriptors = this._propertyDescriptors(object, ownProperties, accessorPropertiesOnly, undefined, adder, 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) |
| @@ -351,7 +393,6 @@ InjectedScript.prototype = { |
| descriptor.enumerable = false; |
| if ("symbol" in descriptor) |
| descriptor.symbol = this._wrapObject(descriptor.symbol, objectGroupName); |
| - push(descriptors, descriptor); |
| } |
| return descriptors; |
| }, |
| @@ -375,24 +416,34 @@ InjectedScript.prototype = { |
| * @param {!Object} object |
| * @param {boolean=} ownProperties |
| * @param {boolean=} accessorPropertiesOnly |
| - * @param {?Array.<string>=} propertyNamesOnly |
| + * @param {?Array<string>=} propertyNamesOnly |
| + * @param {!InjectedScript.AdderWithThreshold=} adder |
| + * @param {boolean=} forPreview |
| + * @return {!Array} |
| */ |
| - _propertyDescriptors: function*(object, ownProperties, accessorPropertiesOnly, propertyNamesOnly) |
| + _propertyDescriptors: function(object, ownProperties, accessorPropertiesOnly, propertyNamesOnly, adder, forPreview) |
| { |
| + var descriptors = []; |
|
kozy
2017/02/21 19:01:55
descriptors.__proto__ = null;
and then we will be
luoe
2017/02/21 21:53:51
Done.
|
| + if (!adder.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 {!Iterable<string|symbol|number>|!Array<string|number|symbol>=} properties |
| */ |
| - function* process(o, properties) |
| + function process(o, properties) |
| { |
| - for (var property of properties) { |
| + // When properties is not provided, iterate over the object's indices. |
| + var length = properties ? properties.length : (o.length || 0); |
|
kozy
2017/02/21 19:01:55
o.length can throw.
luoe
2017/02/21 21:53:51
I'll pass in objectLength
|
| + for (var i = 0; i < length; ++i) { |
| + if (!adder.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)); |
| @@ -417,7 +468,7 @@ InjectedScript.prototype = { |
| delete descriptor.set; |
| } |
| } catch (e) { |
| - if (accessorPropertiesOnly) |
| + if (accessorPropertiesOnly || forPreview) |
| continue; |
| descriptor = { value: e, wasThrown: true }; |
| } |
| @@ -438,33 +489,47 @@ InjectedScript.prototype = { |
| descriptor.isOwn = true; |
| if (isSymbol(property)) |
| descriptor.symbol = property; |
| - yield nullifyObjectProto(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; |
| + } |
| + adder.add(descriptors, nullifyObjectProto(descriptor)); |
| } |
| } |
| 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(/** @type {!Object} */ (o), [name])) |
| - yield descriptor; |
| + process(o, [name]); |
| + if (!adder.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; |
| + return descriptors; |
| } |
| var skipGetOwnPropertyNames; |
| @@ -473,32 +538,31 @@ InjectedScript.prototype = { |
| } catch (e) { |
| } |
| - for (var o = object; this._isDefined(o); o = this._objectPrototype(o)) { |
| - /** @type {!Object} */ (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; |
| + process(o, undefined); |
| } else { |
| // First call Object.keys() to enforce ordering of the property descriptors. |
| - for (var descriptor of process(o, Object.keys(o))) |
| - yield descriptor; |
| - for (var descriptor of process(o, Object.getOwnPropertyNames(o))) |
| - yield descriptor; |
| - } |
| - if (Object.getOwnPropertySymbols) { |
| - for (var descriptor of process(o, Object.getOwnPropertySymbols(o))) |
| - yield descriptor; |
| + process(o, Object.keys(o)); |
|
kozy
2017/02/21 19:01:55
All Object.* can throw, can we wrap it with try ca
luoe
2017/02/21 21:53:51
Done.
|
| + if (adder.isSpaceAvailable()) |
| + process(o, Object.getOwnPropertyNames(o)); |
| } |
| + if (adder.isSpaceAvailable() && Object.getOwnPropertySymbols) |
| + process(o, Object.getOwnPropertySymbols(o)); |
| 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 }; |
| - break; |
| + if (adder.isSpaceAvailable() && proto && !accessorPropertiesOnly) { |
| + var descriptor = { name: "__proto__", value: proto, writable: true, configurable: true, enumerable: false, isOwn: true, __proto__: null }; |
| + adder.add(descriptors, descriptor); |
| + } |
| } |
| + if (ownProperties || forPreview) |
| + break; |
| } |
| + return descriptors; |
| }, |
| /** |
| @@ -777,6 +841,54 @@ InjectedScript.RemoteObject = function(object, objectGroupName, doNotBind, force |
| } |
| } |
| +/** |
| + * @implements {InjectedScript.AdderWithThreshold} |
| + * @constructor |
| + * @param {boolean} isTable |
| + * @param {number} firstLevelKeysCount |
| + */ |
| +InjectedScript.PreviewPropertyAdder = function (isTable, firstLevelKeysCount) { |
| + this._propertiesThreshold = { |
| + properties: isTable ? 1000 : max(5, firstLevelKeysCount), |
| + indexes: isTable ? 1000 : max(100, firstLevelKeysCount), |
| + __proto__: null |
| + }; |
| + this._overflow = false; |
| +}; |
| + |
| +InjectedScript.PreviewPropertyAdder.prototype = { |
| + /** |
| + * @override |
| + * @param {!Array<!Object>} descriptors |
| + * @param {!Object} descriptor |
| + */ |
| + add: function (descriptors, descriptor) { |
| + if (toString(descriptor.name >>> 0) === descriptor.name) |
| + this._propertiesThreshold.indexes--; |
| + else |
| + this._propertiesThreshold.properties--; |
| + if (!this.isSpaceAvailable()) |
| + this._overflow = true; |
| + else |
| + push(descriptors, descriptor); |
| + }, |
| + |
| + /** |
| + * @override |
| + * @return {boolean} |
| + */ |
| + isSpaceAvailable: function () { |
| + return this._propertiesThreshold.indexes >= 0 && this._propertiesThreshold.properties >= 0; |
| + }, |
| + |
| + /** |
| + * @return {boolean} |
| + */ |
| + overflows: function() { |
| + return this._overflow; |
| + } |
| +}; |
| + |
| InjectedScript.RemoteObject.prototype = { |
| /** |
| @@ -864,19 +976,9 @@ 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 adder = new InjectedScript.PreviewPropertyAdder(!!isTable, firstLevelKeysCount); |
| 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, adder, true); |
| // Add internal properties to preview. |
| var rawInternalProperties = InjectedScriptHost.getInternalProperties(object) || []; |
| @@ -887,20 +989,22 @@ 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 |
| - }); |
| + }; |
| + adder.add(descriptors, internalPropertyDescriptor); |
|
kozy
2017/02/21 19:01:55
Our push safer.
luoe
2017/02/21 21:53:51
Replaced with a customPush() that calls push() int
|
| } |
| - 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 || adder.overflows(); |
| return preview; |
| }, |
| @@ -908,43 +1012,14 @@ InjectedScript.RemoteObject.prototype = { |
| /** |
| * @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; |
| @@ -952,9 +1027,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; |
| } |
| @@ -962,7 +1043,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; |
| } |
| @@ -982,24 +1063,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); |
| } |
| }, |