-
Notifications
You must be signed in to change notification settings - Fork 12
Re-evaluate IsRegExp call in MatchAllIterator? #34
Comments
Let's talk first about the second IsRegExp call in MatchAllIterator (the one directly after calling RegExpCreate). We currently have:
ECMA-262 never calls IsRegExp after RegExpCreate, so adding it here seems like a bad precedence. And if the IsRegExp call is removed, step 3.f can also be changed back to Now the other IsRegExp call in MatchAllIterator. I have two separate concerns here:
Which means
While this does create two RegExp objects for |
Given how there are test262 tests and a V8 implementation, these questions need to be answered quickly. I share @anba's intuitions here. |
Regarding the first issue, I think that reasoning makes sense - I'll prepare that change. Regarding the second one: I'll think about your comment and provide a thorough response later today. |
@anba Thus, Regarding the other part: there's already, sadly, multiple ways to subclass regexes: Your suggested change makes |
Filed #35 for the first change (of the three discussed). |
Why does it need to be robust against deleting delete RegExp.prototype[Symbol.match];
"".match(/a/); // <- Throws a TypeError
delete RegExp.prototype[Symbol.search];
"".search(/a/); // <- Throws a TypeError |
Indeed; that is the precedent - but I think it's a terrible one, and I wanted to do something better with this proposal than the status quo. |
If someone forcibly disrupts standard built-in methods or properties, it's their problem. For example we also thrown an error from |
So why is it OK to throw on non-callable values (which I agree with), but not OK here to throw on a non-regex being passed into a method that requires a regex? |
I agree with @anba here. I think it's weird to guard against these things. We've been discussing various guards for some months now on GitHub. If we're not able to come to agreement here, maybe it should be brought to the committee. |
I think the real question is, is this an implementation concern? This wasn’t called out during all of the spec reviews prior to stage 3 - I’m not sure how the suggested change eases implementability; either case seems equally optimizeable in the scenario of an unmodified matchAll function; but as-is, it’d perform the same in the case of an absent one. |
As proposals move along between Stage 3 and Stage 4, deeper review ends up happening, and tweaks are made. This has happened with basically all large Stage 3 proposals, even for changes that are not driven my implementation concerns themselves. I am sorry I didn't call this our earlier. |
Sounds like I’ll have to add this to the May agenda then (altho #35 can be merged in the meantime) |
FWIW, I agree with @anba and @littledan: even agreed that the currently proposed behavior is slightly nicer, consistency beats that in importance. Regarding the concern about this being raised late in the process: I think it would've helped to actively point this out as deviating from precedent. (AFAIK that hasn't happened - my apologies if that is incorrect!) It might make sense to make this an explicit recommendation in our various docs on championing. |
I thought this came up in committee around #28, but it's possible that it was overlooked. |
@littledan @anba so after rereading this issue: it seems like the primary remaining objection is the "IsRegExp" check after the RegExpCreate check. My intention is to get as early an error as possible - specifically, before the iterator is created, instead of during iteration. However, the only error this might guard against is in https://tc39.github.io/ecma262/#sec-regexpexec, whose code path doesn't actually care about Would you be content with replacing the IsRegExp check with https://tc39.github.io/ecma262/#sec-regexpexec step 5 - specifically, "If matcher does not have a [[RegExpMatcher]] internal slot, throw a TypeError exception."? This shouldn't be observable in the happy path, and shouldn't impact performance. |
@ljharb I don't think such a check will ever result in a TypeError thrown; that internal slot will always exist on a return value of RegExpCreate AFAICT (though it might have a different prototype or the prototype may have been mutated). |
ah, good point - you're right. It seems that the appropriate resolution here, then, is to just remove the check - which closes the issue and obviates the need to come back to the committee. |
Maybe it'd be worth a 30-second status update to say that you're making this change and that this resolves the issue. I like to do a quick committee presentation for changes to Stage 3 proposals, personally. |
Sounds great, I'll do that later today. |
Closed with #35. |
#35 only removed one IsRegExp call, but left the other one. I still think the reasoning outlined in #34 (comment) applies, for example when calling |
@anba thanks, can you file a new issue for that? |
So, I've taken another look at this, and precisely because of the different behavior in the regexp vs string paths, the remaining I'm going to keep this as-is, and implementations should continue to ship. If there's further arguments, I'd be happy to have them on a new issue. |
Can you elaborate on why it's "absolutely necessary"? @anba had a lengthy explanation of his reasoning, including a suggested fix, in #34 (comment). Why is the suggested fix being rejected? |
Ah, the bit of info I was missing was this comment in another thread:
IIRC, the last time |
In January, we talked about what it should do with a string, and the committee consensus at that time (which included both options) was what we ended up with - not creating a regex with the string path, which resulted in the branching for string vs regex. |
If you think it needs to come back to committee, please file a separate issue - @anba’s comment is exceeding difficult for me to unpack, and to be able to confidently discuss anything about it in committee, id need to see arguments stated separately. |
@anba outlined two separate concerns about the remaining `IsRegExp` call in `MatchAllIterator`. Quoting from tc39#34 (comment): 1. When `MatchAllIterator` is called from `RegExp.prototype [ @@matchall ]`, we should/have to assume the user explicitly decided to treat `R` as a RegExp object, so having an additional `IsRegExp` call to change this decision seems questionable. It’s also not consistent with how the other `RegExp.prototype` methods work. 2. When `MatchAllIterator` is called from `String.prototype.matchAll`, I’d prefer to handle it more like the other `String.prototype` methods which create RegExp objects (that means `String.prototype.match` and `String.prototype.search`), because I want to avoid adding yet another way to handle RegExp sub-classes. There are already two different RegExp sub-classing/extension interfaces: the `RegExp.prototype` methods all call `RegExpExec`, which means sub-classes, or any other classes, only need to provide their own `exec` methods when they want to reuse the other `RegExp.prototype` methods. And in addition to that, the `@@match/replace/search/split` interfaces allow sub-classes to provide their own implementations for just these methods. The `matchAll` proposal in its current form adds another dimension to this by providing different code paths depending on whether or not an object is RegExp-like (as per the `IsRegExp` abstract operation). In my opinion we should only support RegExp sub-classing in two ways: 1) Either the RegExp sub-class has `%RegExpPrototype%` on its prototype chain, or 2) the RegExp sub-class copies the relevant methods from `%RegExpPrototype%` into its prototype object. Ref. tc39#21, tc39#34.
- `String.prototype.matchAll`: - use `RegExpCreate` when `Symbol.prototype.matchAll` is not found - fall back to regex coercion otherwise - `RegExp.prototype[Symbol.matchAll]`: - receiver is assumed to be a regex implicitly - remove `MatchAllIterator` abstract operation Thus, `IsRegExp` call no longer exists. Addresses #21. Addresses #34. Closes #37.
- `String.prototype.matchAll`: - use `RegExpCreate` when `Symbol.prototype.matchAll` is not found - fall back to regex coercion otherwise - `RegExp.prototype[Symbol.matchAll]`: - receiver is assumed to be a regex implicitly - remove `MatchAllIterator` abstract operation Thus, `IsRegExp` call no longer exists. Addresses #21. Addresses #34. Closes #37.
@anba's concern from #33 (comment):
(Pending merging of #33)
The text was updated successfully, but these errors were encountered: