/** * 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}); 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}); 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}); 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