fixed attribute value bottleneck in parser

pull/7663/head
Mathias Picker 4 years ago
parent 9acef10d48
commit daede43653

@ -9,6 +9,9 @@ import read_script from '../read/script';
import read_style from '../read/style'; import read_style from '../read/style';
import { closing_tag_omitted, decode_character_references } from '../utils/html'; import { closing_tag_omitted, decode_character_references } from '../utils/html';
// characters equivalent to using \s in regex
const whitespace_characters = ['\r', '\n', '\t', '\f', '\v', ' ', '\u00a0', '\u1680', '\u2000-', '\u200a', '\u2028', '\u2029', '\u202f', '\u205f', '\u3000', '\ufeff'];
// eslint-disable-next-line no-useless-escape // eslint-disable-next-line no-useless-escape
const valid_tag_name = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/; const valid_tag_name = /^\!?[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/;
@ -77,6 +80,7 @@ export default function tag(parser: Parser) {
const name = read_tag_name(parser); const name = read_tag_name(parser);
if (meta_tags.has(name)) { if (meta_tags.has(name)) {
const slug = meta_tags.get(name).toLowerCase(); const slug = meta_tags.get(name).toLowerCase();
if (is_closing_tag) { if (is_closing_tag) {
@ -109,6 +113,7 @@ export default function tag(parser: Parser) {
: name === 'title' && parent_is_head(parser.stack) ? 'Title' : name === 'title' && parent_is_head(parser.stack) ? 'Title'
: name === 'slot' && !parser.customElement ? 'Slot' : 'Element'; : name === 'slot' && !parser.customElement ? 'Slot' : 'Element';
const element: TemplateNode = { const element: TemplateNode = {
start, start,
end: null, // filled in later end: null, // filled in later
@ -160,8 +165,9 @@ export default function tag(parser: Parser) {
}; };
} }
const unique_names: Set<string> = new Set(); const unique_names: Set<string> = new Set();
let attribute; let attribute;
while ((attribute = read_attribute(parser, unique_names))) { while ((attribute = read_attribute(parser, unique_names))) {
element.attributes.push(attribute); element.attributes.push(attribute);
@ -363,6 +369,7 @@ function read_attribute(parser: Parser, unique_names: Set<string>) {
parser.error(parser_errors.unexpected_token('='), parser.index); parser.error(parser_errors.unexpected_token('='), parser.index);
} }
if (type) { if (type) {
const [directive_name, ...modifiers] = name.slice(colon_index + 1).split('|'); const [directive_name, ...modifiers] = name.slice(colon_index + 1).split('|');
@ -455,6 +462,7 @@ function get_directive_type(name: string): DirectiveType {
function read_attribute_value(parser: Parser) { function read_attribute_value(parser: Parser) {
const quote_mark = parser.eat("'") ? "'" : parser.eat('"') ? '"' : null; const quote_mark = parser.eat("'") ? "'" : parser.eat('"') ? '"' : null;
if (quote_mark && parser.eat(quote_mark)) { if (quote_mark && parser.eat(quote_mark)) {
return [{ return [{
start: parser.index - 1, start: parser.index - 1,
@ -465,15 +473,10 @@ function read_attribute_value(parser: Parser) {
}]; }];
} }
const regex = (
quote_mark === "'" ? /'/ :
quote_mark === '"' ? /"/ :
/(\/>|[\s"'=<>`])/
);
let value; let value;
try { try {
value = read_sequence(parser, () => !!parser.match_regex(regex)); const characters = quote_mark ? [quote_mark] : [...whitespace_characters, '/>', '"', '"', '=', '<', '>', '`'];
value = read_sequence(parser, () => characters.some(c => parser.match(c)));
} catch (error) { } catch (error) {
if (error.code === 'parse-error') { if (error.code === 'parse-error') {
// if the attribute value didn't close + self-closing tag // if the attribute value didn't close + self-closing tag

@ -0,0 +1,10 @@
export default {
skip_if_hydrate_from_ssr: true,
skip_if_hydrate: true,
skip_if_ssr: true,
test({ assert, target }) {
const input = target.querySelector('input');
assert.equal(input.value.length > 100_000, true);
}
};

File diff suppressed because one or more lines are too long

@ -5,6 +5,6 @@ export default {
test({ assert, target }) { test({ assert, target }) {
const image = target.querySelector('image'); const image = target.querySelector('image');
assert.equal(image.tagName, "IMAGE"); assert.equal(image.tagName, 'IMAGE');
} }
}; };

@ -0,0 +1,10 @@
export default {
skip_if_hydrate_from_ssr: true,
skip_if_hydrate: true,
skip_if_ssr: true,
test({ assert, target }) {
const textarea = target.querySelector('textarea');
assert.equal(textarea.value.length > 100_000, true);
}
};

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save