From 8b202287cd2fd957b48ba182218a239833916bab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20B=C3=B6hm?= <188768+fb55@users.noreply.github.com> Date: Fri, 12 Nov 2021 17:25:10 +0000 Subject: [PATCH] refactor: Use decorators for even faster parsing This leads to another ~5% speed-up. Not sure if it is worth it though, as the code does get quite a bit less understandable. --- src/Tokenizer.ts | 465 +++++++++++++++++++++++++++-------------------- tsconfig.json | 2 + 2 files changed, 272 insertions(+), 195 deletions(-) diff --git a/src/Tokenizer.ts b/src/Tokenizer.ts index 5354aa493..91289e96c 100644 --- a/src/Tokenizer.ts +++ b/src/Tokenizer.ts @@ -37,7 +37,7 @@ const enum CharCodes { } /** All the states the tokenizer can be in. */ -const enum State { +enum State { Text = 1, BeforeTagName, // After < InTagName, @@ -51,9 +51,9 @@ const enum State { InAttributeName, AfterAttributeName, BeforeAttributeValue, - InAttributeValueDq, // " - InAttributeValueSq, // ' - InAttributeValueNq, + InAttributeValueDoubleQuotes, // " + InAttributeValueSingleQuotes, // ' + InAttributeValueNoQuotes, // Declarations BeforeDeclaration, // ! @@ -139,9 +139,70 @@ const Sequences = { TitleEnd: new Uint16Array([0x3c, 0x2f, 0x74, 0x69, 0x74, 0x6c, 0x65]), // ` void = + descriptor.value; + + descriptor.value = function (this: Tokenizer, c: number | null) { + if (c !== null) { + originalMethod.call(this, c); + } else { + this.state = state; + } + }; + + return descriptor; + }; +} + +function stateLoop() { + return function ( + _target: Tokenizer, + key: TokenizerStateMethod, + descriptor: PropertyDescriptor + ) { + const state = fnToState(key); + + const originalMethod: (this: Tokenizer, c: number) => void = + descriptor.value; + + descriptor.value = function loop(this: Tokenizer, c: number | null) { + for (this.state = state; c !== null; c = this._next()) { + originalMethod.call(this, c); + + if (this.state !== state) { + return; + } + } + }; + + return descriptor; + }; +} + export default class Tokenizer { /** The current state the tokenizer is in. */ - private _state = State.Text; + state = State.Text; /** The read buffer. */ private buffer = ""; /** The beginning of the section that is currently being read. */ @@ -179,7 +240,7 @@ export default class Tokenizer { } public reset(): void { - this._state = State.Text; + this.state = State.Text; this.buffer = ""; this.sectionStart = 0; this._index = 0; @@ -231,24 +292,29 @@ export default class Tokenizer { return this.bufferOffset + this._index; } - private stateText(c: number) { - if ( - c === CharCodes.Lt || - (!this.decodeEntities && this.fastForwardTo(CharCodes.Lt)) - ) { + _next(): number | null { + return this.running && this._index + 1 < this.buffer.length + ? this.buffer.charCodeAt(++this._index) + : null; + } + + @stateLoop() + private stateText(c: number): void { + if (c === CharCodes.Lt) { if (this._index > this.sectionStart) { this.cbs.ontext(this.getSection()); } - this._state = State.BeforeTagName; this.sectionStart = this._index; + this.stateBeforeTagName(this._next()!); } else if (this.decodeEntities && c === CharCodes.Amp) { - this._state = State.BeforeEntity; + this.stateBeforeEntity(this._next()!); } } private currentSequence!: Uint16Array; private sequenceIndex = 0; - private stateSpecialStartSequence(c: number) { + @stateLoop() + private stateSpecialStartSequence(c: number): void { const isEnd = this.sequenceIndex === this.currentSequence.length; const isMatch = isEnd ? // If we are at the end of the sequence, make sure the tag name has ended @@ -264,12 +330,12 @@ export default class Tokenizer { } this.sequenceIndex = 0; - this._state = State.InTagName; this.stateInTagName(c); } /** Look for an end tag. For tags, also decode entities. */ - private stateInSpecialTag(c: number) { + @stateLoop() + private stateInSpecialTag(c: number): void { if (this.sequenceIndex === this.currentSequence.length) { if (c === CharCodes.Gt || isWhitespace(c)) { const endOfText = this._index - this.currentSequence.length; @@ -297,11 +363,8 @@ export default class Tokenizer { if (this.currentSequence === Sequences.TitleEnd) { // We have to parse entities in <title> tags. if (this.decodeEntities && c === CharCodes.Amp) { - this._state = State.BeforeEntity; + this.stateBeforeEntity(this._next()!); } - } else if (this.fastForwardTo(CharCodes.Lt)) { - // Outside of <title> tags, we can fast-forward. - this.sequenceIndex = 1; } } else { // If we see a `<`, set the sequence index to 1; useful for eg. `<</script>`. @@ -309,45 +372,21 @@ export default class Tokenizer { } } - private stateCDATASequence(c: number) { + @stateLoop() + private stateCDATASequence(c: number): void { if (c === Sequences.Cdata[this.sequenceIndex]) { if (++this.sequenceIndex === Sequences.Cdata.length) { - this._state = State.InCommentLike; this.currentSequence = Sequences.CdataEnd; this.sequenceIndex = 0; this.sectionStart = this._index + 1; + this.stateInCommentLike(this._next()!); } } else { this.sequenceIndex = 0; - this._state = State.InDeclaration; this.stateInDeclaration(c); // Reconsume the character } } - /** - * When we wait for one specific character, we can speed things up - * by skipping through the buffer until we find it. - * - * @returns Whether the character was found. - */ - private fastForwardTo(c: number): boolean { - while (++this._index < this.buffer.length) { - if (this.buffer.charCodeAt(this._index) === c) { - return true; - } - } - - /* - * We increment the index at the end of the `parse` loop, - * so set it to `buffer.length - 1` here. - * - * TODO: Refactor `parse` to increment index before calling states. - */ - this._index = this.buffer.length - 1; - - return false; - } - /** * Comments and CDATA end with `-->` and `]]>`. * @@ -356,7 +395,8 @@ export default class Tokenizer { * - That character is then repeated, so we have to check multiple repeats. * - All characters but the start character of the sequence can be skipped. */ - private stateInCommentLike(c: number) { + @stateLoop() + private stateInCommentLike(c: number): void { if (c === this.currentSequence[this.sequenceIndex]) { if (++this.sequenceIndex === this.currentSequence.length) { // Remove 2 trailing chars @@ -373,14 +413,12 @@ export default class Tokenizer { this.sequenceIndex = 0; this.sectionStart = this._index + 1; - this._state = State.Text; - } - } else if (this.sequenceIndex === 0) { - // Fast-forward to the first character of the sequence - if (this.fastForwardTo(this.currentSequence[0])) { - this.sequenceIndex = 1; + this.state = State.Text; } - } else if (c !== this.currentSequence[this.sequenceIndex - 1]) { + } else if ( + this.sequenceIndex !== 0 && + c !== this.currentSequence[this.sequenceIndex - 1] + ) { // Allow long sequences, eg. --->, ]]]> this.sequenceIndex = 0; } @@ -400,216 +438,253 @@ export default class Tokenizer { this.isSpecial = true; this.currentSequence = sequence; this.sequenceIndex = offset; - this._state = State.SpecialStartSequence; + this.stateSpecialStartSequence(this._next()!); } - private stateBeforeTagName(c: number) { + @state() + private stateBeforeTagName(c: number): void { if (c === CharCodes.ExclamationMark) { - this._state = State.BeforeDeclaration; this.sectionStart = this._index + 1; + this.stateBeforeDeclaration(this._next()!); } else if (c === CharCodes.Questionmark) { - this._state = State.InProcessingInstruction; this.sectionStart = this._index + 1; + this.stateInProcessingInstruction(this._next()!); } else if (this.isTagStartChar(c)) { const lower = c | 0x20; this.sectionStart = this._index; if (!this.xmlMode && lower === Sequences.TitleEnd[2]) { this.startSpecial(Sequences.TitleEnd, 3); + } else if (!this.xmlMode && lower === Sequences.ScriptEnd[2]) { + this.stateBeforeSpecialS(this._next()!); } else { - this._state = - !this.xmlMode && lower === Sequences.ScriptEnd[2] - ? State.BeforeSpecialS - : State.InTagName; + this.stateInTagName(this._next()!); } } else if (c === CharCodes.Slash) { - this._state = State.BeforeClosingTagName; + this.stateBeforeClosingTagName(this._next()!); } else { - this._state = State.Text; this.stateText(c); } } - private stateInTagName(c: number) { + + @stateLoop() + private stateInTagName(c: number): void { if (isEndOfTagSection(c)) { this.cbs.onopentagname(this.getSection()); this.sectionStart = -1; - this._state = State.BeforeAttributeName; this.stateBeforeAttributeName(c); } } - private stateBeforeClosingTagName(c: number) { + + @stateLoop() + private stateBeforeClosingTagName(c: number): void { if (isWhitespace(c)) { // Ignore } else if (c === CharCodes.Gt) { - this._state = State.Text; + this.state = State.Text; } else { - this._state = this.isTagStartChar(c) - ? State.InClosingTagName - : State.InSpecialComment; this.sectionStart = this._index; + if (this.isTagStartChar(c)) { + this.stateInClosingTagName(this._next()!); + } else { + this.stateInSpecialComment(this._next()!); + } } } - private stateInClosingTagName(c: number) { + + @stateLoop() + private stateInClosingTagName(c: number): void { if (c === CharCodes.Gt || isWhitespace(c)) { this.cbs.onclosetag(this.getSection()); this.sectionStart = -1; - this._state = State.AfterClosingTagName; this.stateAfterClosingTagName(c); } } - private stateAfterClosingTagName(c: number) { + + @stateLoop() + private stateAfterClosingTagName(c: number): void { // Skip everything until ">" - if (c === CharCodes.Gt || this.fastForwardTo(CharCodes.Gt)) { - this._state = State.Text; + if (c === CharCodes.Gt) { this.sectionStart = this._index + 1; + this.state = State.Text; } } - private stateBeforeAttributeName(c: number) { + + @stateLoop() + private stateBeforeAttributeName(c: number): void { if (c === CharCodes.Gt) { this.cbs.onopentagend(); + + this.sectionStart = this._index + 1; + if (this.isSpecial) { - this._state = State.InSpecialTag; this.sequenceIndex = 0; + this.baseState = State.InSpecialTag; + this.stateInSpecialTag(this._next()!); } else { - this._state = State.Text; + this.baseState = State.Text; + this.state = State.Text; } - this.baseState = this._state; - this.sectionStart = this._index + 1; } else if (c === CharCodes.Slash) { - this._state = State.InSelfClosingTag; + this.stateInSelfClosingTag(this._next()!); } else if (!isWhitespace(c)) { - this._state = State.InAttributeName; this.sectionStart = this._index; + this.stateInAttributeName(this._next()!); } } - private stateInSelfClosingTag(c: number) { + + @stateLoop() + private stateInSelfClosingTag(c: number): void { if (c === CharCodes.Gt) { this.cbs.onselfclosingtag(); - this._state = State.Text; this.baseState = State.Text; this.sectionStart = this._index + 1; this.isSpecial = false; // Reset special state, in case of self-closing special tags + this.state = State.Text; } else if (!isWhitespace(c)) { - this._state = State.BeforeAttributeName; - this.stateBeforeAttributeName(c); + this.state = State.BeforeAttributeName; + this._index--; } } - private stateInAttributeName(c: number) { + + @stateLoop() + private stateInAttributeName(c: number): void { if (c === CharCodes.Eq || isEndOfTagSection(c)) { this.cbs.onattribname(this.getSection()); this.sectionStart = -1; - this._state = State.AfterAttributeName; this.stateAfterAttributeName(c); } } - private stateAfterAttributeName(c: number) { + + @stateLoop() + private stateAfterAttributeName(c: number): void { if (c === CharCodes.Eq) { - this._state = State.BeforeAttributeValue; + this.stateBeforeAttributeValue(this._next()!); } else if (c === CharCodes.Slash || c === CharCodes.Gt) { this.cbs.onattribend(undefined); - this._state = State.BeforeAttributeName; - this.stateBeforeAttributeName(c); + this.state = State.BeforeAttributeName; + this._index--; } else if (!isWhitespace(c)) { this.cbs.onattribend(undefined); - this._state = State.InAttributeName; this.sectionStart = this._index; + this.state = State.InAttributeName; } } - private stateBeforeAttributeValue(c: number) { + + @stateLoop() + private stateBeforeAttributeValue(c: number): void { if (c === CharCodes.DoubleQuote) { - this._state = State.InAttributeValueDq; this.sectionStart = this._index + 1; + this.stateInAttributeValueDoubleQuotes(this._next()!); } else if (c === CharCodes.SingleQuote) { - this._state = State.InAttributeValueSq; this.sectionStart = this._index + 1; + this.stateInAttributeValueSingleQuotes(this._next()!); } else if (!isWhitespace(c)) { this.sectionStart = this._index; - this._state = State.InAttributeValueNq; this.stateInAttributeValueNoQuotes(c); // Reconsume token } } private handleInAttributeValue(c: number, quote: number) { - if ( - c === quote || - (!this.decodeEntities && this.fastForwardTo(quote)) - ) { + if (c === quote) { this.cbs.onattribdata(this.getSection()); this.sectionStart = -1; this.cbs.onattribend(String.fromCharCode(quote)); - this._state = State.BeforeAttributeName; + this.state = State.BeforeAttributeName; } else if (this.decodeEntities && c === CharCodes.Amp) { - this.baseState = this._state; - this._state = State.BeforeEntity; + this.baseState = + quote === CharCodes.DoubleQuote + ? State.InAttributeValueDoubleQuotes + : State.InAttributeValueSingleQuotes; + this.stateBeforeEntity(this._next()!); } } - private stateInAttributeValueDoubleQuotes(c: number) { + + @stateLoop() + private stateInAttributeValueDoubleQuotes(c: number): void { this.handleInAttributeValue(c, CharCodes.DoubleQuote); } - private stateInAttributeValueSingleQuotes(c: number) { + + @stateLoop() + private stateInAttributeValueSingleQuotes(c: number): void { this.handleInAttributeValue(c, CharCodes.SingleQuote); } - private stateInAttributeValueNoQuotes(c: number) { + + @stateLoop() + private stateInAttributeValueNoQuotes(c: number): void { if (isWhitespace(c) || c === CharCodes.Gt) { this.cbs.onattribdata(this.getSection()); this.sectionStart = -1; this.cbs.onattribend(null); - this._state = State.BeforeAttributeName; - this.stateBeforeAttributeName(c); + this.state = State.BeforeAttributeName; + this._index--; } else if (this.decodeEntities && c === CharCodes.Amp) { - this.baseState = this._state; - this._state = State.BeforeEntity; + this.baseState = State.InAttributeValueNoQuotes; + this.stateBeforeEntity(this._next()!); } } - private stateBeforeDeclaration(c: number) { + + @state() + private stateBeforeDeclaration(c: number): void { if (c === CharCodes.OpeningSquareBracket) { - this._state = State.CDATASequence; + this.stateCDATASequence(this._next()!); this.sequenceIndex = 0; + } else if (c === CharCodes.Dash) { + this.stateBeforeComment(this._next()!); } else { - this._state = - c === CharCodes.Dash - ? State.BeforeComment - : State.InDeclaration; + this.stateInDeclaration(this._next()!); } } - private stateInDeclaration(c: number) { - if (c === CharCodes.Gt || this.fastForwardTo(CharCodes.Gt)) { + + @stateLoop() + private stateInDeclaration(c: number): void { + if (c === CharCodes.Gt) { this.cbs.ondeclaration(this.getSection()); - this._state = State.Text; + this.sectionStart = this._index + 1; + this.state = State.Text; } } - private stateInProcessingInstruction(c: number) { - if (c === CharCodes.Gt || this.fastForwardTo(CharCodes.Gt)) { + + @stateLoop() + private stateInProcessingInstruction(c: number): void { + if (c === CharCodes.Gt) { this.cbs.onprocessinginstruction(this.getSection()); - this._state = State.Text; + this.sectionStart = this._index + 1; + this.state = State.Text; } } - private stateBeforeComment(c: number) { + + @state() + private stateBeforeComment(c: number): void { if (c === CharCodes.Dash) { - this._state = State.InCommentLike; this.currentSequence = Sequences.CommentEnd; // Allow short comments (eg. <!-->) this.sequenceIndex = 2; this.sectionStart = this._index + 1; + this.stateInCommentLike(this._next()!); } else { - this._state = State.InDeclaration; + this.stateInDeclaration(this._next()!); } } - private stateInSpecialComment(c: number) { - if (c === CharCodes.Gt || this.fastForwardTo(CharCodes.Gt)) { + + @stateLoop() + private stateInSpecialComment(c: number): void { + if (c === CharCodes.Gt) { this.cbs.oncomment(this.getSection()); - this._state = State.Text; + this.sectionStart = this._index + 1; + this.state = State.Text; } } - private stateBeforeSpecialS(c: number) { + + @state() + private stateBeforeSpecialS(c: number): void { const lower = c | 0x20; if (lower === Sequences.ScriptEnd[3]) { this.startSpecial(Sequences.ScriptEnd, 4); } else if (lower === Sequences.StyleEnd[3]) { this.startSpecial(Sequences.StyleEnd, 4); } else { - this._state = State.InTagName; this.stateInTagName(c); // Consume the token again } } @@ -619,24 +694,25 @@ export default class Tokenizer { private trieResult: string | null = null; private entityExcess = 0; - private stateBeforeEntity(c: number) { + @state() + private stateBeforeEntity(c: number): void { // Start excess with 1 to include the '&' this.entityExcess = 1; if (c === CharCodes.Num) { - this._state = State.BeforeNumericEntity; + this.stateBeforeNumericEntity(this._next()!); } else if (c === CharCodes.Amp) { // We have two `&` characters in a row. Stay in the current state. } else { this.trieIndex = 0; this.trieCurrent = this.entityTrie[0]; this.trieResult = null; - this._state = State.InNamedEntity; this.stateInNamedEntity(c); } } - private stateInNamedEntity(c: number) { + @stateLoop() + private stateInNamedEntity(c: number): void { this.entityExcess += 1; this.trieIndex = determineBranch( @@ -691,15 +767,15 @@ export default class Tokenizer { this.emitPartial(this.trieResult); } - this._state = this.baseState; + this.state = this.baseState; } - private stateBeforeNumericEntity(c: number) { + @state() + private stateBeforeNumericEntity(c: number): void { if ((c | 0x20) === CharCodes.LowerX) { this.entityExcess++; - this._state = State.InHexEntity; + this.stateInHexEntity(this._next()!); } else { - this._state = State.InNumericEntity; this.stateInNumericEntity(c); } } @@ -722,23 +798,27 @@ export default class Tokenizer { this.emitPartial(decodeCodePoint(parsed)); this.sectionStart = this._index + Number(strict); } - this._state = this.baseState; + this.state = this.baseState; } - private stateInNumericEntity(c: number) { + + @stateLoop() + private stateInNumericEntity(c: number): void { if (c === CharCodes.Semi) { this.decodeNumericEntity(10, true); } else if (!isNumber(c)) { if (this.allowLegacyEntity()) { this.decodeNumericEntity(10, false); } else { - this._state = this.baseState; + this.state = this.baseState; } this._index--; } else { this.entityExcess++; } } - private stateInHexEntity(c: number) { + + @stateLoop() + private stateInHexEntity(c: number): void { if (c === CharCodes.Semi) { this.decodeNumericEntity(16, true); } else if ( @@ -749,7 +829,7 @@ export default class Tokenizer { if (this.allowLegacyEntity()) { this.decodeNumericEntity(16, false); } else { - this._state = this.baseState; + this.state = this.baseState; } this._index--; } else { @@ -773,9 +853,8 @@ export default class Tokenizer { if ( this.running && this.sectionStart !== this._index && - (this._state === State.Text || - (this._state === State.InSpecialTag && - this.sequenceIndex === 0)) + (this.state === State.Text || + (this.state === State.InSpecialTag && this.sequenceIndex === 0)) ) { // TODO: We could emit attribute data here as well. this.cbs.ontext(this.buffer.substr(this.sectionStart)); @@ -793,73 +872,69 @@ export default class Tokenizer { } } - private shouldContinue() { - return this._index < this.buffer.length && this.running; - } - /** * Iterates through the buffer, calling the function corresponding to the current state. * * States that are more likely to be hit are higher up, as a performance improvement. */ private parse() { - while (this.shouldContinue()) { + while (this.running && this._index < this.buffer.length) { const c = this.buffer.charCodeAt(this._index); - if (this._state === State.Text) { + if (this.state === State.Text) { this.stateText(c); - } else if (this._state === State.SpecialStartSequence) { + } else if (this.state === State.SpecialStartSequence) { this.stateSpecialStartSequence(c); - } else if (this._state === State.InSpecialTag) { + } else if (this.state === State.InSpecialTag) { this.stateInSpecialTag(c); - } else if (this._state === State.CDATASequence) { + } else if (this.state === State.CDATASequence) { this.stateCDATASequence(c); - } else if (this._state === State.InAttributeValueDq) { + } else if (this.state === State.InAttributeValueDoubleQuotes) { this.stateInAttributeValueDoubleQuotes(c); - } else if (this._state === State.InAttributeName) { + } else if (this.state === State.InAttributeName) { this.stateInAttributeName(c); - } else if (this._state === State.InCommentLike) { + } else if (this.state === State.InCommentLike) { this.stateInCommentLike(c); - } else if (this._state === State.InSpecialComment) { + } else if (this.state === State.InSpecialComment) { this.stateInSpecialComment(c); - } else if (this._state === State.BeforeAttributeName) { + } else if (this.state === State.BeforeAttributeName) { this.stateBeforeAttributeName(c); - } else if (this._state === State.InTagName) { + } else if (this.state === State.InTagName) { this.stateInTagName(c); - } else if (this._state === State.InClosingTagName) { + } else if (this.state === State.InClosingTagName) { this.stateInClosingTagName(c); - } else if (this._state === State.BeforeTagName) { + } else if (this.state === State.BeforeTagName) { this.stateBeforeTagName(c); - } else if (this._state === State.AfterAttributeName) { + } else if (this.state === State.AfterAttributeName) { this.stateAfterAttributeName(c); - } else if (this._state === State.InAttributeValueSq) { + } else if (this.state === State.InAttributeValueSingleQuotes) { this.stateInAttributeValueSingleQuotes(c); - } else if (this._state === State.BeforeAttributeValue) { + } else if (this.state === State.BeforeAttributeValue) { this.stateBeforeAttributeValue(c); - } else if (this._state === State.BeforeClosingTagName) { + } else if (this.state === State.BeforeClosingTagName) { this.stateBeforeClosingTagName(c); - } else if (this._state === State.AfterClosingTagName) { + } else if (this.state === State.AfterClosingTagName) { this.stateAfterClosingTagName(c); - } else if (this._state === State.BeforeSpecialS) { + } else if (this.state === State.BeforeSpecialS) { this.stateBeforeSpecialS(c); - } else if (this._state === State.InAttributeValueNq) { + } else if (this.state === State.InAttributeValueNoQuotes) { this.stateInAttributeValueNoQuotes(c); - } else if (this._state === State.InSelfClosingTag) { + } else if (this.state === State.InSelfClosingTag) { this.stateInSelfClosingTag(c); - } else if (this._state === State.InDeclaration) { + } else if (this.state === State.InDeclaration) { this.stateInDeclaration(c); - } else if (this._state === State.BeforeDeclaration) { + } else if (this.state === State.BeforeDeclaration) { this.stateBeforeDeclaration(c); - } else if (this._state === State.BeforeComment) { + } else if (this.state === State.BeforeComment) { this.stateBeforeComment(c); - } else if (this._state === State.InProcessingInstruction) { + } else if (this.state === State.InProcessingInstruction) { this.stateInProcessingInstruction(c); - } else if (this._state === State.InNamedEntity) { + } else if (this.state === State.InNamedEntity) { this.stateInNamedEntity(c); - } else if (this._state === State.BeforeEntity) { + } else if (this.state === State.BeforeEntity) { this.stateBeforeEntity(c); - } else if (this._state === State.InHexEntity) { + } else if (this.state === State.InHexEntity) { this.stateInHexEntity(c); - } else if (this._state === State.InNumericEntity) { + } else if (this.state === State.InNumericEntity) { this.stateInNumericEntity(c); } else { // `this._state === State.BeforeNumericEntity` @@ -871,7 +946,7 @@ export default class Tokenizer { } private finish() { - if (this._state === State.InNamedEntity) { + if (this.state === State.InNamedEntity) { this.emitNamedEntity(); } @@ -885,34 +960,34 @@ export default class Tokenizer { /** Handle any trailing data. */ private handleTrailingData() { const data = this.buffer.substr(this.sectionStart); - if (this._state === State.InCommentLike) { + if (this.state === State.InCommentLike) { if (this.currentSequence === Sequences.CdataEnd) { this.cbs.oncdata(data); } else { this.cbs.oncomment(data); } } else if ( - this._state === State.InNumericEntity && + this.state === State.InNumericEntity && this.allowLegacyEntity() ) { this.decodeNumericEntity(10, false); // All trailing data will have been consumed } else if ( - this._state === State.InHexEntity && + this.state === State.InHexEntity && this.allowLegacyEntity() ) { this.decodeNumericEntity(16, false); // All trailing data will have been consumed } else if ( - this._state === State.InTagName || - this._state === State.BeforeAttributeName || - this._state === State.BeforeAttributeValue || - this._state === State.AfterAttributeName || - this._state === State.InAttributeName || - this._state === State.InAttributeValueSq || - this._state === State.InAttributeValueDq || - this._state === State.InAttributeValueNq || - this._state === State.InClosingTagName + this.state === State.InTagName || + this.state === State.BeforeAttributeName || + this.state === State.BeforeAttributeValue || + this.state === State.AfterAttributeName || + this.state === State.InAttributeName || + this.state === State.InAttributeValueSingleQuotes || + this.state === State.InAttributeValueDoubleQuotes || + this.state === State.InAttributeValueNoQuotes || + this.state === State.InClosingTagName ) { /* * If we are currently in an opening or closing tag, us not calling the diff --git a/tsconfig.json b/tsconfig.json index 5d16dc754..d0049dee3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,6 +19,8 @@ "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, + "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */, + /* Module Resolution Options */ // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,