-
Notifications
You must be signed in to change notification settings - Fork 27.4k
refactor($parse): don't use bind-once interceptor for non-bind-once expressions #9961
Conversation
773ee2d
to
83ce6aa
Compare
It's a bit messy, so I'm open to suggestions for making it a bit smaller. @lgalfaso can you review please |
@caitp it does look overly complicated. What is wrong with just diff --git a/src/ng/interpolate.js b/src/ng/interpolate.js
index 2e24b3f..4691719 100644
--- a/src/ng/interpolate.js
+++ b/src/ng/interpolate.js
@@ -300,6 +300,7 @@ function $InterpolateProvider() {
}
function parseStringifyInterceptor(value) {
+ if (isUndefined(value)) return;
try {
return stringify(getValue(value));
} catch (err) {
diff --git a/src/ng/parse.js b/src/ng/parse.js
index 4b0a239..47b7af0 100644
--- a/src/ng/parse.js
+++ b/src/ng/parse.js
@@ -1257,9 +1257,7 @@ function $ParseProvider() {
var fn = function interceptedExpression(scope, locals) {
var value = parsedExpression(scope, locals);
var result = interceptorFn(value, scope, locals);
- // we only return the interceptor's result if the
- // initial value is defined (for bind-once)
- return isDefined(value) || interceptorFn.$stateful ? result : value;
+ return result;
};
// Propagate $$watchDelegates other then inputsWatchDelegate ?? |
the problem is that it's not correct. we need to split these paths apart, because they do different things, and they screw up when you cross the streams. |
can you please post an example? |
the example is, bind-once, all-or-nothing, and regular watches all have different behaviours, they are not always compatible (which is why we have this bug in the first place), and it's a really stupid idea to try and fit them into a box together, because they are just different. The result of trying to make them all work using the same path, will be confusing, incorrect code. Plain and simple. It will be slightly smaller code, but it will be wrong. Honestly, the interceptors and watch delegates thing was a recipe for bad code to begin with, but it's what we've got. |
interestingly there are some test failures that I wasn't seeing locally, so I'll need to change this a bit to fix those, this isn't ready for review yet. |
@@ -1936,7 +1936,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { | |||
} | |||
return lastValue = parentValue; | |||
}; | |||
parentValueWatch.$stateful = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure this needs to stay. parentValueWatch
depends on isolteBindingContext
which makes it stateful...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's not, setting it as $stateful was just a really stupid hack, it's not the right way to do this. it was added because the decision was to make $stateful equate to essentially the opposite of bind-once
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
which is, obviously, not the right thing to do, it just doesn't make sense, and makes the spaghetti monster even bigger.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that is incorrect. $stateful means the output depends on more then just the input, such as parentValueWatch
depending on isolateBindingContext
. This is for the input-watching, nothing to do with bind-once.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this isn't necessary, should not be necessary, and the whole fact that we have a $stateful flag is unnecessary and should be deleted from the codebase.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is why you have $compile
isolate scope tests failing...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you're misunderstanding what $stateful
is for, but ok.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not misunderstanding it, what I'm understanding is that it was a bad idea to begin with. We don't need hacks to improve performance, what we need is better algorithms, and better VM implementations, to improve performance.
seriously, we have turned a once-simple parser, and turned it into an extremely complicated mess. we should not have done this, this was not the right thing to do.
we've made it harder on users, and we've made it harder on maintainers. this was not the right thing to do, even if it artificially appears faster.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The last 200 lines of parse.js involving all the $watch logic is ugly, for sure, I'm not denying that :P
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we've made it harder on users, and we've made it harder on maintainers.
this was not the right thing to do, even if it artificially appears faster.
Maybe it is about time for $parse
to be split into
- expression parsing and evaluation
- one-time binding and other enhancements
@lgalfaso I think your suggestion would break interpolation of undefined values? How about removing the use of the interceptorFn from |
@jbernard can you write a test that would pass before and would fail with |
alright, i put the $stateful back... but i'm not happy about it! I removed that as a last minute thing, which is why i didn't see failures before submitting the CL. I don't think we should refactor the parser to have an "evaluation" section separate from the parsing. Ideally, we should have something like this:
This is probably the most straight forward, easily understood approach --- and it's also essentially what we were doing before we introduced the bind-once nonsense and the filter "optimization". If bind-once and filter optimization code generation were moved to the AST-visiting level rather than this hack at the end of the pipeline, it would be a lot easier to understand. Unfortunately, having multiple types of AST visitors increases complexity a lot over the original code, so it might not be doable. But if we're going to have this crap with bind-once and filter optimizations, it's the right thing to do |
@lgalfaso it doesn't break what I thought it would (because That's why I think we need a different However I think removing the use of interceptors in $interpolate is better then having multiple: jbedard@4bcf1b1 |
@@ -308,6 +312,20 @@ function $InterpolateProvider() { | |||
$exceptionHandler(newErr); | |||
} | |||
} | |||
|
|||
function allOrNothingParseStringifyInterceptor(value) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need two different interceptors? the allOrNothingParseStringifyInterceptor
should work well in both cases
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the other case will return the empty string for undefined
, which breaks allOrNothing
mode.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am saying that if the input is undefined
, then return undefined
regardless of if this is all-or-nothing or not
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that breaks interpolation, it never worked that way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I recall jbedard had another version of this that avoided interceptors here entirely though, which is probably good, i have to look at it though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would it break interpolation? ['foo', undefined, 'bar'].toString() === 'foobar'
93ac49f
to
b062fb3
Compare
so this is a pretty tiny fixup now, and removes the weirdness from before. We can look at getting rid of the remaining weirdness later, but lets get this in today. Look good to you Lucas? |
agree, we can look later into doing a second cleanup. Can you check the why of the test failures? |
looks like flakes |
LGTM |
It's not a flake, getting rid of the interceptor breaks interpolation -______- very angry with this |
Sorry @jbedard but that approach isn't going to work :\ |
gonna try plan B) ._. |
…xpressions Side-effects: - Logic for allOrNothing watches now lives in $intercept rather than $parse Credit to @jbedard for idea to remove $watch interceptors craziness from $interpolate. Closes angular#9958
Really? :( I had all the tests passing in my branch...
|
@jbedard it was failing docsapp tests, and E2E tests, but passing unit tests. When trying the docsapp failure in the browser, the bound view was not being updated at all, it was pretty broken :( |
Was that something that can be unit tested? Could also add jbedard@bc0e4c0, and I saw someone wanting bind-once working with ng-sanitize which sounds similar... |
Side-effects:
Closes #9958