Better handling of infinite recursion
But it could be better still...
This commit is contained in:
parent
bc89805368
commit
777176d7ec
|
@ -8,8 +8,9 @@ Custom errors for TiddlyWiki.
|
||||||
\*/
|
\*/
|
||||||
(function(){
|
(function(){
|
||||||
|
|
||||||
function TranscludeRecursionError(transcludeMarker) {
|
function TranscludeRecursionError(depth) {
|
||||||
this.marker = transcludeMarker;
|
this.depth = depth;
|
||||||
|
this.signatures = Object.create(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.TranscludeRecursionError = TranscludeRecursionError;
|
exports.TranscludeRecursionError = TranscludeRecursionError;
|
||||||
|
|
|
@ -38,7 +38,13 @@ TranscludeWidget.prototype.render = function(parent,nextSibling) {
|
||||||
// We need to try and abort as much of the loop as we can, so we will keep "throwing" upward until we find a transclusion that has a different signature.
|
// We need to try and abort as much of the loop as we can, so we will keep "throwing" upward until we find a transclusion that has a different signature.
|
||||||
// Hopefully that will land us just outside where the loop began. That's where we want to issue an error.
|
// Hopefully that will land us just outside where the loop began. That's where we want to issue an error.
|
||||||
// Rendering widgets beneath this point may result in a freezing browser if they explode exponentially.
|
// Rendering widgets beneath this point may result in a freezing browser if they explode exponentially.
|
||||||
if(error.marker !== this.getVariable("transclusion")) {
|
var transcludeSignature = this.getVariable("transclusion");
|
||||||
|
if(this.getAncestorCount() > error.depth - 50) {
|
||||||
|
// For the first fifty transcludes we climb up, we simply collect signatures.
|
||||||
|
// We're assuming that those first 50 will likely include all transcludes involved in the loop.
|
||||||
|
error.signatures[transcludeSignature] = true;
|
||||||
|
} else if(!error.signatures[transcludeSignature]) {
|
||||||
|
// Now that we're past the first 50, let's look for the first signature that wasn't in the loop. That'll be where we print the error and resume rendering.
|
||||||
this.children = [this.makeChildWidget({type: "error", attributes: {
|
this.children = [this.makeChildWidget({type: "error", attributes: {
|
||||||
"$message": {type: "string", value: $tw.language.getString("Error/RecursiveTransclusion")}
|
"$message": {type: "string", value: $tw.language.getString("Error/RecursiveTransclusion")}
|
||||||
}})];
|
}})];
|
||||||
|
|
|
@ -495,7 +495,7 @@ Widget.prototype.makeChildWidgets = function(parseTreeNodes,options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
// Check for too much recursion
|
// Check for too much recursion
|
||||||
if(this.getAncestorCount() > MAX_WIDGET_TREE_DEPTH) {
|
if(this.getAncestorCount() > MAX_WIDGET_TREE_DEPTH) {
|
||||||
throw new $tw.utils.TranscludeRecursionError(this.getVariable("transclusion"));
|
throw new $tw.utils.TranscludeRecursionError(MAX_WIDGET_TREE_DEPTH);
|
||||||
} else {
|
} else {
|
||||||
// Create set variable widgets for each variable
|
// Create set variable widgets for each variable
|
||||||
$tw.utils.each(options.variables,function(value,name) {
|
$tw.utils.each(options.variables,function(value,name) {
|
||||||
|
|
|
@ -160,7 +160,7 @@ describe("Widget module", function() {
|
||||||
expect(wrapper.innerHTML).toBe("<span class=\"tc-error\">Recursive transclusion error in transclude widget</span>");
|
expect(wrapper.innerHTML).toBe("<span class=\"tc-error\">Recursive transclusion error in transclude widget</span>");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should handle recursion with branching nodes", function() {
|
it("should handle single-tiddler recursion with branching nodes", function() {
|
||||||
var wiki = new $tw.Wiki();
|
var wiki = new $tw.Wiki();
|
||||||
// Add a tiddler
|
// Add a tiddler
|
||||||
wiki.addTiddlers([
|
wiki.addTiddlers([
|
||||||
|
@ -180,6 +180,27 @@ describe("Widget module", function() {
|
||||||
expect(wrapper.innerHTML).toBe("<span class=\"tc-error\">Recursive transclusion error in transclude widget</span> <span class=\"tc-error\">Recursive transclusion error in transclude widget</span>");
|
expect(wrapper.innerHTML).toBe("<span class=\"tc-error\">Recursive transclusion error in transclude widget</span> <span class=\"tc-error\">Recursive transclusion error in transclude widget</span>");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
fit("should handle many-tiddler recursion with branching nodes", function() {
|
||||||
|
var wiki = new $tw.Wiki();
|
||||||
|
// Add a tiddler
|
||||||
|
wiki.addTiddlers([
|
||||||
|
{title: "TiddlerOne", text: "<$transclude tiddler='TiddlerTwo'/> <$transclude tiddler='TiddlerTwo'/>"},
|
||||||
|
{title: "TiddlerTwo", text: "<$transclude tiddler='TiddlerOne'/>"}
|
||||||
|
]);
|
||||||
|
// Test parse tree
|
||||||
|
var parseTreeNode = {type: "widget", children: [
|
||||||
|
{type: "transclude", attributes: {
|
||||||
|
"tiddler": {type: "string", value: "TiddlerOne"}
|
||||||
|
}}
|
||||||
|
]};
|
||||||
|
// Construct the widget node
|
||||||
|
var widgetNode = createWidgetNode(parseTreeNode,wiki);
|
||||||
|
// Render the widget node to the DOM
|
||||||
|
var wrapper = renderWidgetNode(widgetNode);
|
||||||
|
// Test the rendering
|
||||||
|
expect(wrapper.innerHTML).toBe("<span class=\"tc-error\">Recursive transclusion error in transclude widget</span>");
|
||||||
|
});
|
||||||
|
|
||||||
it("should deal with SVG elements", function() {
|
it("should deal with SVG elements", function() {
|
||||||
var wiki = new $tw.Wiki();
|
var wiki = new $tw.Wiki();
|
||||||
// Construct the widget node
|
// Construct the widget node
|
||||||
|
|
Loading…
Reference in New Issue