Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(compiler): bypass expansion form tokenizing in ngNonBindable blocks #12178

Closed
wants to merge 1 commit into from
Closed

fix(compiler): bypass expansion form tokenizing in ngNonBindable blocks #12178

wants to merge 1 commit into from

Conversation

tiagoroldao
Copy link

Please check if the PR fulfills these requirements

What kind of change does this PR introduce? (check one with "x")

[x] Bugfix
[ ] Feature
[ ] Code style update (formatting, local variables)
[ ] Refactoring (no functional changes, no api changes)
[ ] Build related changes
[ ] CI related changes
[ ] Other... Please describe:

What is the current behavior? (You can also link to an open issue here)
See #11859

Does this PR introduce a breaking change? (check one with "x")

[ ] Yes
[x] No

If this PR contains a breaking change, please describe the impact and migration path for existing applications: ...

Other information:

fixes #11859

@@ -88,6 +89,8 @@ class _Tokenizer {
private _currentTokenType: TokenType;
private _expansionCaseStack: TokenType[] = [];
private _inInterpolation: boolean = false;
private _inNonBindableBlock: boolean = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a getter (depth gt 0)

@@ -60,6 +60,7 @@ export function tokenize(
}

const _CR_OR_CRLF_REGEXP = /\r\n?/g;
const NG_NON_BINDABLE_ATTR = 'ngNonBindable';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be exported from the template parser ?

@tiagoroldao
Copy link
Author

Reviewed comments and ammended, @vicb

@vicb vicb added the action: review The PR is still awaiting reviews from at least one requested reviewer label Oct 10, 2016
try {
if (!chars.isAsciiLetter(this._peek)) {
throw this._createError(_unexpectedCharacterErrorMsg(this._peek), this._getSpan());
}
const nameStart = this._index;
this._consumeTagOpenStart(start);
if (this._inNonBindableBlock()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • merge this test with the code below,
  • push all AttrName tokens to an array,
if (this._inNonBindableBlock()) {
  depth++;
} else if (attrNames.some(isNonBindableAttribute)) {
  depth = 1;
}

@@ -640,6 +655,8 @@ class _Tokenizer {
}
}

private _inNonBindableBlock(): boolean { return this._nonBindableBlockDepth > 0; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Block -> Element

@@ -523,6 +535,9 @@ class _Tokenizer {
this._attemptCharCodeUntilFn(isNotWhitespace);
this._requireCharCode(chars.$GT);
this._endToken(prefixAndName);
if (this._inNonBindableBlock()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some tag are implicitly closed
<ul><li ngNonBindable>blabla<li>{{ 'hello' }}</ul>
Please fix and add a test.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not easy as the logic is in the parser...

May be we should move ICU logic into the parser ??

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was just dwelling on that - makes the explicit close-open tags mush more appealing! 😄

It's definitely doable, specially since the lexer does get a _getTagDefinition getter we can use. Moving all code to a checker function that runs after a tag open and a tag close, instead of sprinkling it around the class might do the trick.

Copy link
Author

@tiagoroldao tiagoroldao Oct 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A first draft for clarity. Let me know if you prefer to move the logic/approach this in another way.

private _checkNonBindableElementDepth () {
    let currentToken: Token = this.tokens[this.tokens.length - 1];

    if (currentToken.type === TokenType.TAG_OPEN_END || 
        currentToken.type === TokenType.TAG_OPEN_END_VOID || 
        currentToken.type === TokenType.TAG_CLOSE) {
      // check if this closes any previous, react
    }

    if (currentToken.type === TokenType.TAG_OPEN_END) {
      if (this._inNonBindableElement()) {
        this._nonBindableElementDepth++;
      }
      else if (/* tag contains ngNonBindable attr */){
        this._nonBindableElementDepth = 1;
      }
    }
    else if (currentToken.type === TokenType.TAG_CLOSE) {
      if (this._inNonBindableElement()) {
        this._nonBindableElementDepth--;
      }
    }
  };

@vicb
Copy link
Contributor

vicb commented Oct 10, 2016

Could you please make sure that we have integration test for

  • ICU like,
  • interpolation like
    in non bindable nodes ?

Thanks !

@vicb vicb added the action: cleanup The PR is in need of cleanup, either due to needing a rebase or in response to comments from reviews label Oct 10, 2016
@tiagoroldao
Copy link
Author

@vicb changed the approach to keep (and cull) an tag token array, which ended up cleaner (code all in one place). None of the original code was kept, so please re-review as fresh.

@@ -423,6 +426,7 @@ class _Tokenizer {
let savedPos = this._savePosition();
let tagName: string;
let lowercaseTagName: string;
let attributeStart: number;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unused

private _inNonBindableElement(): boolean { return this._nonBindableElementArr.length > 0; }

private _checkNonBindableElementDepth() {
let currentToken: Token = this.tokens[this.tokens.length - 1];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_check... makes me expect a boolean or a throw.

  • find a better name
  • add some inline docs // ...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const currentToken

private _checkNonBindableElementDepth() {
let currentToken: Token = this.tokens[this.tokens.length - 1];

if (this._inNonBindableElement() && (currentToken.type === TokenType.TAG_OPEN_END ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

=== || === || === should probably be extracted in a fn

this.tokens, t => t.type === TokenType.TAG_OPEN_START || t.type === TokenType.TAG_CLOSE);
}

private _NonBindableAttrInCurrentElement(): boolean {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_non

@@ -711,3 +779,15 @@ function mergeTextTokens(srcTokens: Token[]): Token[] {

return dstTokens;
}

function findPreviousToken(
array: Token[], filter: (token: Token) => boolean,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filter -> predicate ?


function findPreviousToken(
array: Token[], filter: (token: Token) => boolean,
stopWhen: (token: Token) => boolean = t => false): Token {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lastPredicate

array: Token[], filter: (token: Token) => boolean,
stopWhen: (token: Token) => boolean = t => false): Token {
let index = array.length - 1;
while (index >= 0 && !stopWhen(array[index])) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for ?

@vicb
Copy link
Contributor

vicb commented Oct 13, 2016

Added some comments but I'm not sure if this logic belongs here or in the parser.

Have you tried / thought about detecting ICUs in the parser ?

@tiagoroldao
Copy link
Author

tiagoroldao commented Oct 15, 2016

Fixed the code according to your comments.

Re: refactor:
Don't know if it should - moving tokenisation to the parser seems to further complicate things - wouldn't logic meant Interpolation detection also need to be moved?
Let me know what you think. If I go down that route will need to explore the parser further - may need time and help.

@tiagoroldao
Copy link
Author

Also - The failed check seems unrelated to this work - is this correct?

@pkozlowski-opensource pkozlowski-opensource added the area: core Issues related to the framework runtime label Oct 18, 2016
@tiagoroldao
Copy link
Author

@vicb, can you verify this PR? I disagree with the need for moving responsibilities, and assume the test failure was incidental and not related.

@tbosch
Copy link
Contributor

tbosch commented Jun 6, 2017

We are closing this PR because it is unclear whether this is the right fix and we need to finalize the design before committing to a solution.

@tbosch tbosch closed this Jun 6, 2017
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 11, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: cleanup The PR is in need of cleanup, either due to needing a rebase or in response to comments from reviews action: review The PR is still awaiting reviews from at least one requested reviewer area: core Issues related to the framework runtime cla: yes type: bug/fix
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ngNonBindable can't handle unescaped { characters
6 participants