Simplify or optimize regexes with polynomial time worst cases (#44197)

* Simplify or optimize regexes with polynomial time worst cases

* PR feedback & cleanup

Co-authored-by: David Michon <dmichon-msft@users.noreply.github.com>

* Use builtin scanner function for checking whitespace in fallback method (its faster)

Co-authored-by: David Michon <dmichon-msft@users.noreply.github.com>
This commit is contained in:
Wesley Wigham
2021-05-24 15:28:52 -07:00
committed by GitHub
parent 2203228b62
commit fcabb5c0cc
11 changed files with 158 additions and 85 deletions

View File

@@ -806,7 +806,8 @@ namespace ts {
function tryClassifyTripleSlashComment(start: number, width: number): boolean {
const tripleSlashXMLCommentRegEx = /^(\/\/\/\s*)(<)(?:(\S+)((?:[^/]|\/[^>])*)(\/>)?)?/im;
const attributeRegex = /(\S+)(\s*)(=)(\s*)('[^']+'|"[^"]+")/img;
// Require a leading whitespace character (the parser already does) to prevent terrible backtracking performance
const attributeRegex = /(\s)(\S+)(\s*)(=)(\s*)('[^']+'|"[^"]+")/img;
const text = sourceFile.text.substr(start, width);
const match = tripleSlashXMLCommentRegEx.exec(text);
@@ -842,30 +843,30 @@ namespace ts {
break;
}
const newAttrPos = pos + attrMatch.index;
const newAttrPos = pos + attrMatch.index + attrMatch[1].length; // whitespace
if (newAttrPos > attrPos) {
pushCommentRange(attrPos, newAttrPos - attrPos);
attrPos = newAttrPos;
}
pushClassification(attrPos, attrMatch[1].length, ClassificationType.jsxAttribute); // attribute name
attrPos += attrMatch[1].length;
pushClassification(attrPos, attrMatch[2].length, ClassificationType.jsxAttribute); // attribute name
attrPos += attrMatch[2].length;
if (attrMatch[2].length) {
pushCommentRange(attrPos, attrMatch[2].length); // whitespace
attrPos += attrMatch[2].length;
if (attrMatch[3].length) {
pushCommentRange(attrPos, attrMatch[3].length); // whitespace
attrPos += attrMatch[3].length;
}
pushClassification(attrPos, attrMatch[3].length, ClassificationType.operator); // =
attrPos += attrMatch[3].length;
pushClassification(attrPos, attrMatch[4].length, ClassificationType.operator); // =
attrPos += attrMatch[4].length;
if (attrMatch[4].length) {
pushCommentRange(attrPos, attrMatch[4].length); // whitespace
attrPos += attrMatch[4].length;
if (attrMatch[5].length) {
pushCommentRange(attrPos, attrMatch[5].length); // whitespace
attrPos += attrMatch[5].length;
}
pushClassification(attrPos, attrMatch[5].length, ClassificationType.jsxAttributeStringLiteralValue); // attribute value
attrPos += attrMatch[5].length;
pushClassification(attrPos, attrMatch[6].length, ClassificationType.jsxAttributeStringLiteralValue); // attribute value
attrPos += attrMatch[6].length;
}
pos += match[4].length;

View File

@@ -94,8 +94,15 @@ namespace ts.OutliningElementsCollector {
}
}
const regionDelimiterRegExp = /^\s*\/\/\s*#(end)?region(?:\s+(.*))?(?:\r)?$/;
const regionDelimiterRegExp = /^#(end)?region(?:\s+(.*))?(?:\r)?$/;
function isRegionDelimiter(lineText: string) {
// We trim the leading whitespace and // without the regex since the
// multiple potential whitespace matches can make for some gnarly backtracking behavior
lineText = trimStringStart(lineText);
if (!startsWith(lineText, "\/\/")) {
return null; // eslint-disable-line no-null/no-null
}
lineText = trimString(lineText.slice(2));
return regionDelimiterRegExp.exec(lineText);
}