Commit 2943ded4 authored by Erik Luo's avatar Erik Luo Committed by Commit Bot

DevTools: add frontend support for BigInt

Prepares support for BigInts in
- JSAutocomplete
- objectValue, heap snapshot, console styles
- 'copy' command line API value

Screenshot: https://imgur.com/a/KqyBg

Bug: v8:7486
Change-Id: I12bab5a06dc940ebdb096c3b944fb71646f5b62c
Reviewed-on: https://chromium-review.googlesource.com/940774
Commit-Queue: Erik Luo <luoe@chromium.org>
Reviewed-by: default avatarDmitry Gozman <dgozman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#542032}
parent 7c4c36f7
...@@ -1431,6 +1431,10 @@ crbug.com/546215 [ Android ] fast/inline-block/overflow-clip.html [ Failure ] ...@@ -1431,6 +1431,10 @@ crbug.com/546215 [ Android ] fast/inline-block/overflow-clip.html [ Failure ]
crbug.com/548765 http/tests/devtools/console-fetch-logging.js [ Failure Pass ] crbug.com/548765 http/tests/devtools/console-fetch-logging.js [ Failure Pass ]
# When BigInts are on by default, these should pass and be removed along with their FlagExpectations.
crbug.com/v8/7486 http/tests/devtools/startup/console/console-format-startup-bigint.js [ Skip ]
crbug.com/v8/7486 http/tests/devtools/console/console-format-bigint.js [ Skip ]
crbug.com/564109 [ Win ] http/tests/webfont/font-display-intervention.html [ Pass Failure Timeout ] crbug.com/564109 [ Win ] http/tests/webfont/font-display-intervention.html [ Pass Failure Timeout ]
crbug.com/399951 http/tests/mime/javascript-mimetype-usecounters.html [ Pass Failure ] crbug.com/399951 http/tests/mime/javascript-mimetype-usecounters.html [ Pass Failure ]
......
Tests that console properly displays BigInts.
console-format-bigint.js:15 1n
console-format-bigint.js:16 BigInt {2n}
console-format-bigint.js:17 [1n]
console-format-bigint.js:18 [BigInt]
console-format-bigint.js:19 null 1n BigInt {2n}
Expanded all messages
console-format-bigint.js:15 1n
console-format-bigint.js:16 BigInt {2n}
__proto__: BigInt
[[PrimitiveValue]]: 2n
console-format-bigint.js:17 [1n]
0: 1n
length: 1
__proto__: Array(0)
console-format-bigint.js:18 [BigInt]
0: BigInt {2n}
length: 1
__proto__: Array(0)
console-format-bigint.js:19 null 1n BigInt {2n}
__proto__: BigInt
[[PrimitiveValue]]: 2n
Tests console logging for messages with BigInts that happen before DevTools is open.
console-format-startup-bigint.html:5 1n
console-format-startup-bigint.html:6 BigInt
__proto__: BigInt
[[PrimitiveValue]]: 2n
console-format-startup-bigint.html:7 Array(1)
0: 1n
length: 1
__proto__: Array(0)
console-format-startup-bigint.html:8 Array(1)
0: BigInt {2n}
length: 1
__proto__: Array(0)
console-format-startup-bigint.html:9 null 1n BigInt
__proto__: BigInt
[[PrimitiveValue]]: 2n
Tests that console properly displays BigInts.
console-format-bigint.js:15 1n
console-format-bigint.js:16 BigInt {2n}
console-format-bigint.js:17 [1n]
console-format-bigint.js:18 [BigInt]
console-format-bigint.js:19 null 1n BigInt {2n}
Expanded all messages
console-format-bigint.js:15 1n
console-format-bigint.js:16 BigInt {2n}
__proto__: BigInt
[[PrimitiveValue]]: 2n
console-format-bigint.js:17 [1n]
0: 1n
length: 1
__proto__: Array(0)
console-format-bigint.js:18 [BigInt]
0: BigInt {2n}
length: 1
__proto__: Array(0)
console-format-bigint.js:19 null 1n BigInt {2n}
__proto__: BigInt
[[PrimitiveValue]]: 2n
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// TODO(luoe): once BigInts are on by default, merge this test into
// http/tests/devtools/console/console-format.js
(async function() {
TestRunner.addResult('Tests that console properly displays BigInts.\n');
await TestRunner.loadModule('console_test_runner');
await TestRunner.showPanel('console');
await TestRunner.evaluateInPagePromise(`
var wrappedBigInt = Object(BigInt(2));
console.log(1n);
console.log(wrappedBigInt);
console.log([1n]);
console.log([wrappedBigInt]);
console.log(null, 1n, wrappedBigInt);
`);
ConsoleTestRunner.dumpConsoleMessages(false, false, TestRunner.textContentWithLineBreaks);
TestRunner.addResult('Expanded all messages');
ConsoleTestRunner.expandConsoleMessages(dumpConsoleMessages);
function dumpConsoleMessages() {
ConsoleTestRunner.dumpConsoleMessages(false, false, TestRunner.textContentWithLineBreaks);
TestRunner.completeTest();
}
})();
...@@ -12,7 +12,8 @@ code node size: 50000 ...@@ -12,7 +12,8 @@ code node size: 50000
closure node size: 600000 closure node size: 600000
regexp node size: 7000000 regexp node size: 7000000
native node size: 80000000 native node size: 80000000
{"total":987654341,"v8heap":907654341,"native":80000000,"code":50000,"jsArrays":20,"strings":300,"system":900000000} bigint node size: 900000000
{"total":1887654341,"v8heap":1807654341,"native":80000000,"code":50000,"jsArrays":20,"strings":300,"system":900000000}
Profiler was disabled. Profiler was disabled.
Tests console logging for messages with BigInts that happen before DevTools is open.
console-format-startup-bigint.html:5 1n
console-format-startup-bigint.html:6 BigInt
__proto__: BigInt
[[PrimitiveValue]]: 2n
console-format-startup-bigint.html:7 Array(1)
0: 1n
length: 1
__proto__: Array(0)
console-format-startup-bigint.html:8 Array(1)
0: BigInt {2n}
length: 1
__proto__: Array(0)
console-format-startup-bigint.html:9 null 1n BigInt
__proto__: BigInt
[[PrimitiveValue]]: 2n
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// TODO(luoe): once BigInts are on by default, merge this test into
// http/tests/devtools/startup/console/console-format-startup.js
(async function() {
await TestRunner.setupStartupTest('resources/console-format-startup-bigint.html');
TestRunner.addResult('Tests console logging for messages with BigInts that happen before DevTools is open.\n');
await TestRunner.loadModule('console_test_runner');
await TestRunner.showPanel('console');
TestRunner.hideInspectorView();
ConsoleTestRunner.expandConsoleMessages(finish);
function finish() {
ConsoleTestRunner.dumpConsoleMessagesIgnoreErrorStackFrames();
TestRunner.completeTest();
}
})();
<script type="text/javascript">
function onload()
{
var wrappedBigInt = Object(BigInt(2));
console.log(1n);
console.log(wrappedBigInt);
console.log([1n]);
console.log([wrappedBigInt]);
console.log(null, 1n, wrappedBigInt);
testRunner.inspectSecondaryWindow();
}
</script>
<body onload="onload()">
</body>
\ No newline at end of file
...@@ -547,6 +547,7 @@ Console.ConsoleViewMessage = class { ...@@ -547,6 +547,7 @@ Console.ConsoleViewMessage = class {
case 'regexp': case 'regexp':
case 'symbol': case 'symbol':
case 'undefined': case 'undefined':
case 'bigint':
element = this._formatParameterAsValue(output); element = this._formatParameterAsValue(output);
break; break;
default: default:
......
...@@ -195,6 +195,15 @@ Array.prototype.mergeOrdered = function(array, comparator) {}; ...@@ -195,6 +195,15 @@ Array.prototype.mergeOrdered = function(array, comparator) {};
*/ */
Int32Array.prototype.lowerBound = function(object, comparator, left, right) {}; Int32Array.prototype.lowerBound = function(object, comparator, left, right) {};
// TODO(luoe): remove these BigInt types once closure supports them.
/**
* @param {number|string} value
*/
const BigInt = function(value) {};
/** @typedef {*} */
const bigint = null;
// File System API // File System API
/** /**
* @constructor * @constructor
......
...@@ -117,7 +117,8 @@ HeapProfilerTestRunner.createHeapSnapshotMockFactories = function() { ...@@ -117,7 +117,8 @@ HeapProfilerTestRunner.createHeapSnapshotMockFactories = function() {
'regexp': 'regexp', 'regexp': 'regexp',
'number': 'number', 'number': 'number',
'native': 'native', 'native': 'native',
'synthetic': 'synthetic' 'synthetic': 'synthetic',
'bigint': 'bigint'
}; };
HeapProfilerTestRunner.HeapNode.prototype = { HeapProfilerTestRunner.HeapNode.prototype = {
......
...@@ -236,7 +236,9 @@ ObjectUI.JavaScriptAutocomplete = class { ...@@ -236,7 +236,9 @@ ObjectUI.JavaScriptAutocomplete = class {
completions = completions =
await object.callFunctionJSONPromise(getCompletions, [SDK.RemoteObject.toCallArgument(object.subtype)]) || await object.callFunctionJSONPromise(getCompletions, [SDK.RemoteObject.toCallArgument(object.subtype)]) ||
[]; [];
} else if (object.type === 'string' || object.type === 'number' || object.type === 'boolean') { } else if (
object.type === 'string' || object.type === 'number' || object.type === 'boolean' ||
object.type === 'bigint') {
const evaluateResult = await executionContext.evaluate( const evaluateResult = await executionContext.evaluate(
{ {
expression: '(' + getCompletions + ')("' + object.type + '")', expression: '(' + getCompletions + ')("' + object.type + '")',
...@@ -283,6 +285,9 @@ ObjectUI.JavaScriptAutocomplete = class { ...@@ -283,6 +285,9 @@ ObjectUI.JavaScriptAutocomplete = class {
object = new String(''); object = new String('');
else if (type === 'number') else if (type === 'number')
object = new Number(0); object = new Number(0);
// Object-wrapped BigInts cannot be constructed via `new BigInt`.
else if (type === 'bigint')
object = Object(BigInt(0));
else if (type === 'boolean') else if (type === 'boolean')
object = new Boolean(false); object = new Boolean(false);
else else
......
...@@ -41,6 +41,10 @@ ...@@ -41,6 +41,10 @@
color: rgb(28, 0, 207); color: rgb(28, 0, 207);
} }
.object-value-bigint {
color: rgb(0, 93, 0);
}
.object-value-string, .object-value-string,
.object-value-regexp, .object-value-regexp,
.object-value-symbol { .object-value-symbol {
......
...@@ -536,6 +536,9 @@ Profiler.HeapSnapshotGenericObjectNode = class extends Profiler.HeapSnapshotGrid ...@@ -536,6 +536,9 @@ Profiler.HeapSnapshotGenericObjectNode = class extends Profiler.HeapSnapshotGrid
value = value + '()'; value = value + '()';
valueStyle = 'function'; valueStyle = 'function';
break; break;
case 'bigint':
valueStyle = 'bigint';
break;
case 'number': case 'number':
valueStyle = 'number'; valueStyle = 'number';
break; break;
......
...@@ -84,7 +84,7 @@ SDK.RemoteObject = class { ...@@ -84,7 +84,7 @@ SDK.RemoteObject = class {
} }
/** /**
* @param {!Protocol.Runtime.RemoteObject|!SDK.RemoteObject|number|string|boolean|undefined|null} object * @param {!Protocol.Runtime.RemoteObject|!SDK.RemoteObject|number|string|boolean|undefined|null|bigint} object
* @return {!Protocol.Runtime.CallArgument} * @return {!Protocol.Runtime.CallArgument}
*/ */
static toCallArgument(object) { static toCallArgument(object) {
...@@ -94,24 +94,27 @@ SDK.RemoteObject = class { ...@@ -94,24 +94,27 @@ SDK.RemoteObject = class {
if (type === 'number') { if (type === 'number') {
const description = String(object); const description = String(object);
if (object === 0 && 1 / object < 0) if (object === 0 && 1 / object < 0)
return {unserializableValue: Protocol.Runtime.UnserializableValue.Negative0}; return {unserializableValue: SDK.RemoteObject.UnserializableNumber.Negative0};
if (description === 'NaN') if (description === SDK.RemoteObject.UnserializableNumber.NaN ||
return {unserializableValue: Protocol.Runtime.UnserializableValue.NaN}; description === SDK.RemoteObject.UnserializableNumber.Infinity ||
if (description === 'Infinity') description === SDK.RemoteObject.UnserializableNumber.NegativeInfinity)
return {unserializableValue: Protocol.Runtime.UnserializableValue.Infinity}; return {unserializableValue: description};
if (description === '-Infinity')
return {unserializableValue: Protocol.Runtime.UnserializableValue.NegativeInfinity};
return {value: object}; return {value: object};
} }
if (type === 'bigint') {
const value = String(object) + 'n';
return {unserializableValue: /** @type {!Protocol.Runtime.UnserializableValue} */ (value)};
}
if (type === 'string' || type === 'boolean') if (type === 'string' || type === 'boolean')
return {value: object}; return {value: object};
if (!object) if (!object)
return {value: null}; return {value: null};
if (typeof object.unserializableValue !== 'undefined') const isSDKRemoteObject = object instanceof SDK.RemoteObjectImpl;
if (!isSDKRemoteObject && typeof object.unserializableValue !== 'undefined')
return {unserializableValue: object.unserializableValue}; return {unserializableValue: object.unserializableValue};
if (object instanceof SDK.RemoteObjectImpl && typeof object._unserializableValue !== 'undefined') if (isSDKRemoteObject && typeof object._unserializableValue !== 'undefined')
return {unserializableValue: object._unserializableValue}; return {unserializableValue: object._unserializableValue};
if (typeof object.objectId !== 'undefined') if (typeof object.objectId !== 'undefined')
...@@ -212,6 +215,11 @@ SDK.RemoteObject = class { ...@@ -212,6 +215,11 @@ SDK.RemoteObject = class {
throw 'Not implemented'; throw 'Not implemented';
} }
/** @return {string|undefined} */
unserializableValue() {
throw 'Not implemented';
}
/** @return {string|undefined} */ /** @return {string|undefined} */
get description() { get description() {
throw 'Not implemented'; throw 'Not implemented';
...@@ -443,13 +451,15 @@ SDK.RemoteObjectImpl = class extends SDK.RemoteObject { ...@@ -443,13 +451,15 @@ SDK.RemoteObjectImpl = class extends SDK.RemoteObject {
if (!this._description && (typeof value !== 'object' || value === null)) if (!this._description && (typeof value !== 'object' || value === null))
this._description = value + ''; this._description = value + '';
this._hasChildren = false; this._hasChildren = false;
if (typeof unserializableValue !== 'undefined') { if (typeof unserializableValue === 'string') {
this._unserializableValue = unserializableValue; this._unserializableValue = unserializableValue;
if (unserializableValue === Protocol.Runtime.UnserializableValue.Infinity || if (unserializableValue === SDK.RemoteObject.UnserializableNumber.Infinity ||
unserializableValue === Protocol.Runtime.UnserializableValue.NegativeInfinity || unserializableValue === SDK.RemoteObject.UnserializableNumber.NegativeInfinity ||
unserializableValue === Protocol.Runtime.UnserializableValue.Negative0 || unserializableValue === SDK.RemoteObject.UnserializableNumber.Negative0 ||
unserializableValue === Protocol.Runtime.UnserializableValue.NaN) unserializableValue === SDK.RemoteObject.UnserializableNumber.NaN)
this._value = Number(unserializableValue); this._value = Number(unserializableValue);
else if (type === 'bigint' && unserializableValue.endsWith('n'))
this._value = BigInt(unserializableValue.substring(0, unserializableValue.length - 1));
else else
this._value = unserializableValue; this._value = unserializableValue;
...@@ -500,6 +510,14 @@ SDK.RemoteObjectImpl = class extends SDK.RemoteObject { ...@@ -500,6 +510,14 @@ SDK.RemoteObjectImpl = class extends SDK.RemoteObject {
return this._value; return this._value;
} }
/**
* @override
* @return {string|undefined}
*/
unserializableValue() {
return this._unserializableValue;
}
/** /**
* @override * @override
* @return {string|undefined} * @return {string|undefined}
...@@ -1389,3 +1407,14 @@ SDK.RemoteObject._descriptionLengthParenRegex = /\(([0-9]+)\)/; ...@@ -1389,3 +1407,14 @@ SDK.RemoteObject._descriptionLengthParenRegex = /\(([0-9]+)\)/;
* @type {!RegExp} * @type {!RegExp}
*/ */
SDK.RemoteObject._descriptionLengthSquareRegex = /\[([0-9]+)\]/; SDK.RemoteObject._descriptionLengthSquareRegex = /\[([0-9]+)\]/;
/**
* @const
* @enum {!Protocol.Runtime.UnserializableValue}
*/
SDK.RemoteObject.UnserializableNumber = {
Negative0: /** @type {!Protocol.Runtime.UnserializableValue} */ ('-0'),
NaN: /** @type {!Protocol.Runtime.UnserializableValue} */ ('NaN'),
Infinity: /** @type {!Protocol.Runtime.UnserializableValue} */ ('Infinity'),
NegativeInfinity: /** @type {!Protocol.Runtime.UnserializableValue} */ ('-Infinity')
};
...@@ -187,25 +187,26 @@ SDK.RuntimeModel = class extends SDK.SDKModel { ...@@ -187,25 +187,26 @@ SDK.RuntimeModel = class extends SDK.SDKModel {
} }
/** /**
* @param {number|string|boolean|undefined} value * @param {number|string|boolean|undefined|bigint} value
* @return {!SDK.RemoteObject} * @return {!SDK.RemoteObject}
*/ */
createRemoteObjectFromPrimitiveValue(value) { createRemoteObjectFromPrimitiveValue(value) {
const type = typeof value; const type = typeof value;
let unserializableValue = undefined; let unserializableValue = undefined;
if (typeof value === 'number') { if (type === 'number') {
const description = String(value); const description = String(value);
if (value === 0 && 1 / value < 0) if (value === 0 && 1 / value < 0)
unserializableValue = Protocol.Runtime.UnserializableValue.Negative0; unserializableValue = SDK.RemoteObject.UnserializableNumber.Negative0;
if (description === 'NaN') else if (
unserializableValue = Protocol.Runtime.UnserializableValue.NaN; description === SDK.RemoteObject.UnserializableNumber.NaN ||
if (description === 'Infinity') description === SDK.RemoteObject.UnserializableNumber.Infinity ||
unserializableValue = Protocol.Runtime.UnserializableValue.Infinity; description === SDK.RemoteObject.UnserializableNumber.NegativeInfinity)
if (description === '-Infinity') unserializableValue = description;
unserializableValue = Protocol.Runtime.UnserializableValue.NegativeInfinity;
if (typeof unserializableValue !== 'undefined')
value = undefined;
} }
if (type === 'bigint')
unserializableValue = /** @type {!Protocol.Runtime.UnserializableValue} */ (String(value) + 'n');
if (typeof unserializableValue !== 'undefined')
value = undefined;
return new SDK.RemoteObjectImpl(this, undefined, type, undefined, value, unserializableValue); return new SDK.RemoteObjectImpl(this, undefined, type, undefined, value, unserializableValue);
} }
...@@ -357,7 +358,7 @@ SDK.RuntimeModel = class extends SDK.SDKModel { ...@@ -357,7 +358,7 @@ SDK.RuntimeModel = class extends SDK.SDKModel {
*/ */
_copyRequested(object) { _copyRequested(object) {
if (!object.objectId) { if (!object.objectId) {
InspectorFrontendHost.copyText(object.value); InspectorFrontendHost.copyText(object.unserializableValue() || object.value);
return; return;
} }
object.callFunctionJSON( object.callFunctionJSON(
......
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