/** * 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-bitwise: "off"*/ /*eslint no-console-disallow: "off"*/ // TODO: future features // put in a module.exports // filtering / search // pivot around frames in the middle of a stack by callers / callees // graphing? function stringInterner(){ // eslint-disable-line no-unused-vars var strings=[]; var ids={}; return { intern:function internString(s){ var find=ids[s]; if(find===undefined){ var id=strings.length; ids[s]=id; strings.push(s); return id;}else { return find;}}, get:function getString(id){ return strings[id];}};} function stackData(stackIdMap,maxDepth){ // eslint-disable-line no-unused-vars return { maxDepth:maxDepth, get:function getStack(id){ return stackIdMap[id];}};} function stackRegistry(interner){ // eslint-disable-line no-unused-vars return { root:{id:0}, nodeCount:1, insert:function insertNode(parent,label){ var labelId=interner.intern(label); var node=parent[labelId]; if(node===undefined){ node={id:this.nodeCount}; this.nodeCount++; parent[labelId]=node;} return node;}, flatten:function flattenStacks(){ var stackFrameCount=0; function countStacks(tree,depth){ var leaf=true; for(var frameId in tree){ if(frameId!=='id'){ leaf=countStacks(tree[frameId],depth+1);}} if(leaf){ stackFrameCount+=depth;} return false;} countStacks(this.root,0); console.log('size needed to store stacks: '+(stackFrameCount*4).toString()+'B'); var stackIdMap=new Array(this.nodeCount); var stackArray=new Int32Array(stackFrameCount); var maxStackDepth=0; stackFrameCount=0; function flattenStacksImpl(tree,stack){ var childStack=void 0; maxStackDepth=Math.max(maxStackDepth,stack.length); for(var frameId in tree){ if(frameId!=='id'){ stack.push(Number(frameId)); childStack=flattenStacksImpl(tree[frameId],stack); stack.pop();}} var id=tree.id; if(id<0||id>=stackIdMap.length||stackIdMap[id]!==undefined){ throw '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 { var newStack=stackArray.subarray(stackFrameCount,stackFrameCount+stack.length); stackFrameCount+=stack.length; for(var i=0;i' 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 frameGetter(a,captureDepth)-frameGetter(b,captureDepth);};};for(var depth=0;depth>>NODE_INDENT_SHIFT)+1; var state=NODE_REPOSITION_BIT| NODE_REAGGREGATE_BIT| NODE_REORDER_BIT| indent<',indices,INVALID_ACTIVE_EXPANDER)}; function evaluateAggregate(row){ var activeAggregators=state.activeAggregators; var aggregates=new Array(activeAggregators.length); for(var j=0;j=top&&row.top var begin=0; var beginStack=null; row.children=[]; while(begindepth){ break;} begin++;} if(begin>0){ row.children.push(createTreeNode( row, columnName+'', rowIndices.subarray(0,begin), nextActiveIndex));} // aggregate the rest under frames if(begin=FIELD_EXPANDER_ID_MAX){ throw 'too many field expanders!';} state.fieldExpanders.push({ name:name, // name for column formatter:formatter, // row index -> display string comparer:comparer}); // compares by two row indices return FIELD_EXPANDER_ID_MIN+state.fieldExpanders.length-1;}, addCalleeStackExpander:function addCalleeStackExpander(name,stackGetter){ if(STACK_EXPANDER_ID_MIN+state.fieldExpanders.length>=STACK_EXPANDER_ID_MAX){ throw 'too many stack expanders!';} state.stackExpanders.push({ name:name, // name for column stackGetter:stackGetter, // row index -> stack array comparers:createStackComparers(stackGetter,calleeFrameGetter), // depth -> comparer frameGetter:calleeFrameGetter}); // (stack, depth) -> string id return STACK_EXPANDER_ID_MIN+state.stackExpanders.length-1;}, addCallerStackExpander:function addCallerStackExpander(name,stackGetter){ if(STACK_EXPANDER_ID_MIN+state.fieldExpanders.length>=STACK_EXPANDER_ID_MAX){ throw 'too many stack expanders!';} state.stackExpanders.push({ name:name, stackGetter:stackGetter, comparers:createStackComparers(stackGetter,callerFrameGetter), frameGetter:callerFrameGetter}); return STACK_EXPANDER_ID_MIN+state.stackExpanders.length-1;}, getExpanders:function getExpanders(){ var expanders=[]; for(var _i6=0;_i6=FIELD_EXPANDER_ID_MIN&&id<=FIELD_EXPANDER_ID_MAX){ return state.fieldExpanders[id-FIELD_EXPANDER_ID_MIN].name;}else if(id>=STACK_EXPANDER_ID_MIN&&id<=STACK_EXPANDER_ID_MAX){ return state.stackExpanders[id-STACK_EXPANDER_ID_MIN].name;} throw 'Unknown expander ID '+id.toString();}, setActiveExpanders:function setActiveExpanders(ids){ for(var _i8=0;_i8=FIELD_EXPANDER_ID_MIN&&id<=FIELD_EXPANDER_ID_MAX){ if(id-FIELD_EXPANDER_ID_MIN>=state.fieldExpanders.length){ throw 'field expander for id '+id.toString()+' does not exist!';}}else if(id>=STACK_EXPANDER_ID_MIN&&id<=STACK_EXPANDER_ID_MAX){ if(id-STACK_EXPANDER_ID_MIN>=state.stackExpanders.length){ throw 'stack expander for id '+id.toString()+' does not exist!';}}} for(var _i9=0;_i9=AGGREGATOR_ID_MAX){ throw 'too many aggregators!';} state.aggregators.push({ name:name, // name for column aggregator:aggregator, // index array -> aggregate value formatter:formatter, // aggregate value -> display string sorter:sorter}); // compare two aggregate values return state.aggregators.length-1;}, getAggregators:function getAggregators(){ var aggregators=[]; for(var _i10=0;_i10state.aggregators.length){ throw 'aggregator id '+id.toString()+' not valid';}} 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 evaluateAggregate(state.root); var sorter=noSortOrder;var _loop2=function _loop2( _i12){ var ascending=(ids[_i12]&ACTIVE_AGGREGATOR_ASC_BIT)!==0; var id=ids[_i12]&ACTIVE_AGGREGATOR_MASK; var comparer=state.aggregators[id].sorter; var captureSorter=sorter; var captureIndex=_i12; sorter=function sorter(a,b){ var c=comparer(a.aggregates[captureIndex],b.aggregates[captureIndex]); if(c===0){ return captureSorter(a,b);} return ascending?-c:c;};};for(var _i12=ids.length-1;_i12>=0;_i12--){_loop2(_i12);} state.sorter=sorter; state.root.state|=NODE_REORDER_BIT;}, getActiveAggregators:function getActiveAggregators(){ return state.activeAggregators.slice();}, getRows:function getRows(top,height){ var result=new Array(height); for(var _i13=0;_i13>>NODE_INDENT_SHIFT;}, getRowAggregate:function getRowAggregate(row,index){ var aggregator=state.aggregators[state.activeAggregators[index]]; return aggregator.formatter(row.aggregates[index]);}, getHeight:function getHeight(){ return state.root.height;}, canExpand:function canExpand(row){ return (row.state&NODE_EXPANDED_BIT)===0&&row.expander!==INVALID_ACTIVE_EXPANDER;}, canContract:function canContract(row){ return (row.state&NODE_EXPANDED_BIT)!==0;}, expand:function expand(row){ if((row.state&NODE_EXPANDED_BIT)!==0){ throw 'can not expand row, already expanded';} if(row.height!==1){ throw 'unexpanded row has height '+row.height.toString()+' != 1';} if(row.children===null){ // first expand, generate children var activeIndex=row.expander&ACTIVE_EXPANDER_MASK; var nextActiveIndex=activeIndex+1; // NB: if next is stack, frame is 0 if(nextActiveIndex>=state.activeExpanders.length){ nextActiveIndex=INVALID_ACTIVE_EXPANDER;} if(activeIndex>=state.activeExpanders.length){ throw 'invalid active expander index '+activeIndex.toString();} var exId=state.activeExpanders[activeIndex]; if(exId>=FIELD_EXPANDER_ID_MIN&& exId=STACK_EXPANDER_ID_MIN&& exId>>ACTIVE_EXPANDER_FRAME_SHIFT; var _expander=state.stackExpanders[exId-STACK_EXPANDER_ID_MIN]; addChildrenWithStackExpander(row,_expander,activeIndex,depth,nextActiveIndex);}else { throw 'state.activeIndex '+activeIndex.toString()+ ' has invalid expander'+exId.toString();}} row.state|=NODE_EXPANDED_BIT| NODE_REAGGREGATE_BIT|NODE_REORDER_BIT|NODE_REPOSITION_BIT; var heightChange=0; for(var _i14=0;_i14