Closed Bug 1119777 Opened 9 years ago Closed 6 years ago

Remove non-standard Function.prototype.isGenerator


(Core :: JavaScript: Standard Library, defect)

Not set



Tracking Status
firefox58 --- fixed


(Reporter: evilpie, Assigned: jandem)


(Blocks 1 open bug)


(Keywords: dev-doc-complete, site-compat)


(1 file)

Not sure if we should really do this, because as far as I can tell there is no other way to find out if a function is a generator in ES6.
I guess we can check whether the given function is ES6 generator or not with following code.

> let GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor;
> let isGenerator = f => f instanceof GeneratorFunction;
> isGenerator(function(){});
> isGenerator(function*(){});

But it's different than Function.prototype.isGenerator with legacy generator.

> isGenerator(function(){yield 1});

So, if there is any other way to detect legacy generator (or after the removal of legacy generator support), we can remove Function.prototype.isGenerator :)
Currently Function.prototype.isGenerator is used in addon-sdk and jstests (and might be used in some add-ons).
> if (test.isGenerator && test.isGenerator()) {
> if (iterator.isGenerator && iterator.isGenerator())
> reportCompare(reported.isGenerator(), true, "reported case: is generator");
> reportCompare(simplified1.isGenerator(), true, "simplified case 1: is generator");
> reportCompare(simplified2.isGenerator(), true, "simplified case 2: is generator");
> reportCompare(reported.isGenerator(), true, "reported case: is generator");
> reportCompare(f.isGenerator(), true, desc + ": is generator");
> reportCompare(true, "isGenerator" in Function.prototype, "Function.prototype.isGenerator present");
> ...

A jit-test also uses it, but it's not required to be `isGenerator`, so we can just replace it with some other property.
> reportCompare(true, "isGenerator" in Function, "Function.prototype.isGenerator present");

Then, to remove Function.prototype.isGenerator before removing legacy generator, I have following 4 plans.
(Of course, if we can just remove Function.prototype.isGenerator, it's the simplest solution...)

Plan A:
  Move `isGenerator` method to legacy generator instance,
  We can detect legacy generator by calling it (or just by the existence):

    if ("isGenerator" in f && f.isGenerator()) ...

Plan B:
  Define `isLegacyGenerator` property in legacy generator instance.
  We can detect legacy generator by the value (or just by the existence):

    if ("isLegacyGenerator" in f && f.isLegacyGenerator) ...

  This might be better than Plan A, because this prevents following bad workaround.

    let isGenerator = (function(){yield}).isGenerator;
    if ( ...

Plan C:
  Add `LegacyGeneratorFunction` constructor, like `GeneratorFunction` for ES6 generator,
  We can detect legacy generator by following code, like comment #1:

    let LegacyGeneratorFunction = Object.getPrototypeOf(function(){yield}).constructor;
    if (f instanceof LegacyGeneratorFunction) ...

Plan D:
  Add `IsLegacyGenerator` testing function to js shell, and use it in jstests.
  So, in jstests, we can detect legacy generator by it:

    if (IsLegacyGenerator(f)) ...

  But this plan has a problem with addon-sdk,
  if addon-sdk should handle legacy generator, this cannot be applied.

In any way, we can remove non-standard method from standard class.
I wonder if it's worth bringing this up on es-discuss. It might not be, though. The only non-test usage (from the addon-sdk's Sequence utility) looks to me like it shouldn't use `isGenerator`: the fact that the iterator is implemented as a generator should be treated as an implementation detail. IIUC, the temptation to do these narrow checks is precisely why people are opposed to having `isFoo`-type methods.
Not sure the benefit of making it the standard :/

Anyway, for addon-sdk's case, we can test the returned value.

Plan D':
  Check the returned value from the function call, and test the property (.next method),
  instead of using Function.prototype.isGenerator. (not working now?)
bug 993389 complains a problem around isGenerator.

js> (function*(){}).bind(this).isGenerator()

So checking returned value would also be better for this aspect.
> let GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor;
> let isGenerator = f => f instanceof GeneratorFunction;

Sadly, it doesn't work if f comes from different global :/
Depends on: 1219028
Depends on: 1140759
Keywords: site-compat
now that bug 1140759 is marked as WONTFIX, and we can just remove it at the same time as legacy generator (bug 1083482)
See Also: → 993389
Closed: 6 years ago
Resolution: --- → DUPLICATE
Reopening as suggested in bug 1083482 comment 22.
Resolution: DUPLICATE → ---
Assignee: nobody → jdemooij
Comment on attachment 8924697 [details] [diff] [review]

Review of attachment 8924697 [details] [diff] [review]:

it would be better asking extra review for DeferredTask.jsm
Attachment #8924697 - Flags: review?(arai.unmht) → review+
Comment on attachment 8924697 [details] [diff] [review]

Kris can review the DeferredTask.jsm change?

That line was added in
Attachment #8924697 - Flags: review?(kmaglione+bmo)
Er, *can you review...
Comment on attachment 8924697 [details] [diff] [review]

Review of attachment 8924697 [details] [diff] [review]:

::: toolkit/modules/DeferredTask.jsm
@@ +118,5 @@
>    this._taskFn = aTaskFn;
>    this._delayMs = aDelayMs;
>    this._timeoutMs = aIdleTimeoutMs;
> +  if ( === "[object GeneratorFunction]") {

I'd rather we use `ChromeUtils.getClassName(aTaskFn) === "GeneratorFunction"`. Object.prototype.toString is pretty expensive.

But it looks like that just returns "Function", so let's just remove this check entirely. It shouldn't be necessary anymore.
Attachment #8924697 - Flags: review?(kmaglione+bmo) → review+
Pushed by
Remove non-standard Function.prototype.isGenerator. r=arai,kmag
Closed: 6 years ago6 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla58
You need to log in before you can comment on or make changes to this bug.