tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 52d98a0908a25c0a60220529b2eaa7f88724a248
parent 460dd991693eb369923f8379b3d64799ec9b435a
Author: Atila Butkovits <abutkovits@mozilla.com>
Date:   Thu,  9 Oct 2025 22:14:06 +0300

Revert "Bug 1992249: apply code formatting via Lando" for causing failures at browser_test_feature_jstracing_objtestutils.js.

This reverts commit 90a4dc6c5759cc7e9a3a59d0fc61caf198d2ad8b.

Revert "Bug 1992249 - Fix MapLike entries loop r=ochameau,devtools-reviewers,profiler-reviewers,canaltinova"

This reverts commit c7382062ea10daa2c5c4254331b94d03f0ff1548.

Revert "Bug 1992249 - Verify version when deserializing r=ochameau,devtools-reviewers"

This reverts commit 0408a16e1698ed8c0c5007c383666bb515bf5dca.

Revert "Bug 1992249 - Properly handle symbols with no description r=ochameau,devtools-reviewers"

This reverts commit 52062ee92d1bed8a6b5d72603e06ee9d27343fd0.

Revert "Bug 1992249 - Fix getter/setter magic value mismatch r=ochameau,devtools-reviewers"

This reverts commit 52e5d7788afd86d90ca3817d31e1db2fa38bbde9.

Revert "Bug 1992249 - Fix up code style r=ochameau,devtools-reviewers"

This reverts commit 1459bdab2dcf0d7c9aae9b53a4a19ea3dc897327.

Revert "Bug 1992249 - Move ValueSummaryReader into reps r=ochameau,devtools-reviewers,profiler-reviewers,canaltinova"

This reverts commit f2e93aca0dbb327b346c6965c81ef471a0c2cb82.

Diffstat:
Mdevtools/client/shared/components/reps/reps/moz.build | 1-
Ddevtools/client/shared/components/reps/reps/value-summary-reader.mjs | 564-------------------------------------------------------------------------------
Mjs/public/Debug.h | 2+-
Mjs/src/debugger/ExecutionTracer.cpp | 2--
Mjs/src/jit-test/tests/debug/ExecutionTracer-traced-values.js | 21+--------------------
Mtools/profiler/tests/browser/browser.toml | 1+
Mtools/profiler/tests/browser/browser_test_feature_jstracing_objtestutils.js | 2+-
Mtools/profiler/tests/browser/browser_test_feature_jstracing_objtestutils.snapshot.mjs | 633+------------------------------------------------------------------------------
Atools/profiler/tests/browser/value_summary_reader.mjs | 544+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 551 insertions(+), 1219 deletions(-)

diff --git a/devtools/client/shared/components/reps/reps/moz.build b/devtools/client/shared/components/reps/reps/moz.build @@ -42,6 +42,5 @@ DevToolsModules( "symbol.mjs", "text-node.mjs", "undefined.mjs", - "value-summary-reader.mjs", "window.mjs", ) diff --git a/devtools/client/shared/components/reps/reps/value-summary-reader.mjs b/devtools/client/shared/components/reps/reps/value-summary-reader.mjs @@ -1,564 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -const MAX_ARGUMENTS_TO_RECORD = 4; -const ZERO_ARGUMENTS_MAGIC = -2; -const EXPIRED_VALUES_MAGIC = -1; - -const EXPECTED_VALUE_SUMMARIES_VERSION = 1; - -const JSVAL_TYPE_DOUBLE = 0x00; -const JSVAL_TYPE_INT32 = 0x01; -const JSVAL_TYPE_BOOLEAN = 0x02; -const JSVAL_TYPE_UNDEFINED = 0x03; -const JSVAL_TYPE_NULL = 0x04; -const JSVAL_TYPE_MAGIC = 0x05; -const JSVAL_TYPE_STRING = 0x06; -const JSVAL_TYPE_SYMBOL = 0x07; -const JSVAL_TYPE_BIGINT = 0x09; -const JSVAL_TYPE_OBJECT = 0x0c; - -const GETTER_SETTER_MAGIC = 0x0f; - -const GENERIC_OBJECT_HAS_DENSE_ELEMENTS = 1; -const SYMBOL_NO_DESCRIPTION = 1; - -const NUMBER_IS_OUT_OF_LINE_MAGIC = 0xf; -const MIN_INLINE_INT = -1; - -const STRING_ENCODING_LATIN1 = 0; -const STRING_ENCODING_TWO_BYTE = 1; -const STRING_ENCODING_UTF8 = 2; - -const OBJECT_KIND_NOT_IMPLEMENTED = 0; -const OBJECT_KIND_ARRAY_LIKE = 1; -const OBJECT_KIND_MAP_LIKE = 2; -const OBJECT_KIND_FUNCTION = 3; -const OBJECT_KIND_WRAPPED_PRIMITIVE_OBJECT = 4; -const OBJECT_KIND_GENERIC_OBJECT = 5; -const OBJECT_KIND_PROXY_OBJECT = 6; -const OBJECT_KIND_EXTERNAL = 7; -const OBJECT_KIND_ERROR = 8; - -const MAX_COLLECTION_VALUES = 16; - -const EXTERNAL_SUMMARY_EXPECTED_VERSION = 1; -const EXTERNAL_SUMMARY_KIND_OTHER = 0; -const EXTERNAL_SUMMARY_KIND_NODE = 1; -const EXTERNAL_SUMMARY_KIND_EXCEPTION = 2; - -// const EXTERNAL_NODE_SUBKIND_OTHER = 0; (handled implicitly) -const EXTERNAL_NODE_SUBKIND_ELEMENT = 1; -const EXTERNAL_NODE_SUBKIND_ATTR = 2; -const EXTERNAL_NODE_SUBKIND_DOCUMENT = 3; -const EXTERNAL_NODE_SUBKIND_DOCUMENT_FRAGMENT = 4; -const EXTERNAL_NODE_SUBKIND_TEXT = 5; -const EXTERNAL_NODE_SUBKIND_COMMENT = 6; - -class BufferReader { - #view; - #index; - - constructor(buffer, index = 0) { - this.#view = new DataView(buffer); - this.#index = index; - } - - setIndex(value) { - this.#index = value; - } - - getIndex() { - return this.#index; - } - - peekUint8() { - return this.#view.getUint8(this.#index); - } - - readUint8() { - const result = this.#view.getUint8(this.#index); - this.#index += 1; - return result; - } - - readUint16() { - const result = this.#view.getUint16(this.#index, true); - this.#index += 2; - return result; - } - - readUint32() { - const result = this.#view.getUint32(this.#index, true); - this.#index += 4; - return result; - } - - readInt8() { - const result = this.#view.getInt8(this.#index); - this.#index += 1; - return result; - } - - readInt16() { - const result = this.#view.getInt16(this.#index, true); - this.#index += 2; - return result; - } - - readInt32() { - const result = this.#view.getInt32(this.#index, true); - this.#index += 4; - return result; - } - - readFloat64() { - const result = this.#view.getFloat64(this.#index, true); - this.#index += 8; - return result; - } - - readString() { - const encodingAndLength = this.readUint16(); - const length = encodingAndLength & ~(0b11 << 14); - const encoding = encodingAndLength >> 14; - if (length == 0) { - return ""; - } - - let result = ""; - if (encoding == STRING_ENCODING_LATIN1) { - const decoder = new TextDecoder("latin1"); - result = decoder.decode( - this.#view.buffer.slice(this.#index, this.#index + length) - ); - this.#index += length; - } else if (encoding == STRING_ENCODING_UTF8) { - const decoder = new TextDecoder("utf-8"); - result = decoder.decode( - this.#view.buffer.slice(this.#index, this.#index + length) - ); - this.#index += length; - } else if (encoding == STRING_ENCODING_TWO_BYTE) { - const decoder = new TextDecoder("utf-16"); // this isn't quite right, is it? ugh. - const size = length * 2; - result = decoder.decode( - this.#view.buffer.slice(this.#index, this.#index + size) - ); - this.#index += size; - } - return result; - } -} - -function readArrayLikeSummary(result, reader, depth, shapes) { - const shapeId = reader.readUint32(); - const shape = shapes[shapeId]; - - if (!shape || shape.length <= 0) { - return; - } - result.class = shape[0]; - - const preview = {}; - preview.kind = "ArrayLike"; - - preview.items = []; - preview.length = reader.readUint32(); - if (depth < 1) { - for (let i = 0; i < preview.length && i < MAX_COLLECTION_VALUES; i++) { - if (reader.peekUint8() === JSVAL_TYPE_MAGIC) { - reader.readUint8(); - continue; - } - - const nestedSummary = readValueSummary(reader, depth + 1, shapes); - preview.items.push(nestedSummary); - } - } - - result.preview = preview; -} - -function readFunctionSummary(result, reader) { - result.class = "Function"; - result.name = reader.readString(); - result.parameterNames = []; - const numParameterNames = reader.readUint32(); - for (let i = 0; i < numParameterNames && i < MAX_COLLECTION_VALUES; i++) { - result.parameterNames.push(reader.readString()); - } -} - -function readMapLikeSummary(result, reader, depth, shapes) { - const shapeId = reader.readUint32(); - const shape = shapes[shapeId]; - - if (!shape || shape.length <= 0) { - return; - } - result.class = shape[0]; - - const preview = {}; - preview.kind = "MapLike"; - - preview.entries = []; - preview.size = reader.readUint32(); - if (depth < 1) { - for (let i = 0; i < preview.size && i < MAX_COLLECTION_VALUES; i++) { - const keySummary = readValueSummary(reader, depth + 1, shapes); - const valueSummary = readValueSummary(reader, depth + 1, shapes); - preview.entries.push([ - { - configurable: true, - enumerable: true, - writable: true, - value: keySummary, - }, - { - configurable: true, - enumerable: true, - writable: true, - value: valueSummary, - }, - ]); - } - } - - result.preview = preview; -} - -function readGenericObjectSummary(result, reader, flags, depth, shapes) { - const shapeId = reader.readUint32(); - const shape = shapes[shapeId]; - - if (!shape || shape.length <= 0) { - return; - } - result.class = shape[0]; - - const preview = {}; - preview.kind = "Object"; - - const hasDenseElements = !!(flags & GENERIC_OBJECT_HAS_DENSE_ELEMENTS); - const ownProperties = {}; - let ownPropertiesLength = reader.readUint32(); - - if (depth < 1) { - for (let i = 1; i < shape.length && i <= MAX_COLLECTION_VALUES; i++) { - const header = reader.peekUint8(); - const id = shape[i]; - const desc = { - configurable: true, - enumerable: true, - }; - if (header === GETTER_SETTER_MAGIC) { - reader.readUint8(); - desc.get = readValueSummary(reader, depth + 1, shapes); - desc.set = readValueSummary(reader, depth + 1, shapes); - } else { - const nestedSummary = readValueSummary(reader, depth + 1, shapes); - desc.writable = true; - desc.value = nestedSummary; - } - ownProperties[id] = desc; - } - } - - if (hasDenseElements) { - const elementsLength = reader.readUint32(); - if (depth < 1) { - for (let i = 0; i < elementsLength && i < MAX_COLLECTION_VALUES; i++) { - if (reader.peekUint8() === JSVAL_TYPE_MAGIC) { - reader.readUint8(); - continue; - } - ownPropertiesLength++; - const nestedSummary = readValueSummary(reader, depth + 1, shapes); - ownProperties[i] = { - configurable: true, - enumerable: true, - writable: true, - value: nestedSummary, - }; - } - } - } - - preview.ownProperties = ownProperties; - preview.ownPropertiesLength = ownPropertiesLength; - - result.preview = preview; -} - -function readClassFromShape(result, reader, shapes) { - const shapeId = reader.readUint32(); - const shape = shapes[shapeId]; - - if (!shape || shape.length <= 0) { - return; - } - result.class = shape[0]; -} - -function readNodeSummary(result, reader, depth, shapes) { - const preview = {}; - preview.kind = "DOMNode"; - preview.nodeType = reader.readUint16(); - preview.nodeName = reader.readString().toLowerCase(); - const subkindAndIsConnected = reader.readUint8(); - const subkind = subkindAndIsConnected & ~(1 << 7); - preview.isConnected = subkindAndIsConnected >> 7; - - if (subkind === EXTERNAL_NODE_SUBKIND_ELEMENT) { - preview.attributes = {}; - preview.attributesLength = reader.readUint32(); - for ( - let i = 0; - i < preview.attributesLength && i < MAX_COLLECTION_VALUES; - i++ - ) { - const attrName = reader.readString(); - const attrVal = reader.readString(); - preview.attributes[attrName] = attrVal; - } - } else if (subkind === EXTERNAL_NODE_SUBKIND_ATTR) { - preview.value = reader.readString(); - } else if (subkind === EXTERNAL_NODE_SUBKIND_DOCUMENT) { - preview.location = reader.readString(); - } else if (subkind === EXTERNAL_NODE_SUBKIND_DOCUMENT_FRAGMENT) { - preview.childNodesLength = reader.readUint32(); - if (depth < 1) { - preview.childNodes = []; - for ( - let i = 0; - i < preview.childNodesLength && i < MAX_COLLECTION_VALUES; - i++ - ) { - preview.childNodes.push(readValueSummary(reader, depth + 1, shapes)); - } - } - } else if ( - subkind === EXTERNAL_NODE_SUBKIND_TEXT || - subkind === EXTERNAL_NODE_SUBKIND_COMMENT - ) { - preview.textContent = reader.readString(); - } - - result.preview = preview; -} - -function readExceptionSummary(result, reader, _depth, _shapes) { - result.class = "Error"; - const preview = {}; - preview.kind = "DOMException"; - preview.name = reader.readString(); - preview.message = reader.readString(); - preview.code = reader.readUint16(); - preview.result = reader.readUint32(); - preview.lineNumber = reader.readUint32(); - preview.columnNumber = reader.readUint32(); - preview.stack = reader.readString(); - result.preview = preview; -} - -function readExternalObjectSummary(result, reader, depth, shapes) { - readClassFromShape(result, reader, shapes); - - const startIndex = reader.getIndex(); - const size = reader.readUint32(); - try { - const version = reader.readUint8(); - if (version != EXTERNAL_SUMMARY_EXPECTED_VERSION) { - return; - } - - const kind = reader.readUint8(); - if (kind == EXTERNAL_SUMMARY_KIND_OTHER) { - return; - } - - switch (kind) { - case EXTERNAL_SUMMARY_KIND_NODE: { - readNodeSummary(result, reader, depth, shapes); - break; - } - case EXTERNAL_SUMMARY_KIND_EXCEPTION: { - readExceptionSummary(result, reader, depth, shapes); - break; - } - default: - } - } finally { - reader.setIndex(startIndex + size); - } -} - -function readErrorObjectSummary(result, reader, depth, shapes) { - const shapeId = reader.readUint32(); - const shape = shapes[shapeId]; - - if (!shape || shape.length <= 0) { - return; - } - - const preview = {}; - result.type = "object"; - result.class = shape[0]; - result.isError = true; - preview.kind = "Error"; - preview.name = reader.readString(); - preview.message = reader.readString(); - preview.stack = reader.readString(); - preview.fileName = reader.readString(); - preview.lineNumber = reader.readUint32(); - preview.columnNumber = reader.readUint32(); - result.preview = preview; -} - -function readObjectSummary(reader, flags, depth, shapes) { - const result = { - type: "object", - class: undefined, - ownPropertyLength: 0, - isError: false, - extensible: false, - sealed: false, - frozen: false, - }; - - const kind = reader.readUint8(); - switch (kind) { - case OBJECT_KIND_NOT_IMPLEMENTED: - readClassFromShape(result, reader, shapes); - break; - case OBJECT_KIND_ARRAY_LIKE: - readArrayLikeSummary(result, reader, depth, shapes); - break; - case OBJECT_KIND_MAP_LIKE: - readMapLikeSummary(result, reader, depth, shapes); - break; - case OBJECT_KIND_FUNCTION: - readFunctionSummary(result, reader); - break; - case OBJECT_KIND_EXTERNAL: - readExternalObjectSummary(result, reader, depth, shapes); - break; - case OBJECT_KIND_ERROR: - readErrorObjectSummary(result, reader, depth, shapes); - break; - case OBJECT_KIND_WRAPPED_PRIMITIVE_OBJECT: { - result.wrappedValue = readValueSummary(reader, depth, shapes); - readGenericObjectSummary(result, reader, flags, depth, shapes); - break; - } - case OBJECT_KIND_GENERIC_OBJECT: { - readGenericObjectSummary(result, reader, flags, depth, shapes); - break; - } - case OBJECT_KIND_PROXY_OBJECT: { - readClassFromShape(result, reader, shapes); - result.preview = { - kind: "Object", - ownProperties: Object.create(null), - ownPropertiesLength: 0, - }; - break; - } - default: - throw new Error("Bad object kind"); - } - - return result; -} - -function readValueSummary(reader, depth, shapes) { - const header = reader.readUint8(); - const type = header & 0x0f; - const flags = (header & 0xf0) >> 4; - switch (type) { - case JSVAL_TYPE_DOUBLE: - if (flags == NUMBER_IS_OUT_OF_LINE_MAGIC) { - const value = reader.readFloat64(); - if (value === Infinity) { - return { type: "Infinity" }; - } else if (value === -Infinity) { - return { type: "-Infinity" }; - } else if (Number.isNaN(value)) { - return { type: "NaN" }; - } else if (!value && 1 / value === -Infinity) { - return { type: "-0" }; - } - return value; - } - return 0; - - case JSVAL_TYPE_INT32: - if (flags === NUMBER_IS_OUT_OF_LINE_MAGIC) { - return reader.readInt32(); - } - return flags + MIN_INLINE_INT; - - case JSVAL_TYPE_BOOLEAN: - return !!flags; - case JSVAL_TYPE_NULL: - return { type: "null" }; - case JSVAL_TYPE_UNDEFINED: - return { type: "undefined" }; - case JSVAL_TYPE_SYMBOL: - if (flags & SYMBOL_NO_DESCRIPTION) { - return { - type: "symbol", - }; - } - return { - type: "symbol", - name: reader.readString(), - }; - case JSVAL_TYPE_BIGINT: - return { - type: "BigInt", - text: reader.readString(), - }; - case JSVAL_TYPE_STRING: { - return reader.readString(); - } - case JSVAL_TYPE_OBJECT: { - return readObjectSummary(reader, flags, depth, shapes); - } - default: - throw new Error("Bad value type"); - } -} - -function bufferVersion(valuesBuffer) { - const reader = new BufferReader(valuesBuffer, 0); - const version = reader.readUint32(); - return version; -} - -function getArgumentSummaries(valuesBuffer, shapes, valuesBufferIndex) { - if (valuesBufferIndex === ZERO_ARGUMENTS_MAGIC) { - return []; - } - if (valuesBufferIndex === EXPIRED_VALUES_MAGIC) { - return "<missing>"; - } - const theirVersion = bufferVersion(valuesBuffer); - if (theirVersion != EXPECTED_VALUE_SUMMARIES_VERSION) { - throw new Error( - `Unexpected tracer arguments buffer format. Expected ${EXPECTED_VALUE_SUMMARIES_VERSION}, received ${theirVersion}.` - ); - } - - const reader = new BufferReader(valuesBuffer, valuesBufferIndex); - const argc = reader.readUint32(); - const args = new Array(argc); - for (let i = 0; i < argc && i < MAX_ARGUMENTS_TO_RECORD; i++) { - args[i] = readValueSummary(reader, 0, shapes); - } - return args; -} - -export const ValueSummaryReader = { getArgumentSummaries }; diff --git a/js/public/Debug.h b/js/public/Debug.h @@ -280,7 +280,7 @@ struct ValueSummary { struct ObjectSummary { // This is a special value for ValueSummary::typeAndFlags. It should be noted // that this only works as long as 0xf is not a valid JS::ValueType. - static const uint8_t GETTER_SETTER_MAGIC = 0x0f; + static const uint8_t GETTER_SETTER_MAGIC = 0xf0; enum class Kind : uint8_t { NotImplemented, diff --git a/js/src/debugger/ExecutionTracer.cpp b/js/src/debugger/ExecutionTracer.cpp @@ -668,8 +668,6 @@ void ValueSummaries::writeHeader(JS::ValueType type, uint8_t flags) { JS::ValueSummary header; header.type = type; header.flags = flags; - MOZ_ASSERT(*reinterpret_cast<uint8_t*>(&header) != - JS::ObjectSummary::GETTER_SETTER_MAGIC); valueData_->writeBytes(reinterpret_cast<const uint8_t*>(&header), sizeof(header)); } diff --git a/js/src/jit-test/tests/debug/ExecutionTracer-traced-values.js b/js/src/jit-test/tests/debug/ExecutionTracer-traced-values.js @@ -14,7 +14,7 @@ const JSVAL_TYPE_SYMBOL = 0x07; const JSVAL_TYPE_BIGINT = 0x09; const JSVAL_TYPE_OBJECT = 0x0c; -const GETTER_SETTER_MAGIC = 0x0f; +const GETTER_SETTER_MAGIC = 0xf0; const GENERIC_OBJECT_HAS_DENSE_ELEMENTS = 1; @@ -159,7 +159,6 @@ wrappedNumber.foo = 0; function foobaz(a, a) {}, function barbaz(a, ...rest) {}, ccw.wrappedObject, - {get testGetter() {return 42}}, ].map(function f1(x) { return x; });`); const trace = g.getExecutionTrace(); @@ -611,23 +610,5 @@ testSingleArgument(events.shift(), reader => { assertEq(reader.readUint8(), JSVAL_TYPE_INT32 | inlinedInt32Flags(0)); }); -testSingleArgument(events.shift(), reader => { - assertEq(reader.readUint8(), JSVAL_TYPE_OBJECT); - assertEq(reader.readUint8(), OBJECT_KIND_GENERIC_OBJECT); - - let shape = trace[0].shapeSummaries[reader.readUint32()]; - assertEq(shape.length, 2); - assertEq(shape[0], "Object"); - assertEq(shape[1], "testGetter"); - - assertEq(reader.readUint32(), 1); - assertEq(reader.readUint8(), GETTER_SETTER_MAGIC); - assertEq(reader.readUint8(), JSVAL_TYPE_OBJECT); - assertEq(reader.readUint8(), OBJECT_KIND_FUNCTION); - assertEq(reader.readString(), "get testGetter"); - assertEq(reader.readUint32(), 0); - assertEq(reader.readUint8(), JSVAL_TYPE_UNDEFINED); -}); - assertEq(events.length, 0); diff --git a/tools/profiler/tests/browser/browser.toml b/tools/profiler/tests/browser/browser.toml @@ -18,6 +18,7 @@ skip-if = ["!nightly_build"] # This features is only enabled on nightly via MOZ_ ["browser_test_feature_jstracing_objtestutils.js"] support-files = [ "browser_test_feature_jstracing_objtestutils.snapshot.mjs", + "value_summary_reader.mjs" ] skip-if = [ # This features is only enabled on nightly via MOZ_EXECUTION_TRACING build flag diff --git a/tools/profiler/tests/browser/browser_test_feature_jstracing_objtestutils.js b/tools/profiler/tests/browser/browser_test_feature_jstracing_objtestutils.js @@ -9,7 +9,7 @@ const { XPCShellContentUtils } = ChromeUtils.importESModule( "resource://testing-common/XPCShellContentUtils.sys.mjs" ); const { ValueSummaryReader } = ChromeUtils.importESModule( - "resource://devtools/client/shared/components/reps/reps/value-summary-reader.mjs" + "chrome://mochitests/content/browser/tools/profiler/tests/browser/value_summary_reader.mjs" ); /** diff --git a/tools/profiler/tests/browser/browser_test_feature_jstracing_objtestutils.snapshot.mjs b/tools/profiler/tests/browser/browser_test_feature_jstracing_objtestutils.snapshot.mjs @@ -217,88 +217,7 @@ export default [ "frozen": false, "preview": { "kind": "MapLike", - "entries": [ - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "object", - "class": "Object", - "ownPropertyLength": 0, - "isError": false, - "extensible": false, - "sealed": false, - "frozen": false, - "preview": { - "kind": "Object", - "ownProperties": {}, - "ownPropertiesLength": 1 - } - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "object", - "class": "Object", - "ownPropertyLength": 0, - "isError": false, - "extensible": false, - "sealed": false, - "frozen": false, - "preview": { - "kind": "Object", - "ownProperties": {}, - "ownPropertiesLength": 1 - } - } - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "object", - "class": "Object", - "ownPropertyLength": 0, - "isError": false, - "extensible": false, - "sealed": false, - "frozen": false, - "preview": { - "kind": "Object", - "ownProperties": {}, - "ownPropertiesLength": 1 - } - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "object", - "class": "Object", - "ownPropertyLength": 0, - "isError": false, - "extensible": false, - "sealed": false, - "frozen": false, - "preview": { - "kind": "Object", - "ownProperties": {}, - "ownPropertiesLength": 1 - } - } - } - ] - ], + "entries": [], "size": 2 } }, @@ -314,280 +233,7 @@ export default [ "frozen": false, "preview": { "kind": "MapLike", - "entries": [ - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "0" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 0 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "1" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 1 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "2" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 2 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "3" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 3 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "4" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 4 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "5" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 5 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "6" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 6 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "7" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 7 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "8" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 8 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "9" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 9 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "10" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 10 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "11" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 11 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "12" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 12 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "13" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 13 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "14" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 14 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "15" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 15 - } - ] - ], + "entries": [], "size": 20 } }, @@ -603,280 +249,7 @@ export default [ "frozen": false, "preview": { "kind": "MapLike", - "entries": [ - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "0" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 0 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "1" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 1 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "2" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 2 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "3" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 3 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "4" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 4 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "5" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 5 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "6" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 6 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "7" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 7 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "8" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 8 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "9" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 9 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "10" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 10 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "11" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 11 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "12" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 12 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "13" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 13 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "14" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 14 - } - ], - [ - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": { - "type": "symbol", - "name": "15" - } - }, - { - "configurable": true, - "enumerable": true, - "writable": true, - "value": 15 - } - ] - ], + "entries": [], "size": 331 } }, diff --git a/tools/profiler/tests/browser/value_summary_reader.mjs b/tools/profiler/tests/browser/value_summary_reader.mjs @@ -0,0 +1,544 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +const MAX_ARGUMENTS_TO_RECORD = 4; +const ZERO_ARGUMENTS_MAGIC = -2; +const EXPIRED_VALUES_MAGIC = -1; + +const JSVAL_TYPE_DOUBLE = 0x00; +const JSVAL_TYPE_INT32 = 0x01; +const JSVAL_TYPE_BOOLEAN = 0x02; +const JSVAL_TYPE_UNDEFINED = 0x03; +const JSVAL_TYPE_NULL = 0x04; +const JSVAL_TYPE_MAGIC = 0x05; +const JSVAL_TYPE_STRING = 0x06; +const JSVAL_TYPE_SYMBOL = 0x07; +const JSVAL_TYPE_BIGINT = 0x09; +const JSVAL_TYPE_OBJECT = 0x0c; + +const GETTER_SETTER_MAGIC = 0xf0; + +const GENERIC_OBJECT_HAS_DENSE_ELEMENTS = 1; + +const NUMBER_IS_OUT_OF_LINE_MAGIC = 0xf; +const MIN_INLINE_INT = -1; + +const STRING_ENCODING_LATIN1 = 0; +const STRING_ENCODING_TWO_BYTE = 1; +const STRING_ENCODING_UTF8 = 2; + +const OBJECT_KIND_NOT_IMPLEMENTED = 0; +const OBJECT_KIND_ARRAY_LIKE = 1; +const OBJECT_KIND_MAP_LIKE = 2; +const OBJECT_KIND_FUNCTION = 3; +const OBJECT_KIND_WRAPPED_PRIMITIVE_OBJECT = 4; +const OBJECT_KIND_GENERIC_OBJECT = 5; +const OBJECT_KIND_PROXY_OBJECT = 6; +const OBJECT_KIND_EXTERNAL = 7; +const OBJECT_KIND_ERROR = 8; + +const MAX_COLLECTION_VALUES = 16; + +const EXTERNAL_SUMMARY_EXPECTED_VERSION = 1; +const EXTERNAL_SUMMARY_KIND_OTHER = 0; +const EXTERNAL_SUMMARY_KIND_NODE = 1; +const EXTERNAL_SUMMARY_KIND_EXCEPTION = 2; + +// const EXTERNAL_NODE_SUBKIND_OTHER = 0; (handled implicitly) +const EXTERNAL_NODE_SUBKIND_ELEMENT = 1; +const EXTERNAL_NODE_SUBKIND_ATTR = 2; +const EXTERNAL_NODE_SUBKIND_DOCUMENT = 3; +const EXTERNAL_NODE_SUBKIND_DOCUMENT_FRAGMENT = 4; +const EXTERNAL_NODE_SUBKIND_TEXT = 5; +const EXTERNAL_NODE_SUBKIND_COMMENT = 6; + +class BufferReader { + #view; + #index; + + constructor(buffer, index = 0) { + this.#view = new DataView(buffer); + this.#index = index; + } + + setIndex(value) { + this.#index = value; + } + + getIndex() { + return this.#index; + } + + peekUint8() { + return this.#view.getUint8(this.#index); + } + + readUint8() { + const result = this.#view.getUint8(this.#index); + this.#index += 1; + return result; + } + + readUint16() { + const result = this.#view.getUint16(this.#index, true); + this.#index += 2; + return result; + } + + readUint32() { + const result = this.#view.getUint32(this.#index, true); + this.#index += 4; + return result; + } + + readInt8() { + const result = this.#view.getInt8(this.#index); + this.#index += 1; + return result; + } + + readInt16() { + const result = this.#view.getInt16(this.#index, true); + this.#index += 2; + return result; + } + + readInt32() { + const result = this.#view.getInt32(this.#index, true); + this.#index += 4; + return result; + } + + readFloat64() { + const result = this.#view.getFloat64(this.#index, true); + this.#index += 8; + return result; + } + + readString() { + const encodingAndLength = this.readUint16(); + const length = encodingAndLength & ~(0b11 << 14); + const encoding = encodingAndLength >> 14; + if (length == 0) { + return ""; + } + + let result = ""; + if (encoding == STRING_ENCODING_LATIN1) { + const decoder = new TextDecoder("latin1"); + result = decoder.decode( + this.#view.buffer.slice(this.#index, this.#index + length) + ); + this.#index += length; + } else if (encoding == STRING_ENCODING_UTF8) { + const decoder = new TextDecoder("utf-8"); + result = decoder.decode( + this.#view.buffer.slice(this.#index, this.#index + length) + ); + this.#index += length; + } else if (encoding == STRING_ENCODING_TWO_BYTE) { + const decoder = new TextDecoder("utf-16"); // this isn't quite right, is it? ugh. + const size = length * 2; + result = decoder.decode( + this.#view.buffer.slice(this.#index, this.#index + size) + ); + this.#index += size; + } + return result; + } +} + +function readArrayLikeSummary(result, reader, flags, depth, shapes) { + const shapeId = reader.readUint32(); + const shape = shapes[shapeId]; + + if (!shape || shape.length <= 0) { + return; + } + result.class = shape[0]; + + const preview = {}; + preview.kind = "ArrayLike"; + + preview.items = []; + preview.length = reader.readUint32(); + if (depth < 1) { + for (let i = 0; i < preview.length && i < MAX_COLLECTION_VALUES; i++) { + if (reader.peekUint8() == JSVAL_TYPE_MAGIC) { + reader.readUint8(); + continue; + } + + const nestedSummary = readValueSummary(reader, depth + 1, shapes); + preview.items.push(nestedSummary); + } + } + + result.preview = preview; +} + +function readFunctionSummary(result, reader) { + result.class = "Function"; + result.name = reader.readString(); + result.parameterNames = []; + const numParameterNames = reader.readUint32(); + for (let i = 0; i < numParameterNames && i < MAX_COLLECTION_VALUES; i++) { + result.parameterNames.push(reader.readString()); + } +} + +function readMapLikeSummary(result, reader, flags, depth, shapes) { + const shapeId = reader.readUint32(); + const shape = shapes[shapeId]; + + if (!shape || shape.length <= 0) { + return; + } + result.class = shape[0]; + + const preview = {}; + preview.kind = "MapLike"; + + preview.entries = []; + preview.size = reader.readUint32(); + if (depth < 1) { + for (let i = 0; i < preview.length && i < MAX_COLLECTION_VALUES; i++) { + const keySummary = readValueSummary(reader, depth + 1, shapes); + const valueSummary = readValueSummary(reader, depth + 1, shapes); + preview.entries.push([ + { + configurable: true, + enumerable: true, + writable: true, + value: keySummary, + }, + { + configurable: true, + enumerable: true, + writable: true, + value: valueSummary, + }, + ]); + } + } + + result.preview = preview; +} + +function readGenericObjectSummary(result, reader, flags, depth, shapes) { + const shapeId = reader.readUint32(); + const shape = shapes[shapeId]; + + if (!shape || shape.length <= 0) { + return; + } + result.class = shape[0]; + + const preview = {}; + preview.kind = "Object"; + + const hasDenseElements = !!(flags & GENERIC_OBJECT_HAS_DENSE_ELEMENTS); + const ownProperties = {}; + let ownPropertiesLength = reader.readUint32(); + + if (depth < 1) { + for (let i = 1; i < shape.length && i <= MAX_COLLECTION_VALUES; i++) { + const header = reader.peekUint8(); + const id = shape[i]; + const desc = { + configurable: true, + enumerable: true, + }; + if (header == GETTER_SETTER_MAGIC) { + reader.readUint8(); + desc.get = readValueSummary(reader, depth + 1, shapes); + desc.set = readValueSummary(reader, depth + 1, shapes); + } else { + const nestedSummary = readValueSummary(reader, depth + 1, shapes); + desc.writable = true; + desc.value = nestedSummary; + } + ownProperties[id] = desc; + } + } + + if (hasDenseElements) { + const elementsLength = reader.readUint32(); + if (depth < 1) { + for (let i = 0; i < elementsLength && i < MAX_COLLECTION_VALUES; i++) { + if (reader.peekUint8() == JSVAL_TYPE_MAGIC) { + reader.readUint8(); + continue; + } + ownPropertiesLength++; + const nestedSummary = readValueSummary(reader, depth + 1, shapes); + ownProperties[i] = { + configurable: true, + enumerable: true, + writable: true, + value: nestedSummary, + }; + } + } + } + + preview.ownProperties = ownProperties; + preview.ownPropertiesLength = ownPropertiesLength; + + result.preview = preview; +} + +function readClassFromShape(result, reader, shapes) { + const shapeId = reader.readUint32(); + const shape = shapes[shapeId]; + + if (!shape || shape.length <= 0) { + return; + } + result.class = shape[0]; +} + +function readNodeSummary(result, reader, depth, shapes) { + const preview = {}; + preview.kind = "DOMNode"; + preview.nodeType = reader.readUint16(); + preview.nodeName = reader.readString().toLowerCase(); + const subkindAndIsConnected = reader.readUint8(); + const subkind = subkindAndIsConnected & ~(1 << 7); + preview.isConnected = subkindAndIsConnected >> 7; + + if (subkind == EXTERNAL_NODE_SUBKIND_ELEMENT) { + preview.attributes = {}; + preview.attributesLength = reader.readUint32(); + for ( + let i = 0; + i < preview.attributesLength && i < MAX_COLLECTION_VALUES; + i++ + ) { + const attrName = reader.readString(); + const attrVal = reader.readString(); + preview.attributes[attrName] = attrVal; + } + } else if (subkind == EXTERNAL_NODE_SUBKIND_ATTR) { + preview.value = reader.readString(); + } else if (subkind == EXTERNAL_NODE_SUBKIND_DOCUMENT) { + preview.location = reader.readString(); + } else if (subkind == EXTERNAL_NODE_SUBKIND_DOCUMENT_FRAGMENT) { + preview.childNodesLength = reader.readUint32(); + if (depth < 1) { + preview.childNodes = []; + for ( + let i = 0; + i < preview.childNodesLength && i < MAX_COLLECTION_VALUES; + i++ + ) { + preview.childNodes.push(readValueSummary(reader, depth + 1, shapes)); + } + } + } else if ( + subkind == EXTERNAL_NODE_SUBKIND_TEXT || + subkind == EXTERNAL_NODE_SUBKIND_COMMENT + ) { + preview.textContent = reader.readString(); + } + + result.preview = preview; +} + +function readExceptionSummary(result, reader, _depth, _shapes) { + result.class = "Error"; + const preview = {}; + preview.kind = "DOMException"; + preview.name = reader.readString(); + preview.message = reader.readString(); + preview.code = reader.readUint16(); + preview.result = reader.readUint32(); + preview.lineNumber = reader.readUint32(); + preview.columnNumber = reader.readUint32(); + preview.stack = reader.readString(); + result.preview = preview; +} + +function readExternalObjectSummary(result, reader, depth, shapes) { + readClassFromShape(result, reader, shapes); + + const startIndex = reader.getIndex(); + const size = reader.readUint32(); + try { + const version = reader.readUint8(); + if (version != EXTERNAL_SUMMARY_EXPECTED_VERSION) { + return; + } + + const kind = reader.readUint8(); + if (kind == EXTERNAL_SUMMARY_KIND_OTHER) { + return; + } + + switch (kind) { + case EXTERNAL_SUMMARY_KIND_NODE: { + readNodeSummary(result, reader, depth, shapes); + break; + } + case EXTERNAL_SUMMARY_KIND_EXCEPTION: { + readExceptionSummary(result, reader, depth, shapes); + break; + } + default: + } + } finally { + reader.setIndex(startIndex + size); + } +} + +function readErrorObjectSummary(result, reader, depth, shapes) { + const shapeId = reader.readUint32(); + const shape = shapes[shapeId]; + + if (!shape || shape.length <= 0) { + return; + } + + const preview = {}; + result.type = "object"; + result.class = shape[0]; + result.isError = true; + preview.kind = "Error"; + preview.name = reader.readString(); + preview.message = reader.readString(); + preview.stack = reader.readString(); + preview.fileName = reader.readString(); + preview.lineNumber = reader.readUint32(); + preview.columnNumber = reader.readUint32(); + result.preview = preview; +} + +function readObjectSummary(reader, flags, depth, shapes) { + const result = { + type: "object", + class: undefined, + ownPropertyLength: 0, + isError: false, + extensible: false, + sealed: false, + frozen: false, + }; + + const kind = reader.readUint8(); + switch (kind) { + case OBJECT_KIND_NOT_IMPLEMENTED: + readClassFromShape(result, reader, shapes); + break; + case OBJECT_KIND_ARRAY_LIKE: + readArrayLikeSummary(result, reader, flags, depth, shapes); + break; + case OBJECT_KIND_MAP_LIKE: + readMapLikeSummary(result, reader, flags, depth, shapes); + break; + case OBJECT_KIND_FUNCTION: + readFunctionSummary(result, reader); + break; + case OBJECT_KIND_EXTERNAL: + readExternalObjectSummary(result, reader, depth, shapes); + break; + case OBJECT_KIND_ERROR: + readErrorObjectSummary(result, reader, depth, shapes); + break; + case OBJECT_KIND_WRAPPED_PRIMITIVE_OBJECT: { + result.wrappedValue = readValueSummary(reader, depth, shapes); + readGenericObjectSummary(result, reader, flags, depth, shapes); + break; + } + case OBJECT_KIND_GENERIC_OBJECT: { + readGenericObjectSummary(result, reader, flags, depth, shapes); + break; + } + case OBJECT_KIND_PROXY_OBJECT: { + readClassFromShape(result, reader, shapes); + result.preview = { + kind: "Object", + ownProperties: Object.create(null), + ownPropertiesLength: 0, + }; + break; + } + default: + throw new Error("Bad object kind"); + } + + return result; +} + +function readValueSummary(reader, depth, shapes) { + const header = reader.readUint8(); + const type = header & 0x0f; + const flags = (header & 0xf0) >> 4; + switch (type) { + case JSVAL_TYPE_DOUBLE: + if (flags == NUMBER_IS_OUT_OF_LINE_MAGIC) { + const value = reader.readFloat64(); + if (value === Infinity) { + return { type: "Infinity" }; + } else if (value === -Infinity) { + return { type: "-Infinity" }; + } else if (Number.isNaN(value)) { + return { type: "NaN" }; + } else if (!value && 1 / value === -Infinity) { + return { type: "-0" }; + } + return value; + } + return 0; + + case JSVAL_TYPE_INT32: + if (flags == NUMBER_IS_OUT_OF_LINE_MAGIC) { + return reader.readInt32(); + } + return flags + MIN_INLINE_INT; + + case JSVAL_TYPE_BOOLEAN: + return !!flags; + case JSVAL_TYPE_NULL: + return { type: "null" }; + case JSVAL_TYPE_UNDEFINED: + return { type: "undefined" }; + case JSVAL_TYPE_SYMBOL: + return { + type: "symbol", + name: reader.readString(), + }; + case JSVAL_TYPE_BIGINT: + return { + type: "BigInt", + text: reader.readString(), + }; + case JSVAL_TYPE_STRING: { + return reader.readString(); + } + case JSVAL_TYPE_OBJECT: { + return readObjectSummary(reader, flags, depth, shapes); + } + default: + throw new Error("Bad value type"); + } +} + +function getArgumentSummaries(valuesBuffer, shapes, valuesBufferIndex) { + if (valuesBufferIndex == ZERO_ARGUMENTS_MAGIC) { + return []; + } + if (valuesBufferIndex == EXPIRED_VALUES_MAGIC) { + return "<missing>"; + } + + let reader = new BufferReader(valuesBuffer, valuesBufferIndex); + let argc = reader.readUint32(); + let args = new Array(argc); + for (let i = 0; i < argc && i < MAX_ARGUMENTS_TO_RECORD; i++) { + args[i] = readValueSummary(reader, 0, shapes); + } + return args; +} + +export const ValueSummaryReader = { getArgumentSummaries };