Skip to content

Commit

Permalink
compiler: options.cache=true support
Browse files Browse the repository at this point in the history
  • Loading branch information
indutny committed May 23, 2013
1 parent 4843f3c commit aee8aac
Showing 1 changed file with 175 additions and 27 deletions.
202 changes: 175 additions & 27 deletions lib/bemhtml/compiler.js
Expand Up @@ -8,6 +8,11 @@ var xjst = require('xjst');

function Compiler(options) {
this.options = options || {};
if (options.cache === true) {
if (options.optimize === false || options['no-opt']) {
throw new Error('Cache doesn\'t work with development build');
}
}

this.matches = {
match: true, block: true, elem: true, mode: true, mod: true,
Expand Down Expand Up @@ -333,51 +338,194 @@ Compiler.prototype.getModeObj = function getModeObj(mode) {
Compiler.prototype.replaceApplyCtx = function replaceApplyCtx(ast) {
// applyCtx(newCtx) => apply({ _mode: '', ctx: newCtx })
assert.equal(ast.arguments.length, 1, 'applyCtx() must have one argument');
return {
var changes = [{
type: 'ObjectExpression',
properties: [{
type: 'Property',
key: { type: 'Literal', value: '_mode' },
value: { type: 'Literal', value: '' },
kind: 'init'
}, {
type: 'Property',
key: { type: 'Literal', value: 'ctx' },
value: ast.arguments[0],
kind: 'init'
}]
}];
return this.traceChanges(changes, {
type: 'CallExpression',
callee: { type: 'Identifier', name: 'apply' },
arguments: [{
type: 'ObjectExpression',
properties: [{
type: 'Property',
key: { type: 'Literal', value: '_mode' },
value: { type: 'Literal', value: '' },
kind: 'init'
}, {
type: 'Property',
key: { type: 'Literal', value: 'ctx' },
value: ast.arguments[0],
kind: 'init'
}]
}]
};
arguments: changes
});
};

Compiler.prototype.replaceApply = function replaceApply(ast) {
// apply(mode, {})
return {
var changes = ast.arguments.map(function(arg) {
if (arg.type !== 'Literal') return arg;
return this.getModeObj(arg);
}, this);

return this.traceChanges(changes, {
type: 'CallExpression',
callee: ast.callee,
arguments: ast.arguments.map(function(arg) {
if (arg.type !== 'Literal') return arg;
return this.getModeObj(arg);
}, this)
};
arguments: changes
});
};

Compiler.prototype.replaceLocal = function replaceLocal(ast) {
// (local(mode, {}))(body)
return {
var changes = ast.callee.arguments.map(function(arg) {
if (arg.type !== 'Literal') return arg;
return this.getModeObj(arg);
}, this);

return this.traceChanges(changes, {
type: 'CallExpression',
callee: {
type: 'CallExpression',
callee: ast.callee.callee,
arguments: ast.callee.arguments.map(function(arg) {
if (arg.type !== 'Literal') return arg;
return this.getModeObj(arg);
}, this)
arguments: changes
},
arguments: ast.arguments
});
};

Compiler.prototype.traceChanges = function traceChanges(changes, expr) {
if (this.options.cache !== true) return expr;

// Translate changes AST array to pairs of key/value
var pairs = changes.reduce(function(prev, cur) {
return prev.concat(cur.properties);
}, []).map(function(property) {
return {
key: property.key.name || property.key.value,
value: property.value
};
});

// Filter changes that could be logged
// (Literal values and movements)
pairs = pairs.map(function(pair) {
var key = { type: 'Literal', value: pair.key };

var val = pair.value;
if (val.type === 'Literal') return { key: key, val: val, path: null };

var path = [];
while (val.type === 'MemberExpression' && !val.computed) {
path.unshift(val.property.name);
val = val.object;
}

if (val.type === 'ThisExpression') {
return {
key: key,
val: { type: 'Literal', value: 1 },
path: { type: 'Literal', value: path.join('.') }
};
}

return null;
});

var data = pairs.filter(function(pair) {
return pair !== null;
}).map(function(pair) {
return {
type: 'ArrayExpression',
elements: pair.path ? [pair.key, pair.path, pair.val] :
[pair.key, pair.val]
};
});

var history = {
type: 'MemberExpression',
computed: false,
property: { type: 'Identifier', name: '__$history' },
object: { type: 'ThisExpression' }
};

// this.__$history.push(...)
var save = {
type: 'CallExpression',
callee: {
type: 'MemberExpression',
computed: false,
property: { type: 'Identifier', name: 'push' },
object: history
},
arguments: data
};

// this.__$history = this.__$history.slice(0, -data.len);
var restore = {
type: 'AssignmentExpression',
operator: '=',
left: history,
right: {
type: 'CallExpression',
callee: {
type: 'MemberExpression',
computed: false,
property: { type: 'Identifier', name: 'slice' },
object: history
},
arguments: [
{ type: 'Literal', value: 0 },
{ type: 'Literal', value: -data.length }
]
}
};

// function() { push_history(); invoke local; }.call(this)
var res = { type: 'Identifier', name: '__$r' };
return {
type: 'CallExpression',
arguments: [{ type: 'ThisExpression' }],
callee: {
type: 'MemberExpression',
computed: false,
property: { type: 'Identifier', name: 'call' },
object: {
type: 'FunctionExpression',
id: null,
params: [],
defaults: [],
rest: null,
generator: false,
expression: false,
body: {
type: 'BlockStatement',
body: [{
type: 'VariableDeclaration',
kind: 'var',
declarations: [{
type: 'VariableDeclarator',
id: res,
init: null
}]
}, {
type: 'ExpressionStatement',
expression: save
}, {
type: 'ExpressionStatement',
expression: {
type: 'AssignmentExpression',
operator: '=',
left: res,
right: expr
}
}, {
type: 'ExpressionStatement',
expression: restore
}, {
type: 'ReturnStatement',
argument: res
}]
}
}
}
};
};

Expand Down

0 comments on commit aee8aac

Please sign in to comment.