yield and new : SpiderMonkey and the draft Spec

Dmitry A. Soshnikov dmitry.soshnikov at gmail.com
Mon Mar 14 12:20:17 PDT 2011


On 14.03.2011 16:51, Brendan Eich wrote:
> On Mar 14, 2011, at 3:02 AM, Dmitry Soshnikov wrote:
>
>> Yep, thanks Brendan,
>>
>> I filed the bug https://bugzilla.mozilla.org/show_bug.cgi?id=641436
>>
>> But the thing with `this` is still interesting for me. So in this particular case `this` should be set to `undefined`.
> No, |this| is not set to undefined. The generator function yields undefined each time (via |yield;| in the loop body).
>
> That undefined is what the anonymous expression closure you pass to map should (absent the bug) return in turn (i.e., undefined is the return value of g.next() and thus the return value of function(i) g.next()).
>

Yes, in this particular case I see that `yield` should return 
`undefined` and that's the bug is that it instead returns a newly 
created object (every time different btw, as there would different calls 
to `new`, however by logic there is only one call to `new`, i.e. 
[[Construct]]).

What I asked is the `this` value inside a generator's body in _general_, 
not in this exact case with the `new`. I'll described the question in 
some details below.

> Notice how you evaluate a |new| operator exactly once, in the top-level assignment expression statement:
>

Yes, it's true and that exactly I wrote in the first letter.

>>> // infinite objects generator
>>> let g = new function () {
>>>   this.x = 10;
>>>   while (true) {
>>>     yield;
>>>   }
>>> };
>>>
>>> // generate an array of 3 objects
>>> let objects = [1, 2, 3].map(function(i) g.next());
> A generator function called as a constructor via new receives a fresh Object instance as |this|, same as for any function.
>

Yep. But it's from the point of view how [[Construct]] works. This is 
exactly about what I was asking. However, according to the draft spec, 
[[Call]] applied to the generator function (no matter from [[Construct]] 
or simply) does _not_ enter any new context (which means, a caller 
doesn't/shouldn't provide any `this` value for the new context, since 
there is no entering the context). Only a new generator instance is 
created with the needed [[Code]], [[ExecutionContext]] set to `null` and 
[[State]] as "newborn".

Only when `next()` method (or `send(undefined)` -- which is the same) is 
called, there is a _creation of new execution context for a function 
call_. This is again how it's written in that draft spec. What does it 
mean? -- it means that entering the execution context as for a function 
code, a _caller_, i.e. the _next_ (or send) method should provide `this` 
value for the callee -- a newly created context. And exactly about this 
case I was asking -- which `this` value should be provided in this case? 
Because on the draft page there nothing is said about `this` value of 
the context (saved continuation) passing around.

It's clearly seen in current JS 1.8.5 that `this` value is set to global 
object (as for every normal function called with the base either an 
activation object or undefined => global).

let global = this;

let isGlobal = (function () {
   yield this == global; // true
})().next();

console.log(isGlobal); // true

Moreover, in strict mode it's (again in current JS 1.8.5) `undefined`:

let global = this;

let isUndefined = (function () {
   "use strict";
   yield this == undefined; // true
})().next();

console.log(isUndefined); // true

So the answer is in the next() / send() method which is a caller and 
which is provides `this` value for the context. However, next/send just 
resumes the same context K which was created in first send(undefined) -- 
but there is no info about `this` value of that context.

Backing to the case with `new`. If to take the draft spec (and not the 
current JS's implementation), then we have:

new -> [[Construct]] -> create new object -> apply newly created object 
for the [[Call]]. But. This [[Call]] doesn't enter the context as was 
said. Only the generator object should be created -> generator G is 
returned. At this step there is no any info about newly created object 
since actually [[Call]] wasn't call normally, but just created the G 
generator.

Then G.next() already creates (this is the first time when it happens) 
an execution context K "as for a function call". But where `this` value 
for the context? This was my question. Assuming this, and as we said 
that there's no any info about newly created object from `new` 
subsequent calls to next() should result for `this` just `undefined` 
value -- as for a casual call of the continuation.

> However, there's no way for a generator function to return that instance, because a generator function always implicitly returns a fresh generator iterator when invoked.

Yes, it's true.

>   It could store |this| in the heap, and whatever value |this| receives is saved as part of the shallow continuation capture by the generator.
>

Yeah, but I'm thinking is there a big sense in it? Even if `this` value 
will be captured, all next() calls will have the same `this` value since 
the context is always the same.

> If you write |return this| anywhere in the body of the generator function you'll get an early error.

Yep.

>   So really all |new| is doing for you here is giving a fresh object to |this|, which seems fine if that's what you want.
>

Well, actually I wasn't needed this newly created object, I just though 
to what `this` value should set in different calls to next. By logic, it 
always should be the same -- the first on which is set for the context 
at the context's creation (i.e. at first call to `next`).

I.e. again, what I notice (and asked), is that there's no any info in 
the draft spec about `this` value of the generator's context. I think it 
would be good to add this information.

Thanks again,
Dmitry.


More information about the es-discuss mailing list