From 51143a88833e28f974d558c44b1ab215b7ff2798 Mon Sep 17 00:00:00 2001 From: CR Drost Date: Tue, 21 Nov 2017 16:45:52 -0600 Subject: [PATCH 1/2] Fixes #149 by adding a 30-pass limit to recursion and stopping immediately on loops. --- src/compiler-host.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/compiler-host.js b/src/compiler-host.js index d602004..345ec43 100644 --- a/src/compiler-host.js +++ b/src/compiler-host.js @@ -323,7 +323,7 @@ export default class CompilerHost { * * @private */ - async compileUncached(filePath, hashInfo, compiler) { + async compileUncached(filePath, hashInfo, compiler, history=[]) { let inputMimeType = mimeTypes.lookup(filePath); if (hashInfo.isFileBinary) { @@ -336,6 +336,7 @@ export default class CompilerHost { let ctx = {}; let code = hashInfo.sourceCode || await pfs.readFile(filePath, 'utf8'); + history.push(code); if (!(await compiler.shouldCompileFile(code, ctx))) { d(`Compiler returned false for shouldCompileFile: ${filePath}`); @@ -359,6 +360,12 @@ export default class CompilerHost { if ((finalForms[result.mimeType] && !shouldInlineHtmlify) || isPassthrough) { // Got something we can use in-browser, let's return it return Object.assign(result, {dependentFiles}); + } else if (history.indexOf(result.code) !== -1) { + d(`Compiler loop, I have seen this source before: ${JSON.stringify(result)}`); + return Object.assign(result, {dependentFiles}); + } else if (history.length > 30) { + d(`Runaway compilation: ${JSON.stringify({history: history, result: result})}`); + throw new Error(`Compiling ${filePath} resulted in a recursive recompilation more than 30 levels deep; assuming broken...`); } else { d(`Recursively compiling result of ${filePath} with non-final MIME type ${result.mimeType}, input was ${inputMimeType}`); @@ -373,7 +380,7 @@ export default class CompilerHost { return await this.compileUncached( `${filePath}.${mimeTypes.extension(result.mimeType || 'txt')}`, - hashInfo, compiler); + hashInfo, compiler, history); } } From 407ff4ba2875396bdafcee88a9bf7d900cfaa085 Mon Sep 17 00:00:00 2001 From: CR Drost Date: Tue, 21 Nov 2017 17:13:35 -0600 Subject: [PATCH 2/2] Change [] to set, log less stuff. --- src/compiler-host.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/compiler-host.js b/src/compiler-host.js index 345ec43..7ba61e2 100644 --- a/src/compiler-host.js +++ b/src/compiler-host.js @@ -323,7 +323,7 @@ export default class CompilerHost { * * @private */ - async compileUncached(filePath, hashInfo, compiler, history=[]) { + async compileUncached(filePath, hashInfo, compiler, history=new Set()) { let inputMimeType = mimeTypes.lookup(filePath); if (hashInfo.isFileBinary) { @@ -336,7 +336,7 @@ export default class CompilerHost { let ctx = {}; let code = hashInfo.sourceCode || await pfs.readFile(filePath, 'utf8'); - history.push(code); + history.add(code); if (!(await compiler.shouldCompileFile(code, ctx))) { d(`Compiler returned false for shouldCompileFile: ${filePath}`); @@ -360,12 +360,11 @@ export default class CompilerHost { if ((finalForms[result.mimeType] && !shouldInlineHtmlify) || isPassthrough) { // Got something we can use in-browser, let's return it return Object.assign(result, {dependentFiles}); - } else if (history.indexOf(result.code) !== -1) { - d(`Compiler loop, I have seen this source before: ${JSON.stringify(result)}`); + } else if (history.has(result.code)) { + d(`Compiler loop on ${filePath} after ${history.size - 1} prior candidates: assuming OK.`); return Object.assign(result, {dependentFiles}); - } else if (history.length > 30) { - d(`Runaway compilation: ${JSON.stringify({history: history, result: result})}`); - throw new Error(`Compiling ${filePath} resulted in a recursive recompilation more than 30 levels deep; assuming broken...`); + } else if (history.size > 30) { + throw new Error(`Compiling ${filePath} resulted in a recursive recompilation more than 30 levels deep: assuming broken.`); } else { d(`Recursively compiling result of ${filePath} with non-final MIME type ${result.mimeType}, input was ${inputMimeType}`);