Cache compiled regexps

This commit is contained in:
tophf 2017-03-26 10:19:47 +03:00
parent 2ef87e003d
commit 78c56352cb
2 changed files with 52 additions and 11 deletions

View File

@ -54,6 +54,7 @@ globals:
getApplicableSections: false getApplicableSections: false
isCheckbox: false isCheckbox: false
runTryCatch: false runTryCatch: false
tryRegExp: false
setupLivePrefs: false setupLivePrefs: false
getCodeMirrorThemes: false getCodeMirrorThemes: false
styleSectionsEqual: false styleSectionsEqual: false

View File

@ -36,6 +36,7 @@ var cachedStyles, prefs;
noCode: null, noCode: null,
byId: new Map(), byId: new Map(),
filters: new Map(), filters: new Map(),
regexps: new Map(),
mutex: { mutex: {
inProgress: false, inProgress: false,
onDone: [], onDone: [],
@ -87,9 +88,10 @@ function getStyles(options, callback) {
const noCode = getStyleWithNoCode(style); const noCode = getStyleWithNoCode(style);
cachedStyles.noCode.push(noCode); cachedStyles.noCode.push(noCode);
cachedStyles.byId.set(style.id, {style, noCode}); cachedStyles.byId.set(style.id, {style, noCode});
compileStyleRegExps(style);
} }
//console.log('%s getStyles %s, invoking cached callbacks: %o', (performance.now() - t0).toFixed(1), JSON.stringify(options), cachedStyles.mutex.onDone.map(e => JSON.stringify(e.options))) //console.log('%s getStyles %s, invoking cached callbacks: %o', (performance.now() - t0).toFixed(1), JSON.stringify(options), cachedStyles.mutex.onDone.map(e => JSON.stringify(e.options)))
runTryCatch(callback, filterStyles(options)); callback(filterStyles(options));
cachedStyles.mutex.inProgress = false; cachedStyles.mutex.inProgress = false;
for (const {options, callback} of cachedStyles.mutex.onDone) { for (const {options, callback} of cachedStyles.mutex.onDone) {
@ -290,6 +292,7 @@ function saveStyle(style) {
os.put(style).onsuccess = eventPut => { os.put(style).onsuccess = eventPut => {
style.id = style.id || eventPut.target.result; style.id = style.id || eventPut.target.result;
invalidateCache(notify, existed ? {updated: style} : {added: style}); invalidateCache(notify, existed ? {updated: style} : {added: style});
compileStyleRegExps(style);
if (notify) { if (notify) {
notifyAllTabs({ notifyAllTabs({
method: existed ? 'styleUpdated' : 'styleAdded', method: existed ? 'styleUpdated' : 'styleAdded',
@ -320,6 +323,7 @@ function saveStyle(style) {
// Give it the ID that was generated // Give it the ID that was generated
style.id = event.target.result; style.id = event.target.result;
invalidateCache(notify, {added: style}); invalidateCache(notify, {added: style});
compileStyleRegExps(style);
if (notify) { if (notify) {
notifyAllTabs({method: 'styleAdded', style, reason}); notifyAllTabs({method: 'styleAdded', style, reason});
} }
@ -417,9 +421,12 @@ function getType(o) {
const namespacePattern = /^\s*(@namespace[^;]+;\s*)+$/; const namespacePattern = /^\s*(@namespace[^;]+;\s*)+$/;
function getApplicableSections(style, url) { function getApplicableSections(style, url) {
const sections = style.sections.filter(function(section) { const sections = [];
return sectionAppliesToUrl(section, url); for (const section of style.sections) {
}); if (sectionAppliesToUrl(section, url)) {
sections.push(section);
}
}
// ignore if it's just namespaces // ignore if it's just namespaces
if (sections.length == 1 && namespacePattern.test(sections[0].code)) { if (sections.length == 1 && namespacePattern.test(sections[0].code)) {
return []; return [];
@ -458,13 +465,20 @@ function sectionAppliesToUrl(section, url) {
} }
} }
for (const regexp of section.regexps) { for (const regexp of section.regexps) {
// we want to match the full url, so add ^ and $ if not already present let rx = cachedStyles.regexps.get(regexp);
const prefix = regexp.charAt(0) != '^' && '^'; if (rx == false) {
const suffix = regexp.slice(-1) != '$' && '$'; // bad regexp
const re = runTryCatch(() => new RegExp(prefix + regexp + suffix)); continue;
if (!re) { }
console.warn('Regexp ' + regexp + ' is not valid'); if (!rx) {
} else if (re.test(url)) { rx = tryRegExp('^(?:' + regexp + ')$');
cachedStyles.regexps.set(regexp, rx || false);
if (!rx) {
// bad regexp
continue;
}
}
if (rx.test(url)) {
return true; return true;
} }
} }
@ -485,6 +499,14 @@ function runTryCatch(func, ...args) {
} catch (e) {} } catch (e) {}
} }
function tryRegExp(regexp) {
try {
return new RegExp(regexp);
} catch (e) {}
}
prefs = prefs || new function Prefs() { prefs = prefs || new function Prefs() {
const me = this; const me = this;
@ -845,3 +867,21 @@ function styleSectionsEqual(styleA, styleB) {
} }
return true; return true;
} }
function compileStyleRegExps(style) {
const t0 = performance.now();
for (const section of style.sections || []) {
for (const regexp of section.regexps) {
// we want to match the full url, so add ^ and $ if not already present
if (cachedStyles.regexps.has(regexp)) {
continue;
}
const rx = tryRegExp('^(?:' + regexp + ')$');
cachedStyles.regexps.set(regexp, rx || false);
if (performance.now() - t0 > 100) {
return;
}
}
}
}