Skip to content

Commit

Permalink
Add more flexibility to Cache and use it for caching in the glimmer
Browse files Browse the repository at this point in the history
Environment.
  • Loading branch information
krisselden committed Jul 18, 2016
1 parent 7edd6ff commit 092fd99
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 100 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"express": "^4.5.0",
"finalhandler": "^0.4.0",
"github": "^0.2.3",
"glimmer-engine": "tildeio/glimmer#d2feca1",
"glimmer-engine": "tildeio/glimmer#592445",
"glob": "^5.0.13",
"htmlbars": "0.14.24",
"mocha": "^2.4.5",
Expand Down
108 changes: 65 additions & 43 deletions packages/ember-glimmer/lib/environment.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import {
Environment as GlimmerEnvironment,
HelperSyntax,
AttributeChangeList,
isSafeString
isSafeString,
compileLayout
} from 'glimmer-runtime';
import Dict from 'ember-metal/empty_object';
import Cache from 'ember-metal/cache';
import { assert, warn } from 'ember-metal/debug';
import { CurlyComponentSyntax, CurlyComponentDefinition } from './syntax/curly-component';
import { DynamicComponentSyntax } from './syntax/dynamic-component';
Expand Down Expand Up @@ -142,41 +143,60 @@ export default class Environment extends GlimmerEnvironment {
constructor({ dom, [OWNER]: owner }) {
super(dom);
this.owner = owner;
this._components = new Dict();
this._templateCache = new Dict();

this._definitionCache = new Cache(2000, ({ name, source }) => {
let { component: ComponentClass, layout } = lookupComponent(owner, name, { source });
if (ComponentClass || layout) {
return new CurlyComponentDefinition(name, ComponentClass, layout);
}
}, ({ name, source }) => {
return source && owner._resolveLocalLookupName(name, source) || name;
});

this._templateCache = new Cache(1000, Template => {
return new Template(this);
}, template => template.id);

this._compilerCache = new Cache(10, Compiler => {
return new Cache(2000, template => {
let compilable = new Compiler(template);
return compileLayout(compilable, this);
}, template => template.id);
}, Compiler => Compiler.id);

this.builtInModifiers = {
action: new ActionModifierManager()
};
}

// Hello future traveler, welcome to the world of syntax refinement.
// The method below is called by Glimmer's runtime compiler to allow
// us to take generic statement syntax and refine it to more meaniful
// syntax for Ember's use case. This on the fly switch-a-roo sounds fine
// and dandy, however Ember has precedence on statement refinement that you
// need to be aware of. The presendence for language constructs is as follows:
//
// ------------------------
// Native & Built-in Syntax
// ------------------------
// User-land components
// ------------------------
// User-land helpers
// ------------------------
//
// The one caveat here is that Ember also allows for dashed references that are
// not a component or helper:
//
// export default Component.extend({
// 'foo-bar': 'LAME'
// });
//
// {{foo-bar}}
//
// The heuristic for the above situation is a dashed "key" in inline form
// that does not resolve to a defintion. In this case refine statement simply
// isn't going to return any syntax and the Glimmer engine knows how to handle
// this case.
// Hello future traveler, welcome to the world of syntax refinement.
// The method below is called by Glimmer's runtime compiler to allow
// us to take generic statement syntax and refine it to more meaniful
// syntax for Ember's use case. This on the fly switch-a-roo sounds fine
// and dandy, however Ember has precedence on statement refinement that you
// need to be aware of. The presendence for language constructs is as follows:
//
// ------------------------
// Native & Built-in Syntax
// ------------------------
// User-land components
// ------------------------
// User-land helpers
// ------------------------
//
// The one caveat here is that Ember also allows for dashed references that are
// not a component or helper:
//
// export default Component.extend({
// 'foo-bar': 'LAME'
// });
//
// {{foo-bar}}
//
// The heuristic for the above situation is a dashed "key" in inline form
// that does not resolve to a defintion. In this case refine statement simply
// isn't going to return any syntax and the Glimmer engine knows how to handle
// this case.

refineStatement(statement, parentMeta) {
// 1. resolve any native syntax – if, unless, with, each, and partial
Expand Down Expand Up @@ -244,20 +264,22 @@ export default class Environment extends GlimmerEnvironment {
}

getComponentDefinition(path, parentMeta) {
let source = parentMeta && `template:${parentMeta.moduleName}`;
let name = path[0];
let cacheKey = (source && this.owner._resolveLocalLookupName(name, source)) || name;
let definition = this._components[cacheKey];

if (!definition) {
let { component: ComponentClass, layout } = lookupComponent(this.owner, name, { source });
let source = parentMeta && `template:${parentMeta.moduleName}`;
return this._definitionCache.get({ name, source });
}

if (ComponentClass || layout) {
definition = this._components[cacheKey] = new CurlyComponentDefinition(name, ComponentClass, layout);
}
}
// normally templates should be exported at the proper module name
// and cached in the container, but this cache supports templates
// that have been set directly on the component's layout property
getTemplate(Template) {
return this._templateCache.get(Template);
}

return definition;
// a Compiler can wrap the template so it needs its own cache
getCompiledBlock(Compiler, template) {
let compilerCache = this._compilerCache.get(Compiler);
return compilerCache.get(template);
}

hasPartial(name) {
Expand Down
48 changes: 21 additions & 27 deletions packages/ember-glimmer/lib/syntax/curly-component.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { StatementSyntax, ValueReference, compileLayout } from 'glimmer-runtime';
import { StatementSyntax, ValueReference } from 'glimmer-runtime';
import { TO_ROOT_REFERENCE, AttributeBindingReference, applyClassNameBinding } from '../utils/references';
import { DIRTY_TAG, IS_DISPATCHING_ATTRS, HAS_BLOCK } from '../component';
import { assert } from 'ember-metal/debug';
import processArgs from '../utils/process-args';
import { getOwner } from 'container/owner';
import { privatize as P } from 'container/registry';
import get from 'ember-metal/property_get';
import { ComponentDefinition } from 'glimmer-runtime';
Expand Down Expand Up @@ -128,34 +127,28 @@ class CurlyComponentManager {
}

layoutFor(definition, bucket, env) {
if (definition.template) {
return compileLayout(new CurlyComponentLayoutCompiler(definition.template), env);
let template = definition.template;
if (!template) {
let { component } = bucket;
template = this.templateFor(component, env);
}
return env.getCompiledBlock(CurlyComponentLayoutCompiler, template);
}

let { component } = bucket;
let template;
let TemplateFactory = component.layout;
// seen the definition but not the template
if (TemplateFactory) {
if (env._templateCache[TemplateFactory.id]) {
template = env._templateCache[TemplateFactory.id];
} else {
template = new TemplateFactory(env);
env._templateCache[TemplateFactory.id] = template;
}
} else {
let layoutName = component.layoutName && get(component, 'layoutName');
let owner = getOwner(component);

if (layoutName) {
template = owner.lookup('template:' + layoutName);
}
if (!template) {
template = owner.lookup(DEFAULT_LAYOUT);
templateFor(component, env) {
let Template = component.layout;
if (Template) {
return env.getTemplate(Template);
}
let { owner } = env;
let layoutName = get(component, 'layoutName');
if (layoutName) {
let template = owner.lookup('template:' + layoutName);
if (template) {
return template;
}
}

return compileLayout(new CurlyComponentLayoutCompiler(template), env);
return owner.lookup(DEFAULT_LAYOUT);
}

getSelf({ component }) {
Expand Down Expand Up @@ -254,7 +247,6 @@ export class CurlyComponentDefinition extends ComponentDefinition {
constructor(name, ComponentClass, template) {
super(name, MANAGER, ComponentClass || Component);
this.template = template;
this._cache = undefined;
}
}

Expand All @@ -271,3 +263,5 @@ class CurlyComponentLayoutCompiler {
builder.attrs.static('class', 'ember-view');
}
}

CurlyComponentLayoutCompiler.id = 'curly';
10 changes: 7 additions & 3 deletions packages/ember-glimmer/lib/syntax/outlet.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ArgsSyntax, StatementSyntax, compileLayout } from 'glimmer-runtime';
import { ArgsSyntax, StatementSyntax } from 'glimmer-runtime';
import { generateGuid, guidFor } from 'ember-metal/utils';
import { RootReference } from '../utils/references';

Expand Down Expand Up @@ -124,7 +124,7 @@ class TopLevelOutletComponentManager extends AbstractOutletComponentManager {
}

layoutFor(definition, bucket, env) {
return compileLayout(new TopLevelOutletLayoutCompiler(definition.template), env);
return env.getCompiledBlock(TopLevelOutletLayoutCompiler, definition.template);
}
}

Expand All @@ -139,7 +139,7 @@ class OutletComponentManager extends AbstractOutletComponentManager {
}

layoutFor(definition, bucket, env) {
return compileLayout(new OutletLayoutCompiler(definition.template), env);
return env.getCompiledBlock(OutletLayoutCompiler, definition.template);
}
}

Expand Down Expand Up @@ -175,6 +175,8 @@ class TopLevelOutletLayoutCompiler {
}
}

TopLevelOutletLayoutCompiler.id = 'top-level-outlet';

class OutletComponentDefinition extends AbstractOutletComponentDefinition {
constructor(outletName, template) {
super(MANAGER, outletName, template);
Expand All @@ -190,3 +192,5 @@ class OutletLayoutCompiler {
builder.wrapLayout(this.template.asLayout());
}
}

OutletLayoutCompiler.id = 'outlet';
6 changes: 4 additions & 2 deletions packages/ember-glimmer/lib/syntax/render.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ArgsSyntax, StatementSyntax, compileLayout } from 'glimmer-runtime';
import { ArgsSyntax, StatementSyntax } from 'glimmer-runtime';
import { ConstReference, isConst } from 'glimmer-reference';
import { assert } from 'ember-metal/debug';
import { RootReference } from '../utils/references';
Expand Down Expand Up @@ -58,7 +58,7 @@ class AbstractRenderManager {
/* abstract create(definition, args, dynamicScope); */

layoutFor(definition, bucket, env) {
return compileLayout(new RenderLayoutCompiler(definition.template), env);
return env.getCompiledBlock(RenderLayoutCompiler, definition.template);
}

getSelf({ controller }) {
Expand Down Expand Up @@ -137,3 +137,5 @@ class RenderLayoutCompiler {
builder.wrapLayout(this.template.asLayout());
}
}

RenderLayoutCompiler.id = 'render';
66 changes: 42 additions & 24 deletions packages/ember-metal/lib/cache.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,35 @@
import EmptyObject from 'ember-metal/empty_object';
export default Cache;

function Cache(limit, func) {
this.store = new EmptyObject();
this.size = 0;
this.misses = 0;
this.hits = 0;
this.limit = limit;
this.func = func;
}

function UNDEFINED() {}
export default class Cache {
constructor(limit, func, key, store) {
this.size = 0;
this.misses = 0;
this.hits = 0;
this.limit = limit;
this.func = func;
this.key = key;
this.store = store || new DefaultStore();
}

Cache.prototype = {
set(key, value) {
set(obj, value) {
if (this.limit > this.size) {
let key = this.key === undefined ? obj : this.key(obj);
this.size ++;
if (value === undefined) {
this.store[key] = UNDEFINED;
this.store.set(key, UNDEFINED);
} else {
this.store[key] = value;
this.store.set(key, value);
}
}

return value;
},

get(key) {
let value = this.store[key];
}

get(obj) {
let key = this.key === undefined ? obj : this.key(obj);
let value = this.store.get(key);
if (value === undefined) {
this.misses ++;
value = this.set(key, this.func(key));
value = this.set(key, this.func(obj));
} else if (value === UNDEFINED) {
this.hits ++;
value = undefined;
Expand All @@ -41,12 +39,32 @@ Cache.prototype = {
}

return value;
},
}

purge() {
this.store = new EmptyObject();
this.store.clear();
this.size = 0;
this.hits = 0;
this.misses = 0;
}
};
}

function UNDEFINED() {}

class DefaultStore {
constructor() {
this.data = new EmptyObject();
}

get(key) {
return this.data[key];
}

set(key, value) {
this.data[key] = value;
}

clear() {
this.data = new EmptyObject();
}
}

0 comments on commit 092fd99

Please sign in to comment.