Skip to content

Commit

Permalink
Refactor render debug (#996)
Browse files Browse the repository at this point in the history
* Refactor component tree updates

Rather than lumping this in with the profile updates, which are for the render performance tab, I started trying to split things out a bit. Hopefully this is step one of many of making this easier to follow!

* Move some render functions to utils

* Fix tests
  • Loading branch information
RobbieTheWagner committed Jul 4, 2019
1 parent e8f2502 commit f4bca8e
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 84 deletions.
137 changes: 62 additions & 75 deletions ember_debug/render-debug.js
Original file line number Diff line number Diff line change
@@ -1,59 +1,12 @@
import PortMixin from 'ember-debug/mixins/port-mixin';
import ProfileManager from 'ember-debug/models/profile-manager';
import { addToQueue, flatten, setupQueue } from './utils/render-utils';

const Ember = window.Ember;
const { computed: { readOnly }, run: { later }, subscribe, Object: EmberObject } = Ember;
const { computed: { readOnly }, subscribe, Object: EmberObject } = Ember;

let profileManager = new ProfileManager();
let queue = [];

function push(info) {
const index = queue.push(info);
if (index === 1) {
later(flush, 50);
}
return index - 1;
}

function flush() {
let entry, i;
for (i = 0; i < queue.length; i++) {
entry = queue[i];
if (entry.type === 'began') {
// If there was an error during rendering `entry.endedIndex` never gets set.
if (entry.endedIndex) {
queue[entry.endedIndex].profileNode = profileManager.began(entry.timestamp, entry.payload, entry.now);
}
} else {
profileManager.ended(entry.timestamp, entry.payload, entry.profileNode);
}

}
queue.length = 0;
}

subscribe('render', {
before(name, timestamp, payload) {
const info = {
type: 'began',
timestamp,
payload,
now: Date.now()
};
return push(info);
},

after(name, timestamp, payload, beganIndex) {
const endedInfo = {
type: 'ended',
timestamp,
payload
};

const index = push(endedInfo);
queue[beganIndex].endedIndex = index;
}
});
// Initial setup, that has to occur before the EmberObject init for some reason
let { profileManager, queue } = setupQueue();
_subscribeToRenderEvents();

export default EmberObject.extend(PortMixin, {
namespace: null,
Expand All @@ -64,59 +17,93 @@ export default EmberObject.extend(PortMixin, {

init() {
this._super();

this.profileManager.wrapForErrors = (context, callback) => this.get('port').wrap(() => callback.call(context));
this._subscribeForViewTrees();
this.profileManager.onProfilesAdded(this, this._updateRenderPerformanceAndComponentTree);
},

willDestroy() {
this._super();

this.profileManager.wrapForErrors = function(context, callback) {
return callback.call(context);
};
this.profileManager.offProfilesAdded(this, this.sendAdded);
this.profileManager.offProfilesAdded(this, this._updateViewTree);
this.profileManager.offProfilesAdded(this, this._updateRenderPerformanceAndComponentTree);
},

_subscribeForViewTrees() {
this.profileManager.onProfilesAdded(this, this._updateViewTree);
sendAdded(profiles) {
this.sendMessage('profilesAdded', { profiles });
},

_updateViewTree(profiles) {
/**
* Updates Render Performance tab rendering durations and updates the component tree
* @param profiles
* @private
*/
_updateRenderPerformanceAndComponentTree(profiles) {
let viewDurations = {};
this._flatten(profiles).forEach(node => {
flatten(profiles).forEach(node => {
if (node.viewGuid) {
viewDurations[node.viewGuid] = node.duration;
}
});
this.get('viewDebug').updateDurations(viewDurations);
this._updateComponentTree();
},

_flatten(profiles, array) {
array = array || [];
profiles.forEach(profile => {
array.push(profile);
this._flatten(profile.children, array);
});
return array;
},

sendAdded(profiles) {
this.sendMessage('profilesAdded', { profiles });
/**
* Update the components tree. Called on each `render.component` event.
* @private
*/
_updateComponentTree() {
this.get('viewDebug').sendTree();
},

messages: {
watchProfiles() {
this.sendMessage('profilesAdded', { profiles: this.profileManager.profiles });
this.profileManager.onProfilesAdded(this, this.sendAdded);
clear() {
this.profileManager.clearProfiles();
this.sendMessage('profilesUpdated', { profiles: [] });
},

releaseProfiles() {
this.profileManager.offProfilesAdded(this, this.sendAdded);
},

clear() {
this.profileManager.clearProfiles();
this.sendMessage('profilesUpdated', { profiles: [] });
watchProfiles() {
this.sendMessage('profilesAdded', { profiles: this.profileManager.profiles });
this.profileManager.onProfilesAdded(this, this.sendAdded);
}
}
});

/**
* This subscribes to render events, so every time the page rerenders, it will push a new profile
* @return {*}
* @private
*/
function _subscribeToRenderEvents() {
subscribe('render', {
before(name, timestamp, payload) {
const info = {
type: 'began',
timestamp,
payload,
now: Date.now()
};

return addToQueue(info);
},

after(name, timestamp, payload, beganIndex) {
const endedInfo = {
type: 'ended',
timestamp,
payload
};

const index = addToQueue(endedInfo);
queue[beganIndex].endedIndex = index;
}
});
}
58 changes: 58 additions & 0 deletions ember_debug/utils/render-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import ProfileManager from 'ember-debug/models/profile-manager';

const Ember = window.Ember;
const { run: { later } } = Ember;

let profileManager, queue;

export function setupQueue() {
profileManager = new ProfileManager();
queue = [];

return { profileManager, queue };
}

/**
* Recursively flatten all profiles and their children
* @param {Profile[]} profiles An array of profiles
* @param {Array} array The array to hold the flattened profiles
* @return {*|Array}
*/
export function flatten(profiles, array = []) {
profiles.forEach(profile => {
array.push(profile);
flatten(profile.children, array);
});
return array;
}

/**
* Push a new profile into the queue
* @param info
* @return {number}
*/
export function addToQueue(info) {
const index = queue.push(info);
if (index === 1) {
later(_flush, 50);
}
return index - 1;
}

function _flush() {
let entry, i;
for (i = 0; i < queue.length; i++) {
entry = queue[i];
if (entry.type === 'began') {
// If there was an error during rendering `entry.endedIndex` never gets set.
if (entry.endedIndex) {
queue[entry.endedIndex].profileNode = profileManager.began(entry.timestamp, entry.payload, entry.now);
}
} else {
profileManager.ended(entry.timestamp, entry.payload, entry.profileNode);
}

}
queue.length = 0;
}

9 changes: 0 additions & 9 deletions ember_debug/view-debug.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ export default EmberObject.extend(PortMixin, {
init() {
this._super(...arguments);

this.viewListener();
this.retainedObjects = [];
this.options = {};
this._durations = {};
Expand Down Expand Up @@ -153,7 +152,6 @@ export default EmberObject.extend(PortMixin, {
}

this.glimmerTree.updateDurations(this._durations);
this.sendTree();
},

retainObject(object) {
Expand Down Expand Up @@ -278,13 +276,6 @@ export default EmberObject.extend(PortMixin, {
}, 50);
},

viewListener() {
this.viewTreeChanged = () => {
this.sendTree();
this.hideLayer();
};
},

viewTree() {
let emberApp = this.get('namespace.owner');
if (!emberApp) {
Expand Down

0 comments on commit f4bca8e

Please sign in to comment.