UNPKG

chrome-devtools-frontend

Version:
285 lines (244 loc) • 13.1 kB
// 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); }); } });