chrome-devtools-frontend
Version:
Chrome DevTools UI
285 lines (244 loc) • 13.1 kB
text/typescript
// Copyright 2023 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.
import {CustomFormatters, type TypeInfo} from '../src/CustomFormatters.js';
import * as Formatters from '../src/Formatters.js';
import {TestValue, TestWasmInterface} from './TestUtils.js';
describe('Formatters', () => {
it('formatChar', () => {
const wasm = new TestWasmInterface();
const chars = [0x0, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0x19, 0x20, 0x7e, 0x7f, 0x21, 0x7d];
const expectation = [
'\\0',
'\\a',
'\\b',
'\\t',
'\\n',
'\\v',
'\\f',
'\\r',
'\\x19',
' ',
'~',
'\\x7f',
'!',
'}',
].map(c => `'${c}'`).join();
assert.deepEqual(chars.map(c => Formatters.formatChar(wasm, TestValue.fromInt8(c))).join(), expectation);
assert.deepEqual(chars.map(c => Formatters.formatChar(wasm, TestValue.fromUint8(c))).join(), expectation);
});
it('formatCStrings', () => {
const wasm = new TestWasmInterface();
// 0x0
const ptr = TestValue.fromUint32(0, 'char *');
assert.deepEqual(Formatters.formatCString(wasm, ptr), {'0x0': null});
assert.deepEqual(Formatters.formatU16CString(wasm, ptr), {'0x0': null});
assert.deepEqual(Formatters.formatCWString(wasm, ptr), {'0x0': null});
// short string
const shortString = 'abcdef\0';
const shortStringValue =
new TestValue(new DataView(new TextEncoder().encode(shortString).buffer as ArrayBuffer), 'char');
assert.deepEqual(
Formatters.formatCString(wasm, TestValue.pointerTo(shortStringValue, Formatters.Constants.SAFE_HEAP_START)),
'abcdef');
const shortContents = new DataView(new ArrayBuffer(shortString.length * Uint32Array.BYTES_PER_ELEMENT));
for (let i = 0; i < shortString.length; ++i) {
shortContents.setUint32(i * Uint32Array.BYTES_PER_ELEMENT, shortString.codePointAt(i) ?? 0, true);
}
const shortWString = new TestValue(shortContents, 'wchar_t');
assert.deepEqual(
Formatters.formatCWString(wasm, TestValue.pointerTo(shortWString, Formatters.Constants.SAFE_HEAP_START)),
'abcdef');
// long string
const longString = `${new Array(Formatters.Constants.PAGE_SIZE / 4).fill('abcdefg').join('')}\0`;
const longStringValue =
new TestValue(new DataView(new TextEncoder().encode(longString).buffer as ArrayBuffer), 'char');
assert.deepEqual(
Formatters.formatCString(wasm, TestValue.pointerTo(longStringValue, Formatters.Constants.SAFE_HEAP_START)),
longString.substr(0, longString.length - 1));
const longContents = new DataView(new ArrayBuffer(longString.length * Uint32Array.BYTES_PER_ELEMENT));
for (let i = 0; i < longString.length; ++i) {
longContents.setUint32(i * Uint32Array.BYTES_PER_ELEMENT, longString.codePointAt(i) ?? 0, true);
}
const longWString = new TestValue(longContents, 'wchar_t');
assert.deepEqual(
Formatters.formatCWString(wasm, TestValue.pointerTo(longWString, Formatters.Constants.SAFE_HEAP_START)),
longString.substr(0, longString.length - 1));
});
it('formatLibCXXString', () => {
const wasm = new TestWasmInterface();
// short string
const shortString = 'abcdefgh';
const shortFlag = TestValue.fromUint8(shortString.length);
const longString = new Array(128 / shortString.length).fill(shortString).join('');
const longFlag = TestValue.fromUint8(0x80);
// eslint-disable-next-line @typescript-eslint/naming-convention
const __s_union = TestValue.fromMembers('__s_union', {});
// eslint-disable-next-line @typescript-eslint/naming-convention
const __s = TestValue.fromMembers('__s', {'<union>': __s_union});
// eslint-disable-next-line @typescript-eslint/naming-convention
const __l = TestValue.fromMembers('__l', {});
const str = TestValue.fromMembers('std::string', {
__r_: TestValue.fromMembers('__r_', {
__value_: TestValue.fromMembers('__value_', {'<union>': TestValue.fromMembers('__value_union', {__s, __l})}),
}),
});
// short char8_t
__s.members.__data_ =
new TestValue(new DataView(new TextEncoder().encode(shortString).buffer as ArrayBuffer), 'char');
__s_union.members.__size_ = shortFlag;
assert.deepEqual(Formatters.formatLibCXX8String(wasm, str), {size: shortString.length, string: shortString});
// long char8_t
const wideStringContents = new DataView(new ArrayBuffer(shortString.length * Uint32Array.BYTES_PER_ELEMENT));
for (let i = 0; i < shortString.length; ++i) {
wideStringContents.setUint32(i * Uint32Array.BYTES_PER_ELEMENT, shortString.codePointAt(i) ?? 0, true);
}
__s.members.__data_ = new TestValue(wideStringContents, 'wchar_t');
__s_union.members.__size_ = shortFlag;
assert.deepEqual(Formatters.formatLibCXX32String(wasm, str), {size: shortString.length, string: shortString});
// long char8_t
wasm.memory = new ArrayBuffer(Formatters.Constants.SAFE_HEAP_START + longString.length);
new Uint8Array(wasm.memory).set(new TextEncoder().encode(longString), Formatters.Constants.SAFE_HEAP_START);
__l.members.__data_ = TestValue.pointerTo(
new TestValue(new DataView(wasm.memory, Formatters.Constants.SAFE_HEAP_START), 'char'),
Formatters.Constants.SAFE_HEAP_START);
__s_union.members.__size_ = longFlag;
__l.members.__size_ = TestValue.fromUint32(longString.length);
assert.deepEqual(Formatters.formatLibCXX8String(wasm, str), {size: longString.length, string: longString});
// long char32_t
wasm.memory =
new ArrayBuffer(Formatters.Constants.SAFE_HEAP_START + longString.length * Uint32Array.BYTES_PER_ELEMENT);
const longWideStringContents = new DataView(wasm.memory, Formatters.Constants.SAFE_HEAP_START);
for (let i = 0; i < longString.length; ++i) {
longWideStringContents.setUint32(i * Uint32Array.BYTES_PER_ELEMENT, longString.codePointAt(i) ?? 0, true);
}
__l.members.__data_ =
TestValue.pointerTo(new TestValue(longWideStringContents, 'wchar_t'), Formatters.Constants.SAFE_HEAP_START);
__s_union.members.__size_ = longFlag;
__l.members.__size_ = TestValue.fromUint32(longString.length);
assert.deepEqual(Formatters.formatLibCXX32String(wasm, str), {size: longString.length, string: longString});
});
it('formatVector', () => {
const wasm = new TestWasmInterface();
const elements = [1, 2, 3, 4, 5, 6, 7, 8, 9].map(v => TestValue.fromFloat32(v));
const __begin_ = TestValue.pointerTo(elements, 0x1234); // eslint-disable-line @typescript-eslint/naming-convention
const __end_ = // eslint-disable-line @typescript-eslint/naming-convention
TestValue.pointerTo(elements[elements.length - 1], 0x1234 + Float32Array.BYTES_PER_ELEMENT * elements.length);
const vector = new TestValue(new DataView(new ArrayBuffer(0)), 'std::vector<float>', {__begin_, __end_});
assert.deepEqual(Formatters.formatVector(wasm, vector), elements);
});
it('formatPointerOrReference', () => {
const wasm = new TestWasmInterface();
assert.deepEqual(Formatters.formatPointerOrReference(wasm, TestValue.fromUint32(0)), {'0x0': null});
const pointee = TestValue.fromFloat64(15);
assert.deepEqual(
Formatters.formatPointerOrReference(wasm, TestValue.pointerTo(pointee, 0x1234)), {'0x1234': pointee});
});
it('formatDynamicArray', () => {
const wasm = new TestWasmInterface();
const element = TestValue.fromFloat32(5);
const array = new TestValue(element.asDataView(), 'float[]', {0: element});
array.location = 0x1234;
assert.deepEqual(Formatters.formatDynamicArray(wasm, array), {'0x1234': element});
});
it('formatUint128', () => {
const wasm = new TestWasmInterface();
const high = 0xdeadn;
const low = 0xbeefbeefbeefbeefn;
const content = new DataView(new BigUint64Array([low, high]).buffer);
const actual = Formatters.formatUInt128(wasm, new TestValue(content, 'uint128_t'));
const expected = 0xdeadbeefbeefbeefbeefn;
assert.deepEqual(actual, expected, `expected 0x${actual.toString(16)} to equal 0x${expected.toString(16)}`);
});
it('formatInt128', () => {
const wasm = new TestWasmInterface();
const expected = -0xdeadbeefbeefbeefbeefn;
const high = expected >> 64n;
const low = expected & 0xffffffffffffffffn;
const content = new DataView(new BigInt64Array([low, high]).buffer);
const actual = Formatters.formatInt128(wasm, new TestValue(content, 'int128_t'));
assert.deepEqual(actual, expected, `expected 0x${actual.toString(16)} to equal 0x${expected.toString(16)}`);
});
it('formatVoid', async () => {
const actual = await Formatters.formatVoid()().asRemoteObject();
assert.deepEqual(actual, {
type: 'undefined',
value: undefined,
description: '<void>',
hasChildren: false,
linearMemorySize: undefined,
linearMemoryAddress: undefined,
});
});
});
describe('CustomFormatters', () => {
for (const makeConst of [true, false]) {
it(`looks up formatters correctly ${makeConst ? 'const' : 'non-const'} type`, () => {
const type = (typeName: string, typeProperties: Partial<TypeInfo> = {}): TypeInfo => {
if (makeConst) {
typeName = `const ${typeName}`;
}
return {
typeNames: [typeName],
typeId: typeName,
members: [],
alignment: 0,
arraySize: 0,
size: 0,
isPointer: Boolean(typeName.match(/^.+\*$/) || typeName.match(/^.+&$/)),
canExpand: false,
hasValue: false,
...typeProperties,
};
};
assert.deepEqual(CustomFormatters.get(type('std::__2::string'))?.format, Formatters.formatLibCXX8String);
assert.deepEqual(
CustomFormatters
.get(type('std::__2::basic_string<char, std::__2::char_traits<char>, std::__2::allocator<char> >'))
?.format,
Formatters.formatLibCXX8String);
assert.deepEqual(CustomFormatters.get(type('std::__2::u8string'))?.format, Formatters.formatLibCXX8String);
assert.deepEqual(
CustomFormatters
.get(type(
'std::__2::basic_string<char8_t, std::__2::char_traits<char8_t>, std::__2::allocator<char8_t> >'))
?.format,
Formatters.formatLibCXX8String);
assert.deepEqual(CustomFormatters.get(type('std::__2::u16string'))?.format, Formatters.formatLibCXX16String);
assert.deepEqual(
CustomFormatters
.get(type(
'std::__2::basic_string<char16_t, std::__2::char_traits<char16_t>, std::__2::allocator<char16_t> >'))
?.format,
Formatters.formatLibCXX16String);
assert.deepEqual(CustomFormatters.get(type('std::__2::wstring'))?.format, Formatters.formatLibCXX32String);
assert.deepEqual(
CustomFormatters
.get(type(
'std::__2::basic_string<wchar_t, std::__2::char_traits<wchar_t>, std::__2::allocator<wchar_t> >'))
?.format,
Formatters.formatLibCXX32String);
assert.deepEqual(CustomFormatters.get(type('std::__2::u32string'))?.format, Formatters.formatLibCXX32String);
assert.deepEqual(
CustomFormatters
.get(type(
'std::__2::basic_string<char32_t, std::__2::char_traits<char32_t>, std::__2::allocator<char32_t> >'))
?.format,
Formatters.formatLibCXX32String);
assert.deepEqual(CustomFormatters.get(type('char *'))?.format, Formatters.formatCString);
assert.deepEqual(CustomFormatters.get(type('char8_t *'))?.format, Formatters.formatCString);
assert.deepEqual(CustomFormatters.get(type('char16_t *'))?.format, Formatters.formatU16CString);
assert.deepEqual(CustomFormatters.get(type('wchar_t *'))?.format, Formatters.formatCWString);
assert.deepEqual(CustomFormatters.get(type('char32_t *'))?.format, Formatters.formatCWString);
assert.deepEqual(
CustomFormatters.get(type('int (*)()', {isPointer: true}))?.format, Formatters.formatPointerOrReference);
assert.deepEqual(CustomFormatters.get(type('std::vector<int>'))?.format, Formatters.formatVector);
assert.deepEqual(CustomFormatters.get(type('std::vector<const float>'))?.format, Formatters.formatVector);
assert.deepEqual(CustomFormatters.get(type('int *'))?.format, Formatters.formatPointerOrReference);
assert.deepEqual(CustomFormatters.get(type('int &'))?.format, Formatters.formatPointerOrReference);
assert.deepEqual(CustomFormatters.get(type('int[]'))?.format, Formatters.formatDynamicArray);
assert.deepEqual(CustomFormatters.get(type('unsigned __int128'))?.format, Formatters.formatUInt128);
assert.deepEqual(CustomFormatters.get(type('__int128'))?.format, Formatters.formatInt128);
});
}
});