Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Component Performance Tweaks #12289

Merged
merged 10 commits into from
Sep 4, 2015
Merged
2 changes: 1 addition & 1 deletion ember-cli-build.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ function babelConfigFor(environment) {

if (isProduction) {
plugins.push(filterImports({
'ember-metal/debug': ['assert', 'debug', 'deprecate', 'info', 'runInDebug', 'warn']
'ember-metal/debug': ['assert', 'debug', 'deprecate', 'info', 'runInDebug', 'warn', 'debugSeal']
}));
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"finalhandler": "^0.4.0",
"github": "^0.2.3",
"glob": "~4.3.2",
"htmlbars": "0.14.4",
"htmlbars": "0.14.5",
"qunit-extras": "^1.3.0",
"qunitjs": "^1.16.0",
"route-recognizer": "0.1.5",
Expand Down
4 changes: 4 additions & 0 deletions packages/ember-debug/lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ setDebugFunction('runInDebug', function runInDebug(func) {
func();
});

setDebugFunction('debugSeal', function debugSeal(obj) {
Object.seal(obj);
});

setDebugFunction('deprecate', _deprecate);
setDebugFunction('warn', _warn);
/**
Expand Down
9 changes: 8 additions & 1 deletion packages/ember-htmlbars/lib/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ import merge from 'ember-metal/merge';
import subexpr from 'ember-htmlbars/hooks/subexpr';
import concat from 'ember-htmlbars/hooks/concat';
import linkRenderNode from 'ember-htmlbars/hooks/link-render-node';
import createFreshScope from 'ember-htmlbars/hooks/create-fresh-scope';
import createFreshScope, { createChildScope } from 'ember-htmlbars/hooks/create-fresh-scope';
import bindShadowScope from 'ember-htmlbars/hooks/bind-shadow-scope';
import bindSelf from 'ember-htmlbars/hooks/bind-self';
import bindScope from 'ember-htmlbars/hooks/bind-scope';
import bindLocal from 'ember-htmlbars/hooks/bind-local';
import bindBlock from 'ember-htmlbars/hooks/bind-block';
import updateSelf from 'ember-htmlbars/hooks/update-self';
import getRoot from 'ember-htmlbars/hooks/get-root';
import getChild from 'ember-htmlbars/hooks/get-child';
import getBlock from 'ember-htmlbars/hooks/get-block';
import getValue from 'ember-htmlbars/hooks/get-value';
import getCellOrValue from 'ember-htmlbars/hooks/get-cell-or-value';
import cleanupRenderNode from 'ember-htmlbars/hooks/cleanup-render-node';
Expand All @@ -40,11 +42,14 @@ emberHooks.keywords = keywords;
merge(emberHooks, {
linkRenderNode,
createFreshScope,
createChildScope,
bindShadowScope,
bindSelf,
bindScope,
bindLocal,
bindBlock,
updateSelf,
getBlock,
getRoot,
getChild,
getValue,
Expand Down Expand Up @@ -74,6 +79,7 @@ import partial from 'ember-htmlbars/keywords/partial';
import input from 'ember-htmlbars/keywords/input';
import textarea from 'ember-htmlbars/keywords/textarea';
import collection from 'ember-htmlbars/keywords/collection';
import yieldKeyword from 'ember-htmlbars/keywords/yield';
import legacyYield from 'ember-htmlbars/keywords/legacy-yield';
import mut, { privateMut } from 'ember-htmlbars/keywords/mut';
import each from 'ember-htmlbars/keywords/each';
Expand All @@ -88,6 +94,7 @@ registerKeyword('component', componentKeyword);
registerKeyword('partial', partial);
registerKeyword('input', input);
registerKeyword('textarea', textarea);
registerKeyword('yield', yieldKeyword);
registerKeyword('legacy-yield', legacyYield);
registerKeyword('mut', mut);
registerKeyword('@mut', privateMut);
Expand Down
3 changes: 3 additions & 0 deletions packages/ember-htmlbars/lib/hooks/bind-block.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function bindBlock(env, scope, block, name='default') {
scope.bindBlock(name, block);
}
13 changes: 6 additions & 7 deletions packages/ember-htmlbars/lib/hooks/bind-local.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,18 @@
@submodule ember-htmlbars
*/

import Stream from 'ember-metal/streams/stream';
import { wrap } from 'ember-metal/streams/stream';
import ProxyStream from 'ember-metal/streams/proxy-stream';

export default function bindLocal(env, scope, key, value) {
var isExisting = scope.locals.hasOwnProperty(key);
if (isExisting) {
var existing = scope.locals[key];

// TODO: What is the cause of these cases?
if (scope.hasOwnLocal(key)) {
let existing = scope.getLocal(key);
if (existing !== value) {
existing.setSource(value);
}
} else {
var newValue = Stream.wrap(value, ProxyStream, key);
scope.locals[key] = newValue;
let newValue = wrap(value, ProxyStream, key);
scope.bindLocal(key, newValue);
}
}
35 changes: 26 additions & 9 deletions packages/ember-htmlbars/lib/hooks/bind-self.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
@submodule ember-htmlbars
*/

import newStream from 'ember-htmlbars/utils/new-stream';
import _Ember from 'ember-metal';
import ProxyStream from 'ember-metal/streams/proxy-stream';

export default function bindSelf(env, scope, _self) {
let self = _self;
Expand All @@ -12,25 +13,41 @@ export default function bindSelf(env, scope, _self) {
let { controller } = self;
self = self.self;

newStream(scope.locals, 'controller', controller || self);
if (!!_Ember.ENV._ENABLE_LEGACY_CONTROLLER_SUPPORT) {
scope.bindLocal('controller', newStream(controller || self));
}
}

if (self && self.isView) {
newStream(scope.locals, 'view', self, null);
newStream(scope.locals, 'controller', scope.locals.view.getKey('controller'));
if (!!_Ember.ENV._ENABLE_LEGACY_VIEW_SUPPORT) {
scope.bindLocal('view', newStream(self, 'view'));
}

if (!!_Ember.ENV._ENABLE_LEGACY_CONTROLLER_SUPPORT) {
scope.bindLocal('controller', newStream(self, '').getKey('controller'));
}

let selfStream = newStream(self, '');

if (self.isGlimmerComponent) {
newStream(scope, 'self', self, null, true);
scope.bindSelf(selfStream);
} else {
newStream(scope, 'self', scope.locals.view.getKey('context'), null, true);
scope.bindSelf(newStream(selfStream.getKey('context'), ''));
}

return;
}

newStream(scope, 'self', self, null, true);
let selfStream = newStream(self, '');
scope.bindSelf(selfStream);

if (!scope.locals.controller) {
scope.locals.controller = scope.self;
if (!!_Ember.ENV._ENABLE_LEGACY_CONTROLLER_SUPPORT) {
if (!scope.hasLocal('controller')) {
scope.bindLocal('controller', selfStream);
}
}
}

function newStream(newValue, key) {
return new ProxyStream(newValue, key);
}
20 changes: 12 additions & 8 deletions packages/ember-htmlbars/lib/hooks/bind-shadow-scope.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
@submodule ember-htmlbars
*/

import newStream from 'ember-htmlbars/utils/new-stream';
import ProxyStream from 'ember-metal/streams/proxy-stream';

export default function bindShadowScope(env, parentScope, shadowScope, options) {
if (!options) { return; }
Expand All @@ -12,31 +12,35 @@ export default function bindShadowScope(env, parentScope, shadowScope, options)

if (parentScope && parentScope.overrideController) {
didOverrideController = true;
shadowScope.locals.controller = parentScope.locals.controller;
shadowScope.bindLocal('controller', parentScope.getLocal('controller'));
}

var view = options.view;
if (view && !view.isComponent) {
newStream(shadowScope.locals, 'view', view, null);
shadowScope.bindLocal('view', newStream(view, 'view'));

if (!didOverrideController) {
newStream(shadowScope.locals, 'controller', shadowScope.locals.view.getKey('controller'));
shadowScope.bindLocal('controller', newStream(shadowScope.getLocal('view').getKey('controller')));
}

if (view.isView) {
newStream(shadowScope, 'self', shadowScope.locals.view.getKey('context'), null, true);
shadowScope.bindSelf(newStream(shadowScope.getLocal('view').getKey('context'), ''));
}
}

shadowScope.view = view;
shadowScope.bindView(view);

if (view && options.attrs) {
shadowScope.component = view;
shadowScope.bindComponent(view);
}

if ('attrs' in options) {
shadowScope.attrs = options.attrs;
shadowScope.bindAttrs(options.attrs);
}

return shadowScope;
}

function newStream(newValue, key) {
return new ProxyStream(newValue, key);
}
4 changes: 2 additions & 2 deletions packages/ember-htmlbars/lib/hooks/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import buildComponentTemplate, { buildHTMLTemplate } from 'ember-views/system/bu
import lookupComponent from 'ember-htmlbars/utils/lookup-component';

export default function componentHook(renderNode, env, scope, _tagName, params, attrs, templates, visitor) {
var state = renderNode.state;
var state = renderNode.getState();

// Determine if this is an initial render or a re-render
if (state.manager) {
Expand Down Expand Up @@ -77,7 +77,7 @@ export default function componentHook(renderNode, env, scope, _tagName, params,
tagName,
isAngleBracket: true,
isComponentElement: true,
outerAttrs: scope.attrs,
outerAttrs: scope.getAttrs(),
parentScope: scope
};

Expand Down
139 changes: 131 additions & 8 deletions packages/ember-htmlbars/lib/hooks/create-fresh-scope.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import ProxyStream from 'ember-metal/streams/proxy-stream';
import EmptyObject from 'ember-metal/empty_object';

/*
Ember's implementation of HTMLBars creates an enriched scope.

Expand Down Expand Up @@ -46,13 +49,133 @@
the current view's `controller`.
*/

function Scope(parent) {
this._self = undefined;
this._blocks = undefined;
this._component = undefined;
this._view = undefined;
this._attrs = undefined;
this._locals = undefined;
this._localPresent = undefined;
this.overrideController = undefined;
this.parent = parent;
}

let proto = Scope.prototype;

proto.getSelf = function() {
return this._self || this.parent.getSelf();
};

proto.bindSelf = function(self) {
this._self = self;
};

proto.updateSelf = function(self, key) {
let existing = this._self;

if (existing) {
existing.setSource(self);
} else {
this._self = new ProxyStream(self, key);
}
};

proto.getBlock = function(name) {
if (!this._blocks) { return this.parent.getBlock(name); }
return this._blocks[name] || this.parent.getBlock(name);
};

proto.hasBlock = function(name) {
if (!this._blocks) { return this.parent.hasBlock(name); }
return !!(this._blocks[name] || this.parent.hasBlock(name));
};

proto.bindBlock = function(name, block) {
if (!this._blocks) { this._blocks = new EmptyObject(); }
this._blocks[name] = block;
};

proto.getComponent = function() {
return this._component || this.parent.getComponent();
};

proto.bindComponent = function(component) {
this._component = component;
};

proto.getView = function() {
return this._view || this.parent.getView();
};

proto.bindView = function(view) {
this._view = view;
};

proto.getAttrs = function() {
return this._attrs || this.parent.getAttrs();
};

proto.bindAttrs = function(attrs) {
this._attrs = attrs;
};

proto.hasLocal = function(name) {
if (!this._localPresent) { return this.parent.hasLocal(name); }
return this._localPresent[name] || this.parent.hasLocal(name);
};

proto.hasOwnLocal = function(name) {
return this._localPresent && this._localPresent[name];
};

proto.getLocal = function(name) {
if (!this._localPresent) { return this.parent.getLocal(name); }
return this._localPresent[name] ? this._locals[name] : this.parent.getLocal(name);
};

proto.bindLocal = function(name, value) {
if (!this._localPresent) {
this._localPresent = new EmptyObject();
this._locals = new EmptyObject();
}

this._localPresent[name] = true;
this._locals[name] = value;
};

const EMPTY = {
_self: undefined,
_blocks: undefined,
_component: undefined,
_view: undefined,
_attrs: undefined,
_locals: undefined,
_localPresent: undefined,
overrideController: undefined,

getSelf() { return null; },
bindSelf(self) { return null; },
updateSelf(self, key) { return null; },
getBlock(name) { return null; },
bindBlock(name, block) { return null; },
hasBlock(name) { return false; },
getComponent() { return null; },
bindComponent() { return null; },
getView() { return null; },
bindView(view) { return null; },
getAttrs() { return null; },
bindAttrs(attrs) { return null; },
hasLocal(name) { return false; },
hasOwnLocal(name) { return false; },
getLocal(name) { return null; },
bindLocal(name, value) { return null; }
};

export default function createFreshScope() {
return {
self: null,
blocks: {},
component: null,
attrs: null,
locals: {},
localPresent: {}
};
return new Scope(EMPTY);
}

export function createChildScope(parent) {
return new Scope(parent);
}
2 changes: 1 addition & 1 deletion packages/ember-htmlbars/lib/hooks/did-render-node.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export default function didRenderNode(morph, env) {
env.renderedNodes[morph.guid] = true;
env.renderedNodes.add(morph);
}
Loading