From af590b0c747e096d7a7856e88380307d81142f84 Mon Sep 17 00:00:00 2001 From: Charles Dick Date: Wed, 15 Mar 2017 06:31:38 -0700 Subject: [PATCH] remove the old heap profiler visualization code Differential Revision: D4650983 fbshipit-source-id: 1f791acdd3e2d96e7881ea037045fafa2c6d781a --- .../server/middleware/heapCapture/.gitignore | 3 - .../server/middleware/heapCapture/Makefile | 8 - .../middleware/heapCapture/heapCapture.html | 12 - .../middleware/heapCapture/package.json | 21 - .../middleware/heapCapture/src/Aggrow.js | 190 ----- .../middleware/heapCapture/src/AggrowData.js | 216 ------ .../heapCapture/src/AggrowExpander.js | 694 ------------------ .../heapCapture/src/AggrowTable.jsx | 439 ----------- .../heapCapture/src/DataColumnSelector.jsx | 42 -- .../middleware/heapCapture/src/Draggable.jsx | 26 - .../middleware/heapCapture/src/DropTarget.jsx | 33 - .../heapCapture/src/ExpanderConfiguration.jsx | 29 - .../heapCapture/src/StackExpanderCreator.jsx | 152 ---- .../heapCapture/src/StackRegistry.js | 96 --- .../heapCapture/src/StringInterner.js | 26 - .../heapCapture/src/TableConfiguration.jsx | 86 --- .../heapCapture/src/TableHeader.jsx | 103 --- .../middleware/heapCapture/src/heapCapture.js | 412 ----------- .../middleware/heapCapture/src/index.js | 24 - .../middleware/heapCapture/webpack.config.js | 29 - .../middleware/heapCaptureMiddleware.js | 171 ----- local-cli/server/runServer.js | 2 - 22 files changed, 2814 deletions(-) delete mode 100644 local-cli/server/middleware/heapCapture/.gitignore delete mode 100644 local-cli/server/middleware/heapCapture/Makefile delete mode 100644 local-cli/server/middleware/heapCapture/heapCapture.html delete mode 100644 local-cli/server/middleware/heapCapture/package.json delete mode 100644 local-cli/server/middleware/heapCapture/src/Aggrow.js delete mode 100644 local-cli/server/middleware/heapCapture/src/AggrowData.js delete mode 100644 local-cli/server/middleware/heapCapture/src/AggrowExpander.js delete mode 100644 local-cli/server/middleware/heapCapture/src/AggrowTable.jsx delete mode 100644 local-cli/server/middleware/heapCapture/src/DataColumnSelector.jsx delete mode 100644 local-cli/server/middleware/heapCapture/src/Draggable.jsx delete mode 100644 local-cli/server/middleware/heapCapture/src/DropTarget.jsx delete mode 100644 local-cli/server/middleware/heapCapture/src/ExpanderConfiguration.jsx delete mode 100644 local-cli/server/middleware/heapCapture/src/StackExpanderCreator.jsx delete mode 100644 local-cli/server/middleware/heapCapture/src/StackRegistry.js delete mode 100644 local-cli/server/middleware/heapCapture/src/StringInterner.js delete mode 100644 local-cli/server/middleware/heapCapture/src/TableConfiguration.jsx delete mode 100644 local-cli/server/middleware/heapCapture/src/TableHeader.jsx delete mode 100644 local-cli/server/middleware/heapCapture/src/heapCapture.js delete mode 100644 local-cli/server/middleware/heapCapture/src/index.js delete mode 100644 local-cli/server/middleware/heapCapture/webpack.config.js delete mode 100644 local-cli/server/middleware/heapCaptureMiddleware.js diff --git a/local-cli/server/middleware/heapCapture/.gitignore b/local-cli/server/middleware/heapCapture/.gitignore deleted file mode 100644 index 99ddc99eb..000000000 --- a/local-cli/server/middleware/heapCapture/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/captures/* -preLoadedCapture.js -bundle.js diff --git a/local-cli/server/middleware/heapCapture/Makefile b/local-cli/server/middleware/heapCapture/Makefile deleted file mode 100644 index c30834163..000000000 --- a/local-cli/server/middleware/heapCapture/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -SHELL := /bin/bash - -all: - NODE_PATH="../../../../node_modules/" babel --presets babel-preset-react-native --source-maps inline -d out src - for f in out/*.js; do echo -e "\n// @generated" >> $$f; done - -watch: - NODE_PATH="../../../../node_modules/" babel --watch --presets babel-preset-react-native -d out src diff --git a/local-cli/server/middleware/heapCapture/heapCapture.html b/local-cli/server/middleware/heapCapture/heapCapture.html deleted file mode 100644 index 2faf4387f..000000000 --- a/local-cli/server/middleware/heapCapture/heapCapture.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - JSC Heap Capture - - - Loading... This could take a while depending on how big the profile is. Check devtools console for errors. - - - - diff --git a/local-cli/server/middleware/heapCapture/package.json b/local-cli/server/middleware/heapCapture/package.json deleted file mode 100644 index 1533027d7..000000000 --- a/local-cli/server/middleware/heapCapture/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "jsc-heap-capture", - "version": "1.0.0", - "description": "processes captured heaps from javascript core", - "main": "bundle.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "build": "webpack" - }, - "author": "cwdick", - "devDependencies": { - "babel-core": "^6.17.0", - "babel-loader": "^6.2.5", - "babel-plugin-transform-class-properties": "^6.16.0", - "babel-preset-es2015": "^6.16.0", - "babel-preset-react": "^6.16.0", - "react": "^0.14.1", - "react-dom": "^0.14.1", - "webpack": "^1.13.2" - } -} diff --git a/local-cli/server/middleware/heapCapture/src/Aggrow.js b/local-cli/server/middleware/heapCapture/src/Aggrow.js deleted file mode 100644 index 198bc9bd3..000000000 --- a/local-cli/server/middleware/heapCapture/src/Aggrow.js +++ /dev/null @@ -1,190 +0,0 @@ -// @flow - -import invariant from 'invariant'; - -import AggrowData, { - AggrowDoubleColumn, - AggrowIntColumn, - AggrowStackColumn, - AggrowStringColumn } from './AggrowData'; -import AggrowExpander from './AggrowExpander'; -import type { FlattenedStack } from './StackRegistry'; -import StackRegistry from './StackRegistry'; - -export type FocusConfig = { - pattern: RegExp, - firstMatch: boolean, - leftSide: boolean, -} -type FocusPredicate = (frameId: number) => boolean; - -export default class Aggrow { - data: AggrowData; - expander: AggrowExpander; - - constructor(aggrowData: AggrowData) { - aggrowData.flattenStacks(); - this.data = aggrowData; - this.expander = new AggrowExpander(aggrowData.rowCount); - } - - addSumAggregator(aggregatorName: string, columnName: string): number { - const column = this.data.getColumn(columnName); - - invariant(column, `Column ${columnName} does not exist.`); - invariant(column instanceof AggrowIntColumn || column instanceof AggrowDoubleColumn, - `Sum aggregator does not support ${column.constructor.name} columns!`); - return this.expander.addAggregator( - aggregatorName, - (indices: Int32Array): number => { - let size = 0; - indices.forEach((i: number) => { size += column.get(i); }); - return size; - }, - (value: any): string => value.toLocaleString(), - (a: number, b: number): number => b - a, - ); - } - - addCountAggregator(aggregatorName: string): number { - return this.expander.addAggregator( - aggregatorName, - (indices: Int32Array): number => indices.length, - (value: any): string => value.toLocaleString(), - (a: number, b: number): number => b - a, - ); - } - - addStringExpander(expanderName: string, columnName: string): number { - const column = this.data.getColumn(columnName); - invariant(column, `Column ${columnName} does not exist.`); - invariant(column instanceof AggrowStringColumn, 'String expander needs a string column.'); - const strings = column.strings; - return this.expander.addFieldExpander( - expanderName, - (rowA: number, rowB: number): number => column.get(rowA) - column.get(rowB), - (row: number): string => strings.get(column.get(row)), - (s: string): string => s, - ); - } - - addNumberExpander(expanderName: string, columnName: string): number { - const column = this.data.getColumn(columnName); - invariant(column, `Column ${columnName} does not exist.`); - invariant( - column instanceof AggrowIntColumn || column instanceof AggrowDoubleColumn, - `Number expander does not support ${column.constructor.name} columns.`); - return this.expander.addFieldExpander( - expanderName, - (rowA: number, rowB: number): number => column.get(rowA) - column.get(rowB), - (row: number): number => column.get(row), - (n: any): string => n.toLocaleString(), - ); - } - - addPointerExpander(expanderName: string, columnName: string): number { - const column = this.data.getColumn(columnName); - invariant(column, `Column ${columnName} does not exist.`); - invariant( - column instanceof AggrowIntColumn, - `Pointer expander does not support ${column.constructor.name} columns.`); - return this.expander.addFieldExpander( - expanderName, - (rowA: number, rowB: number): number => column.get(rowA) - column.get(rowB), - (row: number): number => column.get(row), - (p: number): string => `0x${(p >>> 0).toString(16)}`, // eslint-disable-line no-bitwise - ); - } - - addStackExpander( - expanderName: string, - columnName: string, - reverse: boolean, - focus: ?FocusConfig): number { - const column = this.data.getColumn(columnName); - invariant(column, `Column ${columnName} does not exist.`); - invariant( - column instanceof AggrowStackColumn, - `Stack expander does not support ${column.constructor.name} columns.`); - let stacks = column.stacks; - const getter = column.getter; - const formatter = column.formatter; - if (focus) { - const re = focus.pattern; - const predicate = (frameId: number): boolean => re.test(formatter(getter(frameId))); - stacks = focusStacks(stacks, predicate, focus.firstMatch, focus.leftSide); - } - return this.expander.addStackExpander( - expanderName, - stacks.maxDepth, - (row: number): FlattenedStack => stacks.get(column.get(row)), - getter, - formatter, - !!reverse, - ); - } -} - -function focusStacks( - stacks: StackRegistry, - predicate: FocusPredicate, - firstMatch: boolean, - leftSide: boolean): FocusedStackRegistry { - let stackMapper; - if (firstMatch && leftSide) { - stackMapper = (stack: FlattenedStack): FlattenedStack => { - for (let i = 0; i < stack.length; i++) { - if (predicate(stack[i])) { - return stack.subarray(0, i + 1); - } - } - return stack.subarray(0, 0); - }; - } else if (firstMatch && !leftSide) { - stackMapper = (stack: FlattenedStack): FlattenedStack => { - for (let i = 0; i < stack.length; i++) { - if (predicate(stack[i])) { - return stack.subarray(i, stack.length); - } - } - return stack.subarray(0, 0); - }; - } else if (!firstMatch && leftSide) { - stackMapper = (stack: FlattenedStack): FlattenedStack => { - for (let i = stack.length - 1; i >= 0; i--) { - if (predicate(stack[i])) { - return stack.subarray(0, i + 1); - } - } - return stack.subarray(0, 0); - }; - } else { // !firstMatch && !leftSide - stackMapper = (stack: FlattenedStack): FlattenedStack => { - for (let i = stack.length - 1; i >= 0; i--) { - if (predicate(stack[i])) { - return stack.subarray(i, stack.length); - } - } - return stack.subarray(0, 0); - }; - } - - invariant(stacks.stackIdMap, 'Stacks were not flattened.'); - return new FocusedStackRegistry( - stacks.stackIdMap.map(stackMapper), - stacks.maxDepth); -} - -class FocusedStackRegistry { - maxDepth: number; - stackIdMap: Array; - - constructor(stackIdMap: Array, maxDepth: number) { - this.maxDepth = maxDepth; - this.stackIdMap = stackIdMap; - } - - get(id: number): FlattenedStack { - return this.stackIdMap[id]; - } -} diff --git a/local-cli/server/middleware/heapCapture/src/AggrowData.js b/local-cli/server/middleware/heapCapture/src/AggrowData.js deleted file mode 100644 index 856f1ea86..000000000 --- a/local-cli/server/middleware/heapCapture/src/AggrowData.js +++ /dev/null @@ -1,216 +0,0 @@ -// @flow - -import invariant from 'invariant'; - -import type { FrameGetter, FrameFormatter } from './AggrowExpander'; -import type { Stack } from './StackRegistry'; -import StackRegistry from './StackRegistry'; -import StringInterner from './StringInterner'; - -export type AggrowColumnDef = - AggrowStringColumnDef | - AggrowIntColumnDef | - AggrowDoubleColumnDef | - AggrowStackColumnDef; - -type AggrowStringColumnDef = { - type: 'string'; - name: string; - strings: StringInterner; -} - -type AggrowIntColumnDef = { - type: 'int'; - name: string; -} - -type AggrowDoubleColumnDef = { - type: 'double'; - name: string; -} - -type AggrowStackColumnDef = { - type: 'stack'; - name: string; - stacks: StackRegistry, - getter: FrameGetter, - formatter: FrameFormatter, -} - -export interface AggrowColumn { - name: string; - get(row: number): number; - insert(row: number, s: any): void; - extend(count: number): void; -} - -class AggrowColumnBase { - name: string; - - constructor(def: AggrowColumnDef) { - this.name = def.name; - } -} - -export class AggrowStringColumn extends AggrowColumnBase { - strings: StringInterner; - data: Int32Array = new Int32Array(0); - - constructor(def: AggrowStringColumnDef) { - super(def); - this.strings = def.strings; - } - - get(row: number): number { - return this.data[row]; - } - - insert(row: number, s: string) { - this.data[row] = this.strings.intern(s); - } - - extend(count: number) { - const newData = new Int32Array(this.data.length + count); - newData.set(this.data); - this.data = newData; - } -} - -export class AggrowIntColumn extends AggrowColumnBase { - data: Int32Array = new Int32Array(0); - - get(row: number): number { - return this.data[row]; - } - - insert(row: number, i: number) { - this.data[row] = i; - } - - extend(count: number) { - const newData = new Int32Array(this.data.length + count); - newData.set(this.data); - this.data = newData; - } -} - -export class AggrowDoubleColumn extends AggrowColumnBase { - data: Float64Array = new Float64Array(0); - - get(row: number): number { - return this.data[row]; - } - - insert(row: number, d: number) { - this.data[row] = d; - } - - extend(count: number) { - const newData = new Float64Array(this.data.length + count); - newData.set(this.data); - this.data = newData; - } -} - -export class AggrowStackColumn extends AggrowColumnBase { - data: Int32Array = new Int32Array(0); - stacks: StackRegistry; - getter: FrameGetter; - formatter: FrameFormatter; - - constructor(def: AggrowStackColumnDef) { - super(def); - this.stacks = def.stacks; - this.getter = def.getter; - this.formatter = def.formatter; - } - - get(row: number): number { - return this.data[row]; - } - - insert(row: number, s: Stack) { - this.data[row] = s.id; - } - - extend(count: number) { - const newData = new Int32Array(this.data.length + count); - newData.set(this.data); - this.data = newData; - } -} - -function newColumn(def: AggrowColumnDef): AggrowColumn { - switch (def.type) { - case 'string': - return new AggrowStringColumn(def); - case 'int': - return new AggrowIntColumn(def); - case 'double': - return new AggrowDoubleColumn(def); - case 'stack': - return new AggrowStackColumn(def); - default: - throw new Error(`Unknown column type: ${def.type}`); - } -} - -export default class AggrowData { - columns: Array; - rowCount = 0; - - constructor(columnDefs: Array) { - this.columns = columnDefs.map(newColumn); - } - - rowInserter(numRows: number): RowInserter { - const columns = this.columns; - columns.forEach((c: AggrowColumn): void => c.extend(numRows)); - const currRow = this.rowCount; - const endRow = currRow + numRows; - this.rowCount = endRow; - - return new RowInserter(columns, { currRow, endRow }); - } - - getColumn(name: string): ?AggrowColumn { - return this.columns.find((c: AggrowColumn): boolean => c.name === name); - } - - flattenStacks() { - this.columns.forEach((c: AggrowColumn) => { - if (c instanceof AggrowStackColumn) { - c.stacks.flatten(); - } - }); - } -} - -class RowInserter { - columns: Array; - currRow: number; - endRow: number; - - constructor( - columns: Array, - params: { currRow: number, endRow: number }) { - this.columns = columns; - this.currRow = params.currRow; - this.endRow = params.endRow; - } - - insertRow(...args: Array) { - invariant(this.currRow < this.endRow, 'Tried to insert data off end of added range!'); - invariant( - args.length === this.columns.length, - `Expected data for ${this.columns.length} columns, got ${args.length} columns`); - - args.forEach((arg: number | string | Stack, i: number): void => - this.columns[i].insert(this.currRow, arg)); - this.currRow += 1; - } - - done() { - invariant(this.currRow === this.endRow, 'Unfilled rows!'); - } -} diff --git a/local-cli/server/middleware/heapCapture/src/AggrowExpander.js b/local-cli/server/middleware/heapCapture/src/AggrowExpander.js deleted file mode 100644 index 522290198..000000000 --- a/local-cli/server/middleware/heapCapture/src/AggrowExpander.js +++ /dev/null @@ -1,694 +0,0 @@ -// @flow -import invariant from 'invariant'; - -import type { FlattenedStack } from './StackRegistry'; - -// expander ID definitions -const FIELD_EXPANDER_ID_MIN = 0x0000; -const FIELD_EXPANDER_ID_MAX = 0x7fff; -const STACK_EXPANDER_ID_MIN = 0x8000; -const STACK_EXPANDER_ID_MAX = 0xffff; - -// used for row.expander which reference state.activeExpanders (with frame index masked in) -const INVALID_ACTIVE_EXPANDER = -1; -const ACTIVE_EXPANDER_MASK = 0xffff; -const ACTIVE_EXPANDER_FRAME_SHIFT = 16; - -// aggregator ID definitions -const AGGREGATOR_ID_MAX = 0xffff; - -// active aggragators can have sort order changed in the reference -const ACTIVE_AGGREGATOR_MASK = 0xffff; -const ACTIVE_AGGREGATOR_ASC_BIT = 0x10000; - -// tree node state definitions -const NODE_EXPANDED_BIT = 0x0001; // this row is expanded -const NODE_REAGGREGATE_BIT = 0x0002; // children need aggregates -const NODE_REORDER_BIT = 0x0004; // children need to be sorted -const NODE_REPOSITION_BIT = 0x0008; // children need position -const NODE_INDENT_SHIFT = 16; - -function _calleeFrameIdGetter(stack: FlattenedStack, depth: number): number { - return stack[depth]; -} - -function _callerFrameIdGetter(stack: FlattenedStack, depth: number): number { - return stack[stack.length - depth - 1]; -} - -function _createStackComparers( - stackGetter: StackGetter, - frameIdGetter: FrameIdGetter, - maxStackDepth: number): Array> { - const comparers = new Array(maxStackDepth); - for (let depth = 0; depth < maxStackDepth; depth++) { - const captureDepth = depth; // NB: to capture depth per loop iteration - comparers[depth] = function calleeStackComparer(rowA: number, rowB: number): number { - const a = stackGetter(rowA); - const b = stackGetter(rowB); - // NB: we put the stacks that are too short at the top, - // so they can be grouped into the '' bucket - if (a.length <= captureDepth && b.length <= captureDepth) { - return 0; - } else if (a.length <= captureDepth) { - return -1; - } else if (b.length <= captureDepth) { - return 1; - } - return frameIdGetter(a, captureDepth) - frameIdGetter(b, captureDepth); - }; - } - return comparers; -} - -function _createTreeNode( - parent: Row | null, - label: string, - indices: Int32Array, - expander: number): Row { - const indent = parent === null ? 0 : (parent.state >>> NODE_INDENT_SHIFT) + 1; // eslint-disable-line no-bitwise, max-len - const state = NODE_REPOSITION_BIT | // eslint-disable-line no-bitwise - NODE_REAGGREGATE_BIT | - NODE_REORDER_BIT | - (indent << NODE_INDENT_SHIFT); // eslint-disable-line no-bitwise - return { - parent, // null if root - children: null, // array of children nodes - label, // string to show in UI - indices, // row indices under this node - aggregates: null, // result of aggregate on indices - expander, // index into state.activeExpanders - top: 0, // y position of top row (in rows) - height: 1, // number of rows including children - state, // see NODE_* definitions above - }; -} - -const NO_SORT_ORDER: Comparer<*> = (): number => 0; - -type Comparer = (a: T, b: T) => number; - -type Aggregator = { - name: string, // name for column - aggregator: (indexes: Int32Array) => number, // index array -> aggregate value - formatter: (value: number) => string, // aggregate value -> display string - sorter: Comparer, // compare two aggregate values -} - -type FieldExpander = { - name: string, - comparer: Comparer, - getter: (rowIndex: number) => any, - formatter: (value: any) => string, -} - -type StackGetter = (rowIndex: number) => FlattenedStack; // (row) => [frameId int] -type FrameIdGetter = (stack: FlattenedStack, depth: number) => number; // (stack,depth) -> frame id -export type FrameGetter = (id: number) => any; // (frameId int) => frame obj -export type FrameFormatter = (frame: any) => string; // (frame obj) => display string - -type StackExpander = { - name: string, // display name of expander - comparers: Array>, // depth -> comparer - stackGetter: StackGetter, - frameIdGetter: FrameIdGetter, - frameGetter: FrameGetter, - frameFormatter: FrameFormatter, -} - -export type Row = { - top: number, - height: number, - state: number, - parent: Row | null, - indices: Int32Array, - aggregates: Array | null, - children: Array | null, - expander: number, - label: string, -} - -type State = { - fieldExpanders: Array, // tree expanders that expand on simple values - stackExpanders: Array, // tree expanders that expand stacks - activeExpanders: Array, // index into field or stack expanders, hierarchy of tree - aggregators: Array, // all available aggregators, might not be used - activeAggregators: Array, // index into aggregators, to actually compute - sorter: Comparer<*>, - root: Row, -} - -export default class AggrowExpander { // eslint-disable-line no-unused-vars - indices: Int32Array; - state: State; - - constructor(numRows: number) { - this.indices = new Int32Array(numRows); - for (let i = 0; i < numRows; i++) { - this.indices[i] = i; - } - - this.state = { - fieldExpanders: [], - stackExpanders: [], - activeExpanders: [], - aggregators: [], - activeAggregators: [], - sorter: NO_SORT_ORDER, - root: _createTreeNode(null, '', this.indices, INVALID_ACTIVE_EXPANDER), - }; - } - - _evaluateAggregate(row: Row) { - const activeAggregators = this.state.activeAggregators; - const aggregates = new Array(activeAggregators.length); - for (let j = 0; j < activeAggregators.length; j++) { - const aggregator = this.state.aggregators[activeAggregators[j]]; - aggregates[j] = aggregator.aggregator(row.indices); - } - row.aggregates = aggregates; // eslint-disable-line no-param-reassign - row.state |= NODE_REAGGREGATE_BIT; // eslint-disable-line no-bitwise, no-param-reassign - } - - _evaluateAggregates(row: Row) { - if ((row.state & NODE_EXPANDED_BIT) !== 0) { // eslint-disable-line no-bitwise - const children = row.children; - invariant(children, 'Expected non-null children'); - for (let i = 0; i < children.length; i++) { - this._evaluateAggregate(children[i]); - } - row.state |= NODE_REORDER_BIT; // eslint-disable-line no-bitwise, no-param-reassign - } - row.state ^= NODE_REAGGREGATE_BIT; // eslint-disable-line no-bitwise, no-param-reassign - } - - _evaluateOrder(row: Row) { - if ((row.state & NODE_EXPANDED_BIT) !== 0) { // eslint-disable-line no-bitwise - const children = row.children; - invariant(children, 'Expected non-null children'); - for (let i = 0; i < children.length; i++) { - const child = children[i]; - child.state |= NODE_REORDER_BIT; // eslint-disable-line no-bitwise - } - children.sort(this.state.sorter); - row.state |= NODE_REPOSITION_BIT; // eslint-disable-line no-bitwise, no-param-reassign - } - row.state ^= NODE_REORDER_BIT; // eslint-disable-line no-bitwise, no-param-reassign - } - - _evaluatePosition(row: Row) { // eslint-disable-line class-methods-use-this - if ((row.state & NODE_EXPANDED_BIT) !== 0) { // eslint-disable-line no-bitwise - const children = row.children; - invariant(children, 'Expected a children array'); - let childTop = row.top + 1; - for (let i = 0; i < children.length; i++) { - const child = children[i]; - if (child.top !== childTop) { - child.top = childTop; - child.state |= NODE_REPOSITION_BIT; // eslint-disable-line no-bitwise - } - childTop += child.height; - } - } - row.state ^= NODE_REPOSITION_BIT; // eslint-disable-line no-bitwise, no-param-reassign - } - - _getRowsImpl(row: Row, top: number, height: number, result: Array) { - if ((row.state & NODE_REAGGREGATE_BIT) !== 0) { // eslint-disable-line no-bitwise - this._evaluateAggregates(row); - } - if ((row.state & NODE_REORDER_BIT) !== 0) { // eslint-disable-line no-bitwise - this._evaluateOrder(row); - } - if ((row.state & NODE_REPOSITION_BIT) !== 0) { // eslint-disable-line no-bitwise - this._evaluatePosition(row); - } - - if (row.top >= top && row.top < top + height) { - invariant( - result[row.top - top] === null, - `getRows put more than one row at position ${row.top} into result`); - result[row.top - top] = row; // eslint-disable-line no-param-reassign - } - if ((row.state & NODE_EXPANDED_BIT) !== 0) { // eslint-disable-line no-bitwise - const children = row.children; - invariant(children, 'Expected non-null children'); - for (let i = 0; i < children.length; i++) { - const child = children[i]; - if (child.top < top + height && top < child.top + child.height) { - this._getRowsImpl(child, top, height, result); - } - } - } - } - - _updateHeight(row: Row | null, heightChange: number) { // eslint-disable-line class-methods-use-this, max-len - while (row !== null) { - row.height += heightChange; // eslint-disable-line no-param-reassign - row.state |= NODE_REPOSITION_BIT; // eslint-disable-line no-bitwise, no-param-reassign - row = row.parent; // eslint-disable-line no-param-reassign - } - } - - _addChildrenWithFieldExpander(row: Row, expander: FieldExpander, nextActiveIndex: number) { // eslint-disable-line class-methods-use-this, max-len - const rowIndices = row.indices; - const comparer = expander.comparer; - const formatter = expander.formatter; - const getter = expander.getter; - rowIndices.sort(comparer); - let begin = 0; - let end = 1; - row.children = []; // eslint-disable-line no-param-reassign - while (end < rowIndices.length) { - if (comparer(rowIndices[begin], rowIndices[end]) !== 0) { - invariant(row.children, 'Expected a children array'); - row.children.push(_createTreeNode( - row, - `${expander.name}: ${formatter(getter(rowIndices[begin]))}`, - rowIndices.subarray(begin, end), - nextActiveIndex)); - begin = end; - } - end += 1; - } - row.children.push(_createTreeNode( - row, - `${expander.name}: ${formatter(getter(rowIndices[begin]))}`, - rowIndices.subarray(begin, end), - nextActiveIndex)); - } - - _addChildrenWithStackExpander( // eslint-disable-line class-methods-use-this - row: Row, - expander: StackExpander, - activeIndex: number, - depth: number, - nextActiveIndex: number) { - const rowIndices = row.indices; - const stackGetter = expander.stackGetter; - const frameIdGetter = expander.frameIdGetter; - const frameGetter = expander.frameGetter; - const frameFormatter = expander.frameFormatter; - const comparer = expander.comparers[depth]; - const expandNextFrame = activeIndex | ((depth + 1) << ACTIVE_EXPANDER_FRAME_SHIFT); // eslint-disable-line no-bitwise, max-len - rowIndices.sort(comparer); - let columnName = ''; - if (depth === 0) { - columnName = `${expander.name}: `; - } - - // put all the too-short stacks under - let begin = 0; - let beginStack = null; - row.children = []; // eslint-disable-line no-param-reassign - while (begin < rowIndices.length) { - beginStack = stackGetter(rowIndices[begin]); - if (beginStack.length > depth) { - break; - } - begin += 1; - } - invariant(beginStack !== null, 'Expected beginStack at this point'); - if (begin > 0) { - row.children.push(_createTreeNode( - row, - `${columnName}`, - rowIndices.subarray(0, begin), - nextActiveIndex)); - } - // aggregate the rest under frames - if (begin < rowIndices.length) { - let end = begin + 1; - while (end < rowIndices.length) { - const endStack = stackGetter(rowIndices[end]); - if (frameIdGetter(beginStack, depth) !== frameIdGetter(endStack, depth)) { - invariant(row.children, 'Expected a children array'); - row.children.push(_createTreeNode( - row, - columnName + frameFormatter(frameGetter(frameIdGetter(beginStack, depth))), - rowIndices.subarray(begin, end), - expandNextFrame)); - begin = end; - beginStack = endStack; - } - end += 1; - } - row.children.push(_createTreeNode( - row, - columnName + frameFormatter(frameGetter(frameIdGetter(beginStack, depth))), - rowIndices.subarray(begin, end), - expandNextFrame)); - } - } - - _contractRow(row: Row) { - invariant( - (row.state & NODE_EXPANDED_BIT) !== 0, // eslint-disable-line no-bitwise - 'Cannot contract row; already contracted!'); - row.state ^= NODE_EXPANDED_BIT; // eslint-disable-line no-bitwise, no-param-reassign - const heightChange = 1 - row.height; - this._updateHeight(row, heightChange); - } - - _pruneExpanders(row: Row, oldExpander: number, newExpander: number) { - row.state |= NODE_REPOSITION_BIT; // eslint-disable-line no-bitwise, no-param-reassign - if (row.expander === oldExpander) { - row.state |= NODE_REAGGREGATE_BIT | NODE_REORDER_BIT | NODE_REPOSITION_BIT; // eslint-disable-line no-bitwise, no-param-reassign, max-len - if ((row.state & NODE_EXPANDED_BIT) !== 0) { // eslint-disable-line no-bitwise - this._contractRow(row); - } - row.children = null; // eslint-disable-line no-param-reassign - row.expander = newExpander; // eslint-disable-line no-param-reassign - } else { - row.state |= NODE_REPOSITION_BIT; // eslint-disable-line no-bitwise, no-param-reassign - const children = row.children; - if (children != null) { - for (let i = 0; i < children.length; i++) { - const child = children[i]; - this._pruneExpanders(child, oldExpander, newExpander); - } - } - } - } - - addFieldExpander( - name: string, - comparer: Comparer, - getter: (rowIndex: number) => any, - formatter: (value: any) => string): number { - invariant( - FIELD_EXPANDER_ID_MIN + this.state.fieldExpanders.length < FIELD_EXPANDER_ID_MAX, - 'too many field expanders!'); - this.state.fieldExpanders.push({ name, comparer, getter, formatter }); - return FIELD_EXPANDER_ID_MIN + this.state.fieldExpanders.length - 1; - } - - addStackExpander( - name: string, - maxStackDepth: number, - stackGetter: StackGetter, - frameGetter: FrameGetter, - frameFormatter: FrameFormatter, - reverse: boolean): number { - invariant( - STACK_EXPANDER_ID_MIN + this.state.fieldExpanders.length < STACK_EXPANDER_ID_MAX, - 'Too many stack expanders!'); - const idGetter = reverse ? _callerFrameIdGetter : _calleeFrameIdGetter; - this.state.stackExpanders.push({ - name, - stackGetter, - comparers: _createStackComparers(stackGetter, idGetter, maxStackDepth), - frameIdGetter: idGetter, - frameGetter, - frameFormatter, - }); - return STACK_EXPANDER_ID_MIN + this.state.stackExpanders.length - 1; - } - - getExpanders(): Array { - const expanders = []; - for (let i = 0; i < this.state.fieldExpanders.length; i++) { - expanders.push(FIELD_EXPANDER_ID_MIN + i); - } - for (let i = 0; i < this.state.stackExpanders.length; i++) { - expanders.push(STACK_EXPANDER_ID_MIN + i); - } - return expanders; - } - - getExpanderName(id: number): string { - if (id >= FIELD_EXPANDER_ID_MIN && id <= FIELD_EXPANDER_ID_MAX) { - return this.state.fieldExpanders[id - FIELD_EXPANDER_ID_MIN].name; - } else if (id >= STACK_EXPANDER_ID_MIN && id <= STACK_EXPANDER_ID_MAX) { - return this.state.stackExpanders[id - STACK_EXPANDER_ID_MIN].name; - } - throw new Error(`Unknown expander ID ${id.toString()}`); - } - - setActiveExpanders(ids: Array) { - for (let i = 0; i < ids.length; i++) { - const id = ids[i]; - if (id >= FIELD_EXPANDER_ID_MIN && id <= FIELD_EXPANDER_ID_MAX) { - invariant( - id - FIELD_EXPANDER_ID_MIN < this.state.fieldExpanders.length, - `field expander for id ${id.toString()} does not exist!`); - } else if (id >= STACK_EXPANDER_ID_MIN && id <= STACK_EXPANDER_ID_MAX) { - invariant(id - STACK_EXPANDER_ID_MIN < this.state.stackExpanders.length, - `stack expander for id ${id.toString()} does not exist!`); - } - } - for (let i = 0; i < ids.length; i++) { - if (this.state.activeExpanders.length <= i) { - this._pruneExpanders(this.state.root, INVALID_ACTIVE_EXPANDER, i); - break; - } else if (ids[i] !== this.state.activeExpanders[i]) { - this._pruneExpanders(this.state.root, i, i); - break; - } - } - // TODO: if ids is prefix of activeExpanders, we need to make an expander invalid - this.state.activeExpanders = ids.slice(); - } - - getActiveExpanders(): Array { - return this.state.activeExpanders.slice(); - } - - addAggregator( - name: string, - aggregator: (indexes: Int32Array) => number, - formatter: (value: number) => string, - sorter: Comparer): number { - invariant(this.state.aggregators.length < AGGREGATOR_ID_MAX, 'too many aggregators!'); - this.state.aggregators.push({ name, aggregator, formatter, sorter }); - return this.state.aggregators.length - 1; - } - - getAggregators(): Array { - const aggregators = []; - for (let i = 0; i < this.state.aggregators.length; i++) { - aggregators.push(i); - } - return aggregators; - } - - getAggregatorName(id: number): string { - return this.state.aggregators[id & ACTIVE_AGGREGATOR_MASK].name; // eslint-disable-line no-bitwise, max-len - } - - setActiveAggregators(ids: Array) { - for (let i = 0; i < ids.length; i++) { - const id = ids[i] & ACTIVE_AGGREGATOR_MASK; // eslint-disable-line no-bitwise - invariant( - id >= 0 && id < this.state.aggregators.length, - `aggregator id ${id.toString()} not valid`); - } - this.state.activeAggregators = ids.slice(); - // NB: evaluate root here because dirty bit is for children - // so someone has to start with root, and it might as well be right away - this._evaluateAggregate(this.state.root); - let sorter = NO_SORT_ORDER; - for (let i = ids.length - 1; i >= 0; i--) { - const ascending = (ids[i] & ACTIVE_AGGREGATOR_ASC_BIT) !== 0; // eslint-disable-line no-bitwise, max-len - const id = ids[i] & ACTIVE_AGGREGATOR_MASK; // eslint-disable-line no-bitwise - const comparer = this.state.aggregators[id].sorter; - const captureSorter = sorter; - const captureIndex = i; - sorter = (a: Row, b: Row): number => { - invariant(a.aggregates && b.aggregates, 'Expected aggregates.'); - const c = comparer(a.aggregates[captureIndex], b.aggregates[captureIndex]); - if (c === 0) { - return captureSorter(a, b); - } - return ascending ? -c : c; - }; - } - this.state.sorter = sorter; // eslint-disable-line no-param-reassign - this.state.root.state |= NODE_REORDER_BIT; // eslint-disable-line no-bitwise, no-param-reassign - } - - getActiveAggregators(): Array { - return this.state.activeAggregators.slice(); - } - - getRows(top: number, height: number): Array { - const result = new Array(height); - for (let i = 0; i < height; i++) { - result[i] = null; - } - this._getRowsImpl(this.state.root, top, height, result); - return result; - } - - _findRowImpl(fromRow: number, predicate: (row: Row) => boolean, row: Row): number { - if (row.top > fromRow && predicate(row)) { - return row.top; // this row is a match! - } - - // remember how to clean up after ourselves so we only expand as little as possible - const contractChildren = this.canExpand(row); - const cleanUpChildren = row.children === null; - if (contractChildren) { - this.expand(row); - } - - // evaluate position so we search in the correct order - if ((row.state & NODE_REAGGREGATE_BIT) !== 0) { // eslint-disable-line no-bitwise - this._evaluateAggregates(row); - } - if ((row.state & NODE_REORDER_BIT) !== 0) { // eslint-disable-line no-bitwise - this._evaluateOrder(row); - } - if ((row.state & NODE_REPOSITION_BIT) !== 0) { // eslint-disable-line no-bitwise - this._evaluatePosition(row); - } - // TODO: encapsulate row state management somewhere so logic can be shared with _getRowsImpl - - // search in children - const children = row.children; - if (children !== null) { - for (let i = 0; i < children.length; i++) { - const child = children[i]; - if (child.top + child.height > fromRow) { - const find = this._findRowImpl(fromRow, predicate, child); - if (find >= 0) { - return find; - } - } - } - } - // clean up to leave the tree how it was if we didn't find anything - // this also saves memory - if (contractChildren) { - this.contract(row); - } - if (cleanUpChildren) { - row.children = null; - } - return -1; - } - - // findRow - find the first row that matches a predicate - // parameters - // predicate: returns true when row is found - // fromRow: start search from after this row index (negative for start at beginning) - // returns: index of first row that matches, -1 if no match found - findRow(predicate: (row: Row) => boolean, fromRow: ?number): number { - return this._findRowImpl(!fromRow ? -1 : fromRow, predicate, this.state.root); - } - - getRowLabel(row: Row): string { // eslint-disable-line class-methods-use-this - return row.label; - } - - getRowIndent(row: Row): number { // eslint-disable-line class-methods-use-this - return row.state >>> NODE_INDENT_SHIFT; // eslint-disable-line no-bitwise - } - - getRowExpanderIndex(row: Row): number { // eslint-disable-line class-methods-use-this - if (row.parent) { - return row.parent.expander & ACTIVE_EXPANDER_MASK; // eslint-disable-line no-bitwise - } - return -1; - } - - getRowExpansionPath(row: Row | null): Array { - const path = []; - invariant(row, 'Expected non-null row here'); - const index = row.indices[0]; - row = row.parent; // eslint-disable-line no-param-reassign - while (row) { - const exIndex = row.expander & ACTIVE_EXPANDER_MASK; // eslint-disable-line no-bitwise - const exId = this.state.activeExpanders[exIndex]; - if (exId >= FIELD_EXPANDER_ID_MIN && - exId < FIELD_EXPANDER_ID_MIN + this.state.fieldExpanders.length) { - const expander = this.state.fieldExpanders[exId - FIELD_EXPANDER_ID_MIN]; // eslint-disable-line no-bitwise, max-len - path.push(expander.getter(index)); - row = row.parent; // eslint-disable-line no-param-reassign - } else if (exId >= STACK_EXPANDER_ID_MIN && - exId < STACK_EXPANDER_ID_MIN + this.state.stackExpanders.length) { - const expander = this.state.stackExpanders[exId - STACK_EXPANDER_ID_MIN]; - const stackGetter = expander.stackGetter; - const frameIdGetter = expander.frameIdGetter; - const frameGetter = expander.frameGetter; - const stack = []; - while (row && (row.expander & ACTIVE_EXPANDER_MASK) === exIndex) { // eslint-disable-line no-bitwise, max-len - const depth = row.expander >>> ACTIVE_EXPANDER_FRAME_SHIFT; // eslint-disable-line no-bitwise, max-len - const rowStack = stackGetter(index); - if (depth >= rowStack.length) { - stack.push(''); - } else { - stack.push(frameGetter(frameIdGetter(rowStack, depth))); - } - row = row.parent; // eslint-disable-line no-param-reassign - } - path.push(stack.reverse()); - } - } - return path.reverse(); - } - - getRowAggregate(row: Row, index: number): string { - const aggregator = this.state.aggregators[this.state.activeAggregators[index]]; - invariant(row.aggregates, 'Expected aggregates'); - return aggregator.formatter(row.aggregates[index]); - } - - getHeight(): number { - return this.state.root.height; - } - - canExpand(row: Row): boolean { // eslint-disable-line class-methods-use-this - return (row.state & NODE_EXPANDED_BIT) === 0 && (row.expander !== INVALID_ACTIVE_EXPANDER); // eslint-disable-line no-bitwise, max-len - } - - canContract(row: Row): boolean { // eslint-disable-line class-methods-use-this - return (row.state & NODE_EXPANDED_BIT) !== 0; // eslint-disable-line no-bitwise - } - - expand(row: Row) { - invariant( - (row.state & NODE_EXPANDED_BIT) === 0, // eslint-disable-line no-bitwise - 'can not expand row, already expanded'); - invariant(row.height === 1, `unexpanded row has height ${row.height.toString()} != 1`); - if (row.children === null) { // first expand, generate children - const activeIndex = row.expander & ACTIVE_EXPANDER_MASK; // eslint-disable-line no-bitwise - let nextActiveIndex = activeIndex + 1; // NB: if next is stack, frame is 0 - if (nextActiveIndex >= this.state.activeExpanders.length) { - nextActiveIndex = INVALID_ACTIVE_EXPANDER; - } - invariant( - activeIndex < this.state.activeExpanders.length, - `invalid active expander index ${activeIndex.toString()}`); - const exId = this.state.activeExpanders[activeIndex]; - if (exId >= FIELD_EXPANDER_ID_MIN && - exId < FIELD_EXPANDER_ID_MIN + this.state.fieldExpanders.length) { - const expander = this.state.fieldExpanders[exId - FIELD_EXPANDER_ID_MIN]; - this._addChildrenWithFieldExpander(row, expander, nextActiveIndex); - } else if (exId >= STACK_EXPANDER_ID_MIN && - exId < STACK_EXPANDER_ID_MIN + this.state.stackExpanders.length) { - const depth = row.expander >>> ACTIVE_EXPANDER_FRAME_SHIFT; // eslint-disable-line no-bitwise, max-len - const expander = this.state.stackExpanders[exId - STACK_EXPANDER_ID_MIN]; - this._addChildrenWithStackExpander(row, expander, activeIndex, depth, nextActiveIndex); - } else { - throw new Error(`state.activeIndex ${activeIndex} has invalid expander${exId}`); - } - } - row.state |= NODE_EXPANDED_BIT | NODE_REAGGREGATE_BIT | NODE_REORDER_BIT | NODE_REPOSITION_BIT; // eslint-disable-line no-bitwise, no-param-reassign, max-len - let heightChange = 0; - invariant(row.children, 'Expected a children array'); - for (let i = 0; i < row.children.length; i++) { - heightChange += row.children[i].height; - } - this._updateHeight(row, heightChange); - // if children only contains one node, then expand it as well - invariant(row.children, 'Expected a children array'); - if (row.children.length === 1 && this.canExpand(row.children[0])) { - this.expand(row.children[0]); - } - } - - contract(row: Row) { - this._contractRow(row); - } -} diff --git a/local-cli/server/middleware/heapCapture/src/AggrowTable.jsx b/local-cli/server/middleware/heapCapture/src/AggrowTable.jsx deleted file mode 100644 index 8a54b2ba2..000000000 --- a/local-cli/server/middleware/heapCapture/src/AggrowTable.jsx +++ /dev/null @@ -1,439 +0,0 @@ -// @flow - -import invariant from 'invariant'; -import React from 'react'; - -import Aggrow from './Aggrow'; -import type { Row } from './AggrowExpander'; -import TableConfiguration from './TableConfiguration'; -import TableHeader from './TableHeader'; - -const rowHeight = 20; -const treeIndent = 16; - -type Props = { - aggrow: Aggrow, - enableConfigurationPane: boolean, - onSelectionChange?: (row: Row) => void, -} - -type State = { - aggrow: Aggrow, - viewport: { - top: number, - height: number, - }, - cursor: number, - searchValue: string, -} - -export default class AggrowTable extends React.Component { - static defaultProps = { - enableConfigurationPane: true, - }; - - constructor(props: Props) { - super(props); - this.state = { - aggrow: props.aggrow, - viewport: { top: 0, height: 100 }, - cursor: 0, - searchValue: '', - }; - } - - props: Props; - - state: State; - - componentDidMount() { - document.body && document.body.addEventListener('keydown', this.keydown); - } - - componentWillReceiveProps(nextProps: Props) { - if (this.props.aggrow !== nextProps.aggrow) { - this.setState({ - aggrow: nextProps.aggrow, - viewport: { top: 0, height: 100 }, - cursor: 0, - }); - } - } - - componentWillUnmount() { - document.body && document.body.removeEventListener('keydown', this.keydown); - } - - scroll = (e: SyntheticUIEvent) => { - const viewport = e.target; - invariant(viewport instanceof HTMLElement, 'Expected an HTML element'); - const top = Math.floor((viewport.scrollTop - (viewport.clientHeight * 1.0)) / rowHeight); - const height = Math.ceil(viewport.clientHeight * 3.0 / rowHeight); - if (top !== this.state.viewport.top || height !== this.state.viewport.height) { - this.setState({ viewport: { top, height } }); - } - } - - _updateCursor(position: number) { - this.setState({ cursor: position }); - const onSelectionChange = this.props.onSelectionChange; - if (onSelectionChange) { - const row = this.state.aggrow.expander.getRows(position, 1)[0]; - invariant(row, 'Expected a row'); - onSelectionChange(row); - } - } - - _contractRow(row: Row) { - let newCursor = this.state.cursor; - if (newCursor > row.top && newCursor < row.top + row.height) { // in contracted section - newCursor = row.top; - } else if (newCursor >= row.top + row.height) { // below contracted section - newCursor -= row.height - 1; - } - this.state.aggrow.expander.contract(row); - this._updateCursor(newCursor); - } - - _expandRow(row: Row) { - let newCursor = this.state.cursor; - this.state.aggrow.expander.expand(row); - if (newCursor > row.top) { // below expanded section - newCursor += row.height - 1; - } - this._updateCursor(newCursor); - } - - _scrollDiv: ?HTMLDivElement = null; - - _setScrollDiv = (div: ?HTMLDivElement) => { - this._scrollDiv = div; - } - - _keepCursorInViewport() { - if (this._scrollDiv) { - const cursor = this.state.cursor; - const scrollDiv = this._scrollDiv; - if (cursor * rowHeight < scrollDiv.scrollTop + (scrollDiv.clientHeight * 0.1)) { - scrollDiv.scrollTop = (cursor * rowHeight) - (scrollDiv.clientHeight * 0.1); - } else if ((cursor + 1) * rowHeight > scrollDiv.scrollTop + (scrollDiv.clientHeight * 0.9)) { - scrollDiv.scrollTop = ((cursor + 1) * rowHeight) - (scrollDiv.clientHeight * 0.9); - } - } - } - - keydown = (e: KeyboardEvent) => { - const expander = this.state.aggrow.expander; - let cursor = this.state.cursor; - let row = expander.getRows(cursor, 1)[0]; - invariant(row, 'Expected a row'); - switch (e.keyCode) { - case 38: // up - if (cursor > 0) { - this._updateCursor(cursor - 1); - this._keepCursorInViewport(); - } - e.preventDefault(); - break; - case 40: // down - if (cursor < expander.getHeight() - 1) { - this._updateCursor(cursor + 1); - this._keepCursorInViewport(); - } - e.preventDefault(); - break; - case 37: // left - if (expander.canContract(row)) { - this._contractRow(row); - } else if (expander.getRowIndent(row) > 0) { - const indent = expander.getRowIndent(row) - 1; - while (expander.getRowIndent(row) > indent) { - cursor -= 1; - row = expander.getRows(cursor, 1)[0]; - } - this._updateCursor(cursor); - this._keepCursorInViewport(); - } - e.preventDefault(); - break; - case 39: // right - if (expander.canExpand(row)) { - this._expandRow(row); - } else if (cursor < expander.getHeight() - 1) { - this._updateCursor(cursor + 1); - this._keepCursorInViewport(); - } - e.preventDefault(); - break; - default: - // Do nothing - break; - } - } - - dropAction = (s: string, d: string) => { - const expander = this.state.aggrow.expander; - if (s.startsWith('aggregate:active:')) { - const sIndex = parseInt(s.substr(17), 10); - let dIndex = -1; - const active = expander.getActiveAggregators(); - const dragged = active[sIndex]; - if (d.startsWith('aggregate:insert:')) { - dIndex = parseInt(d.substr(17), 10); - } else if (d === 'divider:insert') { - dIndex = active.length; - } else { - throw new Error(`not allowed to drag ${s} to ${d}`); - } - if (dIndex > sIndex) { - dIndex -= 1; - } - active.splice(sIndex, 1); - active.splice(dIndex, 0, dragged); - expander.setActiveAggregators(active); - this._updateCursor(0); - } else if (s.startsWith('expander:active:')) { - const sIndex = parseInt(s.substr(16), 10); - let dIndex = -1; - const active = expander.getActiveExpanders(); - const dragged = active[sIndex]; - if (d.startsWith('expander:insert:')) { - dIndex = parseInt(d.substr(16), 10); - } else if (d === 'divider:insert') { - dIndex = 0; - } else { - throw new Error(`not allowed to drag ${s} to ${d}`); - } - if (dIndex > sIndex) { - dIndex -= 1; - } - active.splice(sIndex, 1); - active.splice(dIndex, 0, dragged); - expander.setActiveExpanders(active); - this._updateCursor(0); - } else if (s.startsWith('expander:add:')) { - let dIndex = -1; - const sExpander = parseInt(s.substring(13), 10); - if (d.startsWith('expander:insert:')) { - dIndex = parseInt(d.substr(16), 10); - } else if (d === 'divider:insert') { - dIndex = 0; - } else { - throw new Error(`not allowed to drag ${s} to ${d}`); - } - const active = expander.getActiveExpanders(); - active.splice(dIndex, 0, sExpander); - expander.setActiveExpanders(active); - this._updateCursor(0); - } - } - - _handleUpdate = () => { - this.setState({ aggrow: this.state.aggrow }); - } - - renderVirtualizedRows(): React.Element<*> { - const expander = this.state.aggrow.expander; - const viewport = this.state.viewport; - const rows = expander.getRows(viewport.top, viewport.height); - return ( -
- { rows.map((child: Row | null): ?React.Element<*> => this.renderRow(child)) } -
- ); - } - - renderRow(toRender: Row | null): ?React.Element<*> { - if (toRender === null) { - return null; - } - const row = toRender; - let bg = 'white'; - const expander = this.state.aggrow.expander; - const columns = []; - let rowText = ''; - const indent = 4 + (expander.getRowIndent(row) * treeIndent); - const aggregates = expander.getActiveAggregators(); - if (expander.getRowExpanderIndex(row) % 2 === 1) { - bg = '#f0f0f0'; - } - if (row.top === this.state.cursor) { - bg = '#dfe3ee'; - } - for (let i = 0; i < aggregates.length; i++) { - const aggregate = expander.getRowAggregate(row, i); - columns.push(( -
- )); - columns.push(( -
- {aggregate} -
- )); - } - columns.push(( -
- )); - if (expander.canExpand(row)) { - columns.push(( -
this._expandRow(row)} - style={{ - marginLeft: `${indent}px`, - flexShrink: '0', - width: '12px', - textAlign: 'center', - border: '1px solid gray', - }}> - + -
- )); - } else if (expander.canContract(row)) { - columns.push(( -
this._contractRow(row)} - style={{ - marginLeft: `${indent}px`, - flexShrink: '0', - width: '12px', - textAlign: 'center', - border: '1px solid gray', - }}> - - -
- )); - } else { - columns.push(( -
- )); - } - rowText += expander.getRowLabel(row); - columns.push(( -
- {rowText} -
- )); - return ( -
{ // eslint-disable-line react/jsx-no-bind - this._updateCursor(row.top); - }} - style={{ - position: 'absolute', - height: `${(rowHeight - 1)}px`, - top: `${(rowHeight * row.top)}px`, - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - backgroundColor: bg, - borderBottom: '1px solid gray', - }}> - {columns} -
- ); - } - - render(): React.Element<*> { - const expander = this.state.aggrow.expander; - const cursor = this.state.cursor; - const row = expander.getRows(cursor, 1)[0]; - invariant(row, 'Expected a row'); - const selectedExpander = expander.getRowExpanderIndex(row); - return ( -
-
-
- {this.setState({searchValue: event.target.value});}} /> - { - const re = new RegExp(this.state.searchValue); - const i = this.state.aggrow.expander.findRow((row) => re.test(row.label), this.state.cursor); - if (i >= 0) { - this._updateCursor(i); - this._keepCursorInViewport(); - } - }} /> -
- -
-
- { this.renderVirtualizedRows() } -
-
-
- { - this.props.enableConfigurationPane ? - : - undefined - } -
- ); - } -} diff --git a/local-cli/server/middleware/heapCapture/src/DataColumnSelector.jsx b/local-cli/server/middleware/heapCapture/src/DataColumnSelector.jsx deleted file mode 100644 index e7a1784f8..000000000 --- a/local-cli/server/middleware/heapCapture/src/DataColumnSelector.jsx +++ /dev/null @@ -1,42 +0,0 @@ -// @flow - -import invariant from 'invariant'; -import React from 'react'; - -import AggrowData from './AggrowData'; -import type { AggrowColumn } from './AggrowData'; - -type Props = { - aggrow: AggrowData, - filter: (column: AggrowColumn) => boolean, - onSelect: (columnName: string) => void, - selected?: string, -} - -export default class DataColumnSelector extends React.Component { - static defaultProps = { - filter: (): boolean => true, - }; - - props: Props; - - _handleChange = (e: SyntheticEvent) => { - invariant(e.target instanceof HTMLSelectElement, 'Expected element'); - const changed = Number.parseInt(e.target.value, 10); - this.props.onSelect(this.props.aggrow.columns[changed].name); - } - - render(): React.Element<*> { - const columns = this.props.aggrow.columns.filter(this.props.filter); - const selected = columns.findIndex( - (c: AggrowColumn): boolean => c.name === this.props.selected); - return ( - - ); - } -} diff --git a/local-cli/server/middleware/heapCapture/src/Draggable.jsx b/local-cli/server/middleware/heapCapture/src/Draggable.jsx deleted file mode 100644 index 0bf99039d..000000000 --- a/local-cli/server/middleware/heapCapture/src/Draggable.jsx +++ /dev/null @@ -1,26 +0,0 @@ -// @flow - -import React from 'react'; - -type Props = { - id: string, - children?: any, -} - -export default class Draggable extends React.Component { - props: Props; - - _handleDragStart = (e: SyntheticDragEvent) => { - e.dataTransfer.setData('text', this.props.id); - } - - render(): React.Element<*> { - return React.cloneElement( - React.Children.only(this.props.children), - { - draggable: 'true', - onDragStart: this._handleDragStart, - } - ); - } -} diff --git a/local-cli/server/middleware/heapCapture/src/DropTarget.jsx b/local-cli/server/middleware/heapCapture/src/DropTarget.jsx deleted file mode 100644 index 83365eaf0..000000000 --- a/local-cli/server/middleware/heapCapture/src/DropTarget.jsx +++ /dev/null @@ -1,33 +0,0 @@ -// @flow - -import React from 'react'; - -type Props = { - id: string, - dropAction: (sourceId: string, thisId: string) => void, - children?: any, -} - -export default class DropTarget extends React.Component { - props: Props; - - _handleDragOver = (e: SyntheticDragEvent) => { - e.preventDefault(); - } - - _handleDrop = (e: SyntheticDragEvent) => { - const sourceId = e.dataTransfer.getData('text'); - e.preventDefault(); - this.props.dropAction(sourceId, this.props.id); - } - - render(): React.Element<*> { - return React.cloneElement( - React.Children.only(this.props.children), - { - onDragOver: this._handleDragOver, - onDrop: this._handleDrop, - } - ); - } -} diff --git a/local-cli/server/middleware/heapCapture/src/ExpanderConfiguration.jsx b/local-cli/server/middleware/heapCapture/src/ExpanderConfiguration.jsx deleted file mode 100644 index 728f1dfb4..000000000 --- a/local-cli/server/middleware/heapCapture/src/ExpanderConfiguration.jsx +++ /dev/null @@ -1,29 +0,0 @@ -// @flow - -import React from 'react'; - -import AggrowExpander from './AggrowExpander'; -import Draggable from './Draggable'; - -type Props = { - expander: AggrowExpander; - id: number; -} - -export default function ExpanderConfiguration(props: Props): React.Element<*> { - const expander = props.expander; - const id = props.id; - return ( - -
- {expander.getExpanderName(id)} -
-
- ); -} diff --git a/local-cli/server/middleware/heapCapture/src/StackExpanderCreator.jsx b/local-cli/server/middleware/heapCapture/src/StackExpanderCreator.jsx deleted file mode 100644 index 477165ba5..000000000 --- a/local-cli/server/middleware/heapCapture/src/StackExpanderCreator.jsx +++ /dev/null @@ -1,152 +0,0 @@ -// @flow -/* eslint-disable jsx-a11y/label-has-for */ - -import invariant from 'invariant'; -import React from 'react'; - -import Aggrow from './Aggrow'; -import { AggrowStackColumn } from './AggrowData'; -import type { AggrowColumn } from './AggrowData'; -import type { FocusConfig } from './Aggrow'; - -type Props = { - aggrow: Aggrow, - onCreate: (expanderId: number) => void, -} - -type State = { - column: string, - pattern: string, - reverse: boolean, - leftSide: boolean, - firstMatch: boolean, -} - -export default class StackExpanderCreator extends React.Component { - constructor(props: Props) { - super(props); - const data = this.props.aggrow.data; - const firstColumn = data.columns.find(isStackColumn); - this.state = { - column: firstColumn ? firstColumn.name : '', - pattern: '', - reverse: false, - leftSide: false, - firstMatch: true, - }; - } - props: Props; - - state: State; - - _handleColumnSelected = (e: SyntheticEvent) => { - invariant(e.target instanceof HTMLSelectElement, 'Expected select element'); - this.setState({ column: e.target.value }); - } - - _handlePatternSelected = (e: SyntheticEvent) => { - invariant(e.target instanceof HTMLInputElement, 'Expected input element'); - this.setState({ pattern: e.target.value }); - } - - _handleReverseSelected = (e: SyntheticEvent) => { - invariant(e.target instanceof HTMLInputElement, 'Expected input element'); - this.setState({ reverse: e.target.checked }); - } - - _handleFirstMatchSelected = (e: SyntheticEvent) => { - invariant(e.target instanceof HTMLInputElement, 'Expected input element'); - this.setState({ firstMatch: e.target.checked }); - } - - _handleLeftSideSelected = (e: SyntheticEvent) => { - invariant(e.target instanceof HTMLInputElement, 'Expected input element'); - this.setState({ leftSide: e.target.checked }); - } - - _handleCreateClicked = () => { - let focus: FocusConfig; - let expanderName = this.state.column; - if (this.state.pattern !== '') { - focus = { - pattern: new RegExp(this.state.pattern), - firstMatch: this.state.firstMatch, - leftSide: this.state.leftSide, - }; - expanderName += this.state.reverse ? ' reversed' : ''; - expanderName += this.state.leftSide ? ' before' : ' after'; - expanderName += this.state.firstMatch ? ' first ' : ' last '; - expanderName += this.state.pattern; - } - - this.props.onCreate( - this.props.aggrow.addStackExpander( - expanderName, - this.state.column, - this.state.reverse, - focus, - )); - } - - render(): React.Element<*> { - const data = this.props.aggrow.data; - const stackColumns = data.columns.filter(isStackColumn); - return ( -
- - - - - - -
- ); - } -} - -function isStackColumn(c: AggrowColumn): boolean { - return c instanceof AggrowStackColumn; -} diff --git a/local-cli/server/middleware/heapCapture/src/StackRegistry.js b/local-cli/server/middleware/heapCapture/src/StackRegistry.js deleted file mode 100644 index 5964ffcbc..000000000 --- a/local-cli/server/middleware/heapCapture/src/StackRegistry.js +++ /dev/null @@ -1,96 +0,0 @@ -// @flow - -import invariant from 'invariant'; - -export type Stack = { - id: number, - [frameId: number]: Stack, -} - -export type FlattenedStack = Int32Array; - -type StackIdMap = Array; - -export default class StackRegistry { - root: ?Stack = { id: 0 }; - nodeCount: number = 1; - maxDepth: number = -1; - stackIdMap: ?StackIdMap = null; - - insert(parent: Stack, frameId: number): Stack { - invariant(this.stackIdMap === null, 'Stacks already flattened!'); - let node = parent[frameId]; - if (node === undefined) { - node = { id: this.nodeCount }; - this.nodeCount += 1; - - // TODO: make a builder instead of mutating the array? - parent[frameId] = node; // eslint-disable-line no-param-reassign - } - return node; - } - - get(id: number): FlattenedStack { - invariant(this.stackIdMap, 'Stacks not flattened!'); - return this.stackIdMap[id]; - } - - flatten() { - if (this.stackIdMap !== null) { - return; - } - let stackFrameCount = 0; - function countStacks(tree: Stack, depth: number): boolean { - let leaf = true; - Object.keys(tree).forEach((frameId: any) => { - if (frameId !== 'id') { - leaf = countStacks(tree[Number(frameId)], depth + 1); - } - }); - if (leaf) { - stackFrameCount += depth; - } - return false; - } - const root = this.root; - invariant(root, 'Stacks already flattened'); - countStacks(root, 0); - const stackIdMap = new Array(this.nodeCount); - const stackArray = new Int32Array(stackFrameCount); - let maxStackDepth = 0; - stackFrameCount = 0; - function flattenStacksImpl(tree: Stack, stack: Array): void { - let childStack; - maxStackDepth = Math.max(maxStackDepth, stack.length); - Object.keys(tree).forEach((frameId: any) => { - if (frameId !== 'id') { - stack.push(Number(frameId)); - childStack = flattenStacksImpl(tree[frameId], stack); - stack.pop(); - } - }); - - const id = tree.id; - invariant( - id >= 0 && id < stackIdMap.length && stackIdMap[id] === undefined, - 'Invalid stack ID!'); - - if (childStack !== undefined) { - // each child must have our stack as a prefix, so just use that - stackIdMap[id] = childStack.subarray(0, stack.length); - } else { - const newStack = stackArray.subarray(stackFrameCount, stackFrameCount + stack.length); - stackFrameCount += stack.length; - for (let i = 0; i < stack.length; i++) { - newStack[i] = stack[i]; - } - stackIdMap[id] = newStack; - } - return stackIdMap[id]; - } - flattenStacksImpl(root, []); - this.root = null; - this.stackIdMap = stackIdMap; - this.maxDepth = maxStackDepth; - } -} diff --git a/local-cli/server/middleware/heapCapture/src/StringInterner.js b/local-cli/server/middleware/heapCapture/src/StringInterner.js deleted file mode 100644 index ad7b50804..000000000 --- a/local-cli/server/middleware/heapCapture/src/StringInterner.js +++ /dev/null @@ -1,26 +0,0 @@ -// @flow - -type InternedStringsTable = { - [key: string]: number, -} - -export default class StringInterner { - strings: Array = []; - ids: InternedStringsTable = {}; - - intern(s: string): number { - const find = this.ids[s]; - if (find === undefined) { - const id = this.strings.length; - this.ids[s] = id; - this.strings.push(s); - return id; - } - - return find; - } - - get(id: number): string { - return this.strings[id]; - } -} diff --git a/local-cli/server/middleware/heapCapture/src/TableConfiguration.jsx b/local-cli/server/middleware/heapCapture/src/TableConfiguration.jsx deleted file mode 100644 index 98dc71aa4..000000000 --- a/local-cli/server/middleware/heapCapture/src/TableConfiguration.jsx +++ /dev/null @@ -1,86 +0,0 @@ -// @flow - -import React from 'react'; - -import Aggrow from './Aggrow'; -import ExpanderConfiguration from './ExpanderConfiguration'; -import StackExpanderCreator from './StackExpanderCreator'; - -type State = { - expanded: boolean; -} - -type Props = { - aggrow: Aggrow, - onUpdate: () => void, -} - -export default class TableConfiguration extends React.Component { - props: Props; - - state: State = { - expanded: false, - } - - _handleUpdate = () => { - this.props.onUpdate(); - } - - _toggleExpanded = () => { - this.setState({ expanded: !this.state.expanded }); - } - - renderExpander(id: number): React.Element<*> { - return (); - } - - render(): React.Element<*> { - const expanderText = this.state.expanded ? '>>' : '<<'; - const expander = this.props.aggrow.expander; - let config = []; - if (this.state.expanded) { - config = expander.getExpanders().map( - (ex: number): React.Element<*> => this.renderExpander(ex)); - } - return ( -
-
- { expanderText } -
-
- { config } -
-
- -
-
- ); - } -} diff --git a/local-cli/server/middleware/heapCapture/src/TableHeader.jsx b/local-cli/server/middleware/heapCapture/src/TableHeader.jsx deleted file mode 100644 index 3326a2d85..000000000 --- a/local-cli/server/middleware/heapCapture/src/TableHeader.jsx +++ /dev/null @@ -1,103 +0,0 @@ -// @flow - -import React from 'react'; - -import Aggrow from './Aggrow'; -import Draggable from './Draggable'; -import DropTarget from './DropTarget'; - -type Props = { - aggrow: Aggrow, - dropAction: (sourceId: string, thisId: string) => void, - selectedExpander: ?number, -} - -export default function TableHeader(props: Props): React.Element<*> { - const expander = props.aggrow.expander; - const aggregators = expander.getActiveAggregators(); - const expanders = expander.getActiveExpanders(); - const headers = []; - for (let i = 0; i < aggregators.length; i++) { - const name = expander.getAggregatorName(aggregators[i]); - headers.push(( - -
- )); - headers.push(( - -
{name}
-
)); - } - headers.push(( - -
- )); - for (let i = 0; i < expanders.length; i++) { - const name = expander.getExpanderName(expanders[i]); - headers.push(( - -
- {name} -
-
)); - const sep = i + 1 < expanders.length ? '->' : '...'; - headers.push(( - -
- {sep} -
-
) - ); - } - return ( -
- {headers} -
- ); -} diff --git a/local-cli/server/middleware/heapCapture/src/heapCapture.js b/local-cli/server/middleware/heapCapture/src/heapCapture.js deleted file mode 100644 index a40888988..000000000 --- a/local-cli/server/middleware/heapCapture/src/heapCapture.js +++ /dev/null @@ -1,412 +0,0 @@ -/** - * Copyright (c) 2016-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ -'use strict'; -/*eslint no-console-disallow: "off"*/ -/*global preLoadedCapture:true*/ - -import ReactDOM from 'react-dom'; -import React from 'react'; -import { - Aggrow, - AggrowData, - AggrowTable, - StringInterner, - StackRegistry, -} from './index.js'; - -function RefVisitor(refs, id) { - this.refs = refs; - this.id = id; -} - -RefVisitor.prototype = { - moveToEdge: function moveToEdge(name) { - const ref = this.refs[this.id]; - if (ref && ref.edges) { - const edges = ref.edges; - for (const edgeId in edges) { - if (edges[edgeId] === name) { - this.id = edgeId; - return this; - } - } - } - this.id = undefined; - return this; - }, - moveToFirst: function moveToFirst(callback) { - const ref = this.refs[this.id]; - if (ref && ref.edges) { - const edges = ref.edges; - for (const edgeId in edges) { - this.id = edgeId; - if (callback(edges[edgeId], this)) { - return this; - } - } - } - this.id = undefined; - return this; - }, - forEachEdge: function forEachEdge(callback) { - const ref = this.refs[this.id]; - if (ref && ref.edges) { - const edges = ref.edges; - const visitor = new RefVisitor(this.refs, undefined); - for (const edgeId in edges) { - visitor.id = edgeId; - callback(edges[edgeId], visitor); - } - } - }, - getType: function getType() { - const ref = this.refs[this.id]; - if (ref) { - return ref.type; - } - return undefined; - }, - getRef: function getRef() { - return this.refs[this.id]; - }, - clone: function clone() { - return new RefVisitor(this.refs, this.id); - }, - isDefined: function isDefined() { - return !!this.id; - }, - getValue: function getValue() { - const ref = this.refs[this.id]; - if (ref) { - if (ref.type === 'string') { - if (ref.value) { - return ref.value; - } else { - const rope = []; - this.forEachEdge((name, visitor) => { - if (name && name.startsWith('[') && name.endsWith(']')) { - const index = parseInt(name.substring(1, name.length - 1), 10); - rope[index] = visitor.getValue(); - } - }); - return rope.join(''); - } - } else if (ref.type === 'ScriptExecutable' - || ref.type === 'EvalExecutable' - || ref.type === 'ProgramExecutable') { - return ref.value.url + ':' + ref.value.line + ':' + ref.value.col; - } else if (ref.type === 'FunctionExecutable') { - return ref.value.name + '@' + ref.value.url + ':' + ref.value.line + ':' + ref.value.col; - } else if (ref.type === 'NativeExecutable') { - return ref.value.function + ' ' + ref.value.constructor + ' ' + ref.value.name; - } else if (ref.type === 'Function') { - const executable = this.clone().moveToEdge('@Executable'); - if (executable.id) { - return executable.getRef().type + ' ' + executable.getValue(); - } - } - } - return '#none'; - } -}; - -function forEachRef(refs, callback) { - const visitor = new RefVisitor(refs, undefined); - for (const id in refs) { - visitor.id = id; - callback(visitor); - } -} - -function firstRef(refs, callback) { - for (const id in refs) { - const ref = refs[id]; - if (callback(id, ref)) { - return new RefVisitor(refs, id); - } - } - return new RefVisitor(refs, undefined); -} - -function getInternalInstanceName(visitor) { - const type = visitor.clone().moveToEdge('_currentElement').moveToEdge('type'); - if (type.getType() === 'string') { // element.type is string - return type.getValue(); - } else if (type.getType() === 'Function') { // element.type is function - const displayName = type.clone().moveToEdge('displayName'); - if (displayName.isDefined()) { - return displayName.getValue(); // element.type.displayName - } - const name = type.clone().moveToEdge('name'); - if (name.isDefined()) { - return name.getValue(); // element.type.name - } - type.moveToEdge('@Executable'); - if (type.getType() === 'FunctionExecutable') { - return type.getRef().value.name; // element.type symbolicated name - } - } - return '#unknown'; -} - -function buildReactComponentTree(visitor, registry, strings) { - const ref = visitor.getRef(); - if (ref.reactTree || ref.reactParent === undefined) { - return; // has one or doesn't need one - } - const parentVisitor = ref.reactParent; - if (parentVisitor === null) { - ref.reactTree = registry.insert(registry.root, strings.intern(getInternalInstanceName(visitor))); - } else if (parentVisitor) { - const parentRef = parentVisitor.getRef(); - buildReactComponentTree(parentVisitor, registry, strings); - let relativeName = getInternalInstanceName(visitor); - if (ref.reactKey) { - relativeName = ref.reactKey + ': ' + relativeName; - } - ref.reactTree = registry.insert(parentRef.reactTree, strings.intern(relativeName)); - } else { - throw 'non react instance parent of react instance'; - } -} - -function markReactComponentTree(refs, registry, strings) { - // annotate all refs that are react internal instances with their parent and name - // ref.reactParent = visitor that points to parent instance, - // null if we know it's an instance, but don't have a parent yet - // ref.reactKey = if a key is used to distinguish siblings - forEachRef(refs, (visitor) => { - const visitorClone = visitor.clone(); // visitor will get stomped on next iteration - const ref = visitor.getRef(); - visitor.forEachEdge((edgeName, edgeVisitor) => { - const edgeRef = edgeVisitor.getRef(); - if (edgeRef) { - if (edgeName === '_renderedChildren') { - if (ref.reactParent === undefined) { - // ref is react component, even if we don't have a parent yet - ref.reactParent = null; - } - edgeVisitor.forEachEdge((childName, childVisitor) => { - const childRef = childVisitor.getRef(); - if (childRef && childName.startsWith('.')) { - childRef.reactParent = visitorClone; - childRef.reactKey = childName; - } - }); - } else if (edgeName === '_renderedComponent') { - if (ref.reactParent === undefined) { - ref.reactParent = null; - } - edgeRef.reactParent = visitorClone; - } - } - }); - }); - // build tree of react internal instances (since that's what has the structure) - // fill in ref.reactTree = path registry node - forEachRef(refs, (visitor) => { - buildReactComponentTree(visitor, registry, strings); - }); - // hook in components by looking at their _reactInternalInstance fields - forEachRef(refs, (visitor) => { - const ref = visitor.getRef(); - const instanceRef = visitor.moveToEdge('_reactInternalInstance').getRef(); - if (instanceRef) { - ref.reactTree = instanceRef.reactTree; - } - }); -} - -function functionUrlFileName(visitor) { - const executable = visitor.clone().moveToEdge('@Executable'); - const ref = executable.getRef(); - if (ref && ref.value && ref.value.url) { - const url = ref.value.url; - let file = url.substring(url.lastIndexOf('/') + 1); - if (file.endsWith('.js')) { - file = file.substring(0, file.length - 3); - } - return file; - } - return undefined; -} - -function markModules(refs) { - const modules = firstRef(refs, (id, ref) => ref.type === 'CallbackGlobalObject'); - modules.moveToEdge('require'); - modules.moveToFirst((name, visitor) => visitor.getType() === 'JSActivation'); - modules.moveToEdge('modules'); - modules.forEachEdge((name, visitor) => { - const ref = visitor.getRef(); - visitor.moveToEdge('exports'); - if (visitor.getType() === 'Object') { - visitor.moveToFirst((memberName, member) => member.getType() === 'Function'); - if (visitor.isDefined()) { - ref.module = functionUrlFileName(visitor); - } - } else if (visitor.getType() === 'Function') { - const displayName = visitor.clone().moveToEdge('displayName'); - if (displayName.isDefined()) { - ref.module = displayName.getValue(); - } - ref.module = functionUrlFileName(visitor); - } - if (ref && !ref.module) { - ref.module = '#unknown ' + name; - } - }); -} - -function registerPathToRootBFS(breadth, registry, strings) { - while (breadth.length > 0) { - const nextBreadth = []; - for (let i = 0; i < breadth.length; i++) { - const visitor = breadth[i]; - const ref = visitor.getRef(); - visitor.forEachEdge((edgeName, edgeVisitor) => { - const edgeRef = edgeVisitor.getRef(); - if (edgeRef && edgeRef.rootPath === undefined) { - let pathName = edgeRef.type; - if (edgeName) { - pathName = edgeName + ': ' + pathName; - } - edgeRef.rootPath = registry.insert(ref.rootPath, strings.intern(pathName)); - nextBreadth.push(edgeVisitor.clone()); - // copy module and react tree forward - if (edgeRef.module === undefined) { - edgeRef.module = ref.module; - } - if (edgeRef.reactTree === undefined) { - edgeRef.reactTree = ref.reactTree; - } - } - }); - } - breadth = nextBreadth; - } -} - -function registerPathToRoot(capture, registry, strings) { - const refs = capture.refs; - const roots = capture.roots; - markReactComponentTree(refs, registry, strings); - markModules(refs); - let breadth = []; - // BFS from global objects first - forEachRef(refs, (visitor) => { - const ref = visitor.getRef(); - if (ref.type === 'CallbackGlobalObject') { - ref.rootPath = registry.insert(registry.root, strings.intern(ref.type)); - breadth.push(visitor.clone()); - } - }); - registerPathToRootBFS(breadth, registry, strings); - breadth = []; - // lower priority, BFS from other roots - for (const id of roots) { - const visitor = new RefVisitor(refs, id); - const ref = visitor.getRef(); - if (ref.rootPath === undefined) { - ref.rootPath = registry.insert(registry.root, strings.intern(`root ${id}: ${ref.type}`)); - breadth.push(visitor.clone()); - } - } - registerPathToRootBFS(breadth, registry, strings); -} - -function registerCapture(data, captureId, capture, stacks, strings) { - // NB: capture.refs is potentially VERY large, so we try to avoid making - // copies, even if iteration is a bit more annoying. - let rowCount = 0; - for (const id in capture.refs) { // eslint-disable-line no-unused-vars - rowCount++; - } - for (const id in capture.markedBlocks) { // eslint-disable-line no-unused-vars - rowCount++; - } - const inserter = data.rowInserter(rowCount); - registerPathToRoot(capture, stacks, strings); - const noneString = strings.intern('#none'); - const noneStack = stacks.insert(stacks.root, noneString); - forEachRef(capture.refs, (visitor) => { - // want to data.append(value, value, value), not IDs - const ref = visitor.getRef(); - const id = visitor.id; - inserter.insertRow( - parseInt(id, 16), - ref.type, - ref.size, - ref.cellSize, - captureId, - ref.rootPath === undefined ? noneStack : ref.rootPath, - ref.reactTree === undefined ? noneStack : ref.reactTree, - visitor.getValue(), - ref.module === undefined ? '#none' : ref.module, - ); - }); - for (const id in capture.markedBlocks) { - const block = capture.markedBlocks[id]; - inserter.insertRow( - parseInt(id, 16), - 'Marked Block Overhead', - block.capacity - block.size, - 0, - captureId, - noneStack, - noneStack, - 'capacity: ' + block.capacity + ', size: ' + block.size + ', granularity: ' + block.cellSize, - '#none', - ); - } - inserter.done(); -} - -if (preLoadedCapture) { - const strings = new StringInterner(); - const stacks = new StackRegistry(); - const columns = [ - { name: 'id', type: 'int' }, - { name: 'type', type: 'string', strings: strings }, - { name: 'size', type: 'int' }, - { name: 'cell', type: 'int' }, - { name: 'trace', type: 'string', strings: strings }, - { name: 'path', type: 'stack', stacks: stacks, getter: x => strings.get(x), formatter: x => x }, - { name: 'react', type: 'stack', stacks: stacks, getter: x => strings.get(x), formatter: x => x }, - { name: 'value', type: 'string', strings: strings }, - { name: 'module', type: 'string', strings: strings }, - ]; - const data = new AggrowData(columns); - registerCapture(data, 'trace', preLoadedCapture, stacks, strings); - preLoadedCapture = undefined; // let GG clean up the capture - const aggrow = new Aggrow(data); - aggrow.addPointerExpander('Id', 'id'); - const typeExpander = aggrow.addStringExpander('Type', 'type'); - aggrow.addNumberExpander('Size', 'size'); - aggrow.addStringExpander('Trace', 'trace'); - const pathExpander = aggrow.addStackExpander('Path', 'path'); - const reactExpander = aggrow.addStackExpander('React Tree', 'react'); - const valueExpander = aggrow.addStringExpander('Value', 'value'); - const moduleExpander = aggrow.addStringExpander('Module', 'module'); - aggrow.expander.setActiveExpanders([ - pathExpander, - reactExpander, - moduleExpander, - typeExpander, - valueExpander, - ]); - const sizeAggregator = aggrow.addSumAggregator('Size', 'size'); - const cellAggregator = aggrow.addSumAggregator('Cell Size', 'cell'); - const countAggregator = aggrow.addCountAggregator('Count'); - aggrow.expander.setActiveAggregators([ - cellAggregator, - sizeAggregator, - countAggregator, - ]); - ReactDOM.render(, document.body); -} diff --git a/local-cli/server/middleware/heapCapture/src/index.js b/local-cli/server/middleware/heapCapture/src/index.js deleted file mode 100644 index dd8acf0d9..000000000 --- a/local-cli/server/middleware/heapCapture/src/index.js +++ /dev/null @@ -1,24 +0,0 @@ -// @flow - -import Aggrow from './Aggrow'; -import AggrowData from './AggrowData'; -import type { AggrowColumnDef } from './AggrowData'; -import AggrowExpander from './AggrowExpander'; -import AggrowTable from './AggrowTable'; -import StackRegistry from './StackRegistry'; -import type { Stack } from './StackRegistry'; -import StringInterner from './StringInterner'; - -export type { - AggrowColumnDef, - Stack, -}; - -export { - Aggrow, - AggrowData, - AggrowExpander, - AggrowTable, - StackRegistry, - StringInterner, -}; diff --git a/local-cli/server/middleware/heapCapture/webpack.config.js b/local-cli/server/middleware/heapCapture/webpack.config.js deleted file mode 100644 index 1f614fa84..000000000 --- a/local-cli/server/middleware/heapCapture/webpack.config.js +++ /dev/null @@ -1,29 +0,0 @@ -const webpack = require('webpack'); - -module.exports = { - devtool: 'inline-source-map', - entry: './src/heapCapture.js', - resolve: { - extensions: ["", ".js", ".jsx"], - }, - module: { - loaders: [ - { - test: /\.jsx?$/, - include: /\/src\//, - loader: 'babel-loader', - query: { - presets: [ 'react', 'es2015' ], - plugins: [ 'transform-class-properties' ] - }, - }, - ], - }, - plugins: [ - new webpack.BannerPlugin('\n// @generated\n', { raw: true }), - ], - output: { - path: './', - filename: 'bundle.js', - }, -}; diff --git a/local-cli/server/middleware/heapCaptureMiddleware.js b/local-cli/server/middleware/heapCaptureMiddleware.js deleted file mode 100644 index 7f73e5420..000000000 --- a/local-cli/server/middleware/heapCaptureMiddleware.js +++ /dev/null @@ -1,171 +0,0 @@ -/** - * Copyright (c) 2016-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ -'use strict'; -/*eslint no-console-disallow: "off"*/ - -const spawn = require('child_process').spawn; -const fs = require('fs'); -const http = require('http'); -const path = require('path'); -const urlLib = require('url'); -const SourceMapConsumer = require('source-map').SourceMapConsumer; - - -// url: string -// onSuccess: function (SourceMapConsumer) -// onFailure: function (string) -function getSourceMapForUrl(url, onFailure, onSuccess) { - if (!url) { - onFailure('must provide a URL'); - return; - } - - if (url === 'assets://default_bundle') { - onFailure('Don\'t know how to symbolicate in-app bundle, please load from server'); - return; - } - - const parsedUrl = urlLib.parse(url); - const mapPath = parsedUrl.pathname.replace(/\.bundle$/, '.map'); - const options = { - host: 'localhost', - port: parsedUrl.port, - path: mapPath + parsedUrl.search + '&babelSourcemap=true', - }; - - http.get(options, (res) => { - res.setEncoding('utf8'); - let sawEnd = false; - let resBody = ''; - res.on('data', (chunk) => { - resBody += chunk; - }).on('end', () => { - sawEnd = true; - onSuccess(new SourceMapConsumer(resBody)); - }).on('close', (err) => { - if (!sawEnd) { - onFailure('Connection terminated prematurely because of: ' - + err.code + ' for url: ' + url); - } - }); - }).on('error', (err) => { - onFailure('Could not get response from: ' + url + ', error: ' + err.message); - }); -} - -// capture: capture object -// onSuccess: function (Map of url -> SourceMapConsumer) -// onFailure: function (string) -function getSourceMapsForCapture(capture, onFailure, onSuccess) { - const urls = new Set(); - const sourcemaps = new Map(); - for (const id in capture.refs) { - const ref = capture.refs[id]; - if ((ref.type === 'ScriptExecutable' || - ref.type === 'EvalExecutable' || - ref.type === 'ProgramExecutable' || - ref.type === 'FunctionExecutable') && ref.value.url) { - urls.add(ref.value.url); - } - } - urls.forEach((url) => { - getSourceMapForUrl(url, onFailure, (sourcemap) => { - sourcemaps.set(url, sourcemap); - urls.delete(url); - if (urls.size === 0) { - onSuccess(sourcemaps); - } - }); - }); - if (urls.size === 0) { - console.warn('No source information found in capture'); - onSuccess(sourcemaps); - } -} - -// capture: capture object -// onSuccess: function (capture object) -// onFailure: function (string) -function symbolicateHeapCaptureFunctions(capture, onFailure, onSuccess) { - getSourceMapsForCapture(capture, onFailure, (sourcemaps) => { - for (const id in capture.refs) { - const ref = capture.refs[id]; - if (ref.type === 'ScriptExecutable' || - ref.type === 'EvalExecutable' || - ref.type === 'ProgramExecutable' || - ref.type === 'FunctionExecutable') { - const sourcemap = sourcemaps.get(ref.value.url); - if (sourcemap) { - const original = sourcemap.originalPositionFor({ - line: ref.value.line, - column: ref.value.col, - }); - if (original.name) { - ref.value.name = original.name; - } else if (!ref.value.name) { - ref.value.name = path.posix.basename(original.source || '') + ':' + original.line; - } - ref.value.url = original.source; - ref.value.line = original.line; - ref.value.col = original.column; - } - } - } - onSuccess(capture); - }); -} - -module.exports = function(req, res, next) { - if (req.url !== '/jscheapcaptureupload') { - next(); - return; - } - - console.log('symbolicating Heap Capture'); - symbolicateHeapCaptureFunctions(JSON.parse(req.rawBody), (err) => { - console.error('Error when symbolicating: ' + err); - }, - (capture) => { - const preload = path.join(__dirname, 'heapCapture/preLoadedCapture.js'); - fs.writeFileSync(preload, 'var preLoadedCapture = '); - fs.appendFileSync(preload, JSON.stringify(capture)); - fs.appendFileSync(preload, ';'); - const captureDir = path.join(__dirname, 'heapCapture/captures'); - if (!fs.existsSync(captureDir)) { - fs.mkdirSync(captureDir); - } - console.log('Packaging Trace'); - var captureHtml = captureDir + '/capture_' + Date.now() + '.html'; - var capture = fs.createWriteStream(captureHtml); - var inliner = spawn( - 'inliner', - ['--nocompress', 'heapCapture.html'], - { cwd: path.join(__dirname, '/heapCapture/'), - stdio: [ process.stdin, 'pipe', process.stderr ], - }); - inliner.stdout.pipe(capture); - inliner.on('error', (err) => { - console.error('Error processing heap capture: ' + err.message); - console.error('make sure you have installed inliner with \'npm install inliner -g\''); - }); - inliner.on('exit', (code, signal) => { - if (code === 0) { - var response = captureHtml; - console.log('Heap capture written to: ' + response); - res.end(response); - } else { - var response = 'Error processing heap capture, inliner returned code: ' + code; - console.error(response); - res.statusCode = 500; - res.end(response); - } - }); - } - ); -}; diff --git a/local-cli/server/runServer.js b/local-cli/server/runServer.js index 09501018b..5cc924a3b 100644 --- a/local-cli/server/runServer.js +++ b/local-cli/server/runServer.js @@ -20,7 +20,6 @@ const defaultAssetExts = require('../../packager/defaults').assetExts; const defaultPlatforms = require('../../packager/defaults').platforms; const defaultProvidesModuleNodeModules = require('../../packager/defaults').providesModuleNodeModules; const getDevToolsMiddleware = require('./middleware/getDevToolsMiddleware'); -const heapCaptureMiddleware = require('./middleware/heapCaptureMiddleware.js'); const http = require('http'); const indexPageMiddleware = require('./middleware/indexPage'); const loadRawBodyMiddleware = require('./middleware/loadRawBodyMiddleware'); @@ -46,7 +45,6 @@ function runServer(args, config, readyCallback) { .use(copyToClipBoardMiddleware) .use(statusPageMiddleware) .use(systraceProfileMiddleware) - .use(heapCaptureMiddleware) .use(cpuProfilerMiddleware) .use(indexPageMiddleware) .use(unless('/inspector', inspectorProxy.processRequest.bind(inspectorProxy)))