Bug 28285 - Remove non-spec support for callable RegExp
: Remove non-spec support for callable RegExp
Status: RESOLVED FIXED
: WebKit
JavaScriptCore
: 528+ (Nightly build)
: PC Mac OS X 10.5
: P2 Normal
Assigned To:
:
:
:
:
  Show dependency treegraph
 
Reported: 2009-08-13 15:57 PST by
Modified: 2011-01-19 16:45 PST (History)


Attachments
Patch (50.71 KB, patch)
2011-01-19 16:23 PST, Oliver Hunt
barraclough: review+
Review Patch | Details | Formatted Diff | Diff


Note

You need to log in before you can comment on or make changes to this bug.


Description From 2009-08-13 15:57:13 PST
This is a migration of discussion begun in the comments of bug 281117

Currently, WK nightlies include a vendor extension adding support for treating instances of RegExp as functions.

/a/("abc");

This is not specified behavior, but has the effect of changing typeof /a/ == 'object' to typeof /a/ == 'function' since the regexp is now considered callable.  Additional ramifications from this are that native JSON.stringify will ignore RegExp instances as it does Functions.

It seems to me the value of syntactic sugar that shaves 5 characters from /a/.exec("abc") is not worth the price of creating a development environment where testing random input to see if it is a function changes from

if (typeof v === "function") {

to
if (typeof v === "function" && v.call) {

or some other more intelligent test.

Because /a/(str) is not supported across all current browsers, it is not recommended for use in scripts that will live in internet environments available for public consumption.  So currently, there is no benefit to the extension for IMO the largest consumer base (web developers), but there is a cost.

Other js runtimes include support for callable RegExp (Opera 9.6-10 beta2, Firefox 3.0-3.5, Chrome 3) though they all maintain typeof /a/ == "object".

Additionally, this feature complicates the story of function invocation.  Regular functions can be executed as
someFunction(str) => this === global object
obj.someFunction(str) => this === obj
new someFunction(str) => this === new instance of someFunction
someFunction.call(obj,str) => this === obj

whereas
someRegExp(str) => this === someRegExp
obj.someRegExp(str) => this === obj.someRegExp
new obj.someRegExp(str) => boom
someRegExp.call(obj,str) => boom

I realize that it may be difficult to remove functionality that has been supported for some time.  If it is not possible to do so, allowing typeof /a/ == "object" (returning false to IsCallable(reInstance)) should avoid the repercussions to the development environment, creating a common behavior across js runtimes.
------- Comment #1 From 2009-08-13 21:05:42 PST -------
I'm really not sure why you persist will the incorrect idea that it is possible for IsCallable to return false on an object that can be called.
------- Comment #2 From 2009-08-14 14:50:32 PST -------
> Additional ramifications from this are that native JSON.stringify
> will ignore RegExp instances as it does Functions.

Hmmm, this certainly does sound like a ramification we should consider carefully, and could even be a reason to remove the ability to call RegExp objects.

> It seems to me the value of syntactic sugar that shaves 5 characters from
> /a/.exec("abc") is not worth the price of creating a development environment
> where testing random input to see if it is a function changes from
> 
> if (typeof v === "function") {
> 
> to
> if (typeof v === "function" && v.call) {

The spec states "Unless specified otherwise, the [[Class]] property of a built-in object is "Function" if that built-in object has a [[Call]] property, or "Object" if that built-in object does not have a [[Call]] property."

This would seem to pretty clearly imply that an object of a class other than "Function" can validly implement [[Call]] (provided that it is stated as such), and therefore assuming that an object with a [[Call]] property is specifically of class Function is incorrect.

To test for a function you can just use the instaceof operator:

if (v instanceof Function)

> Because /a/(str) is not supported across all current browsers, it is not
> recommended for use in scripts that will live in internet environments
> available for public consumption.  So currently, there is no benefit to the
> extension for IMO the largest consumer base (web developers), but there is a
> cost.
> 
> Other js runtimes include support for callable RegExp (Opera 9.6-10 beta2,
> Firefox 3.0-3.5, Chrome 3) though they all maintain typeof /a/ == "object".

Hmmm, section 11.4.3 seems pretty clear, and I certainly can't agree with FireFox's implementation here.  The spec very clearly states that for any native object that implements [[Call]] the typeof operator must return "function".  So far as I'm aware, this is the only way within the language to ask this specific question (is this any kind of object that can be called as a function, as opposed to, is this object an instance of "Function"), and it doesn't seem like a capability we'd want to take away.  So I can't say that I think we want to unify on FireFox's present behaviour here.  I believe WebKit's current behavior is sensible, and is not fundamentally at conflict with the spec (and it would also be a sensible and consistent state to remove the ability to call RegExp objects as functions).

I would suggest the FireFox's RegExp implementation is presently noncompliant with the spec in that it violates section 11.4.3 - a native object with a [[Call]] property fails to return "function" from typeof.  It would probably be helpful to file a bug with them.

> new obj.someRegExp(str) => boom

If I'm understanding your test case correctly, you seem to be assuming that all objects that have a [[Call]] property must also have a [[Construct]] property?  From my reading of the spec this is not required (and is not a requirement within WebKit).

> someRegExp.call(obj,str) => boom

If I'm understanding your test case correctly, you seem to be assuming that all objects that have a [[Call]] property must be of class Function, and have the Function prototype featuring a "call" property?  From my reading of the spec this is again not required (and again is not a requirement within WebKit).

I'm tempted to close as behaves correctly, though as I say the 'JSON.stringify' issue certainly does seem interesting. :-/
------- Comment #3 From 2009-08-14 16:05:08 PST -------
(In reply to comment #2)
> The spec states "Unless specified otherwise, the [[Class]] property of a
> built-in object is "Function" if that built-in object has a [[Call]] property,
> or "Object" if that built-in object does not have a [[Call]] property."
> 
> This would seem to pretty clearly imply that an object of a class other than
> "Function" can validly implement [[Call]] (provided that it is stated as such),
> and therefore assuming that an object with a [[Call]] property is specifically
> of class Function is incorrect.
> 

I agree that by that wording it is valid to add [[Call]] to RegExp, as the "Unless stated otherwise" allows for RegExp [[Class]] defined in 15.10.4.1.

> To test for a function you can just use the instaceof operator:
> 
> if (v instanceof Function)

Unfortunately not true.  If v is a function from another frame, it is not instanceof Function as defined in the current frame.


> Hmmm, section 11.4.3 seems pretty clear, and I certainly can't agree with
> FireFox's implementation here.  The spec very clearly states that for any
> native object that implements [[Call]] the typeof operator must return
> "function".  So far as I'm aware, this is the only way within the language to
> ask this specific question (is this any kind of object that can be called as a
> function, as opposed to, is this object an instance of "Function"), and it
> doesn't seem like a capability we'd want to take away.  So I can't say that I
> think we want to unify on FireFox's present behaviour here.  I believe WebKit's
> current behavior is sensible, and is not fundamentally at conflict with the
> spec (and it would also be a sensible and consistent state to remove the
> ability to call RegExp objects as functions).
> 
> I would suggest the FireFox's RegExp implementation is presently noncompliant
> with the spec in that it violates section 11.4.3 - a native object with a
> [[Call]] property fails to return "function" from typeof.  It would probably be
> helpful to file a bug with them.

This is discussed in the originating bug 28117 (not 281117 as I fat fingered above).
https://bugs.webkit.org/show_bug.cgi?id=28117

Specifically, the following bugs were filed with Firefox:
https://bugzilla.mozilla.org/show_bug.cgi?id=61911
https://bugzilla.mozilla.org/show_bug.cgi?id=289933

I also agree that per the spec, reporting typeof /a/ == "function" is correct if RegExp has been extended with [[Call]] and that Firefox, Opera, and Chrome violate the spec by implementing the feature and reporting "object".  However, the practical ramifications of reporting "function" were enough to convince each of these vendors to do so.  Callable RegExp were not present in Chrome 2 but are in Chrome 3, and despite its close (historical at least) relationship to WK, it diverged from WK's behavior in favor of FF and Opera's.

My concern is that following the spec in this regard when 3 other vendors agree not to creates another inconsistency for developers to deal with.  Firefox, Chrome, and Opera's choice also brings their typeof reporting into alignment with IE (in this respect) who do not implement callable RegExp.  So of 5 major browser vendors, WebKit's behavior wrt typeof /a/ is unique.

There is more active discussion on this topic on es5-discuss:
https://mail.mozilla.org/pipermail/es5-discuss/2009-August/003033.html

> 
> > new obj.someRegExp(str) => boom
> 
> If I'm understanding your test case correctly, you seem to be assuming that all
> objects that have a [[Call]] property must also have a [[Construct]] property?

No, I understand the behavior.  I'm just suggesting that it's already challenging to teach the nuances of js function invocation to new developers.  Having something else that can look like a function
obj(str)

but not be a function just muddies the waters.
------- Comment #4 From 2011-01-16 18:36:34 PST -------
See Mozilla bug https://bugzilla.mozilla.org/show_bug.cgi?id=582717 -- we plan to remove support for callable RegExps after Firefox 4. I may remove it from our ES5 strict mode implementation for Firefox 4.

It would be excellent to make a coordinated removal from JSC, SpiderMonkey, and V8. I think Opera folks have said they will go along too. Thoughts?

/be
------- Comment #5 From 2011-01-17 00:36:54 PST -------
(In reply to comment #4)
> It would be excellent to make a coordinated removal from JSC, SpiderMonkey, and V8. I think Opera folks have said they will go along too. Thoughts?

Sounds like a good change, and it would be great to coordinate this.

> See Mozilla bug https://bugzilla.mozilla.org/show_bug.cgi?id=582717 -- we plan to remove support for callable RegExps after Firefox 4.

To help us understand the schedule, when do you see this appearing in shipping FireFox?  I believe FireFox 3 was released mid-2008, FireFox 3.5 was released mid-2009, FireFox 4 is in beta now - so I'm reading "after Firefox 4" as meaning "actually shipping to end users some time in 2012."  Am I in the right ballpark here?

> I may remove it from our ES5 strict mode implementation for Firefox 4.

This is interesting, I imagine we could probably do the same for consistency.  What are you thinking here? - from the [[Call]] method of the RegExp would you check the scope containing the call site to see if it was strict?  I think we could probably do the same.

I can't think of too many problems with this.  One issue would be if the call came from host code instead of JS code - e.g. "[].sort(/a/)".  In these cases do you think you would:
 * try to trace back up the call stack to the next non-host scope, and check if this is strict.
 * assume the caller to be non-strict (allow the call).
 * or treat all host code as strict (throw).
or some other resolution I'm not thinking of. :-)

(I'm not sure if there are any host functions where it is particularly useful to pass a RegExp as a callback, so I guess this is probably somewhat moot anyway).
------- Comment #6 From 2011-01-17 23:26:58 PST -------
(In reply to comment #5)
> > See Mozilla bug https://bugzilla.mozilla.org/show_bug.cgi?id=582717 -- we plan to remove support for callable RegExps after Firefox 4.
> 
> To help us understand the schedule, when do you see this appearing in shipping FireFox?

We're going to quarterly releases after Firefox 4, so mid-year. If too much web content depends on callable regexps, we'll have to rethink, but to get that signal we will disable callability in nightlies, soon (next month).

> > I may remove it from our ES5 strict mode implementation for Firefox 4.
> 
> This is interesting, I imagine we could probably do the same for consistency.  What are you thinking here? - from the [[Call]] method of the RegExp would you check the scope containing the call site to see if it was strict?  I think we could probably do the same.

It seems better to flag the regexp itself as strict, depending on how it was created. Strict code could still trampoline the RegExp constructor call off some non-strict code, but loss of strictness in the created regexp seems right in such cases.

/be
------- Comment #7 From 2011-01-18 04:57:29 PST -------
(In reply to comment #6)
> > > I may remove it from our ES5 strict mode implementation for Firefox 4.
> > 
> > This is interesting, I imagine we could probably do the same for
> > consistency.  What are you thinking here? - from the [[Call]] method of the
> > RegExp would you check the scope containing the call site to see if it was
> > strict?  I think we could probably do the same.
> 
> It seems better to flag the regexp itself as strict, depending on how it was
> created. Strict code could still trampoline the RegExp constructor call off
> some non-strict code, but loss of strictness in the created regexp seems right
> in such cases.

Making regular expressions callable or not based on the creating code would seem to founder on the same concerns about knowing the strictness of the caller as did our bug to make parseInt ES5-compatible only when called from strict mode:

https://bugzilla.mozilla.org/show_bug.cgi?id=577536
https://bugzilla.mozilla.org/show_bug.cgi?id=583925

Or so it seems to me on first pale.
------- Comment #8 From 2011-01-19 16:23:49 PST -------
Created an attachment (id=79519) [details]
Patch
------- Comment #9 From 2011-01-19 16:37:35 PST -------
Committed r76180: <http://trac.webkit.org/changeset/76180>
------- Comment #10 From 2011-01-19 16:45:11 PST -------
Woohoo!

We will draft off of you as soon as Firefox 4 RC1 or whatever it will be is on a branch (or repository; I'm old).

Please share any fallout you encounter via nightly testing.

/be