Skip to content

Commit

Permalink
runtime: invalidate nested applyNext()
Browse files Browse the repository at this point in the history
Fix: #71
  • Loading branch information
indutny committed Jul 23, 2015
1 parent 038303a commit 1140426
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 3 deletions.
2 changes: 1 addition & 1 deletion bench/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"author": "",
"license": "MIT",
"dependencies": {
"bem-xjst": "git+ssh://github.com/bem/bem-xjst.git#51dee47",
"bem-xjst": "git+ssh://github.com/bem/bem-xjst.git#038303a",
"benchmark": "^1.0.0",
"browserify": "^11.0.0",
"microtime": "^1.4.2",
Expand Down
8 changes: 8 additions & 0 deletions lib/bemhtml/runtime/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ function BEMHTML() {
inherits(this.contextConstructor, Context);
this.context = null;

// Execution depth, used to invalidate `applyNext` bitfields
this.depth = 0;

// oninit templates
this.oninit = null;

Expand Down Expand Up @@ -179,6 +182,7 @@ BEMHTML.prototype.run = function run(json) {

this.match = null;
this.context = new this.contextConstructor(this);
this.depth = 0;
var res = this._run(json);

this.match = match;
Expand Down Expand Up @@ -264,6 +268,9 @@ BEMHTML.prototype.runOne = function runOne(json) {
else
context._listLength--;

// To invalidate `applyNext` flags
this.depth++;

var key;
if (elem === undefined)
key = block;
Expand All @@ -282,6 +289,7 @@ BEMHTML.prototype.runOne = function runOne(json) {
context.elem = oldElem;
context.mods = oldMods;
context.elemMods = oldElemMods;
this.depth--;

return res;
};
Expand Down
43 changes: 41 additions & 2 deletions lib/bemhtml/runtime/match.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,12 @@ function Match(entity) {
// applyNext mask
this.mask = [ 0 ];

// We are going to create copies of mask for nested `applyNext()`
this.maskSize = 0;
this.maskOffset = 0;

this.count = 0;
this.depth = -1;
}
exports.Match = Match;

Expand All @@ -103,6 +108,7 @@ Match.prototype.clone = function clone(entity) {

res.templates = this.templates.slice();
res.mask = this.mask.slice();
res.maskSize = this.maskSize;
res.count = this.count;

return res;
Expand All @@ -114,6 +120,8 @@ Match.prototype.prepend = function prepend(other) {

while (Math.ceil(this.count / 31) > this.mask.length)
this.mask.push(0);

this.maskSize = this.mask.length;
};

Match.prototype.push = function push(template) {
Expand All @@ -122,12 +130,16 @@ Match.prototype.push = function push(template) {

if (Math.ceil(this.count / 31) > this.mask.length)
this.mask.push(0);

this.maskSize = this.mask.length;
};

Match.prototype.exec = function exec(context) {
var save = this.checkDepth();

var template;
var mask = this.mask[0];
var bitIndex = 0;
var bitIndex = this.maskOffset;
var mask = this.mask[bitIndex];
var bit = 1;
for (var i = 0; i < this.count; i++) {
if ((mask & bit) === 0) {
Expand Down Expand Up @@ -170,6 +182,33 @@ Match.prototype.exec = function exec(context) {

this.mask[bitIndex] = oldMask;
this.bemhtml.match = oldMatch;
this.restoreDepth(save);

return out;
};

Match.prototype.checkDepth = function checkDepth() {
if (this.depth === -1) {
this.depth = this.bemhtml.depth;
return -1;
}

if (this.bemhtml.depth === this.depth)
return this.depth;

var depth = this.depth;
this.depth = this.bemhtml.depth;

this.maskOffset += this.maskSize;

while (this.mask.length < this.maskOffset + this.maskSize)
this.mask.push(0);

return depth;
};

Match.prototype.restoreDepth = function restoreDepth(depth) {
if (depth !== -1 && depth !== this.depth)
this.maskOffset -= this.maskSize;
this.depth = depth;
};
23 changes: 23 additions & 0 deletions test/runtime-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,29 @@ describe('BEMHTML compiler/Runtime', function() {
}
}, { block: 'b1' }, '<div class="b1">ok</div>');
});

it('should support recursive applyNext() over block boundary', function() {
test(function() {
block('b1').tag()('a');
block('b1').bem()(false);
block('b1').def()(function() {
return '[' + applyNext() + ':' + applyNext() + ']';
});
}, {
block: 'b1',
content: {
block: 'b1',
content: {
block: 'b1',
content: 'ok'
}
}
}, '[<a>' +
'[<a>[<a>ok</a>:<a>ok</a>]</a>:<a>[<a>ok</a>:<a>ok</a>]</a>]' +
'</a>:<a>' +
'[<a>[<a>ok</a>:<a>ok</a>]</a>:<a>[<a>ok</a>:<a>ok</a>]</a>]' +
'</a>]');
});
});

describe('applyCtx()', function() {
Expand Down

0 comments on commit 1140426

Please sign in to comment.