Hi Dmitry,
It looks to me like you've just found a bug in SpiderMonkey's generator
implementation, which pre-dates the strawman:generators proposal by four+
years. Please file it in the right place: https://bugzilla.mozilla.org/. Thanks.
The bug is a regression. Here's what my Firefox 3-era JS shell does:
js> // infinite objects generator
js> let g = new function () {
this.x = 10;
while (true) {
yield;
}
};
js>
js> // generate an array of 3 objects
js> let objects = [1, 2, 3].map(function(i) g.next());
js> objects
,,
js> uneval(objects)
[(void 0), (void 0), (void 0)]
Clearly, yield; should not yield a mysterious object as the return value of
g.next().
/be
On Mar 13, 2011, at 3:04 PM, Dmitry A. Soshnikov wrote:
> Hello,
>
> I hope you can help with explaining of what is going on with `this` value
> inside the body of a generator?
>
> Consider e.g. the following case:
>
> // 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());
>
> console.dir(objects);
>
> Results:
>
> [
> [[Class]]: "Array",
> length: 3,
> 0: {
> [[Class]]: "Object",
> x: 10
> },
> 1: {
> [[Class]]: "Object"
> },
> 2: {
> [[Class]]: "Object"
> }
> ]
>
> Only first object has `x` property. Also:
>
> console.log(objects[0] == objects[1]); // false
> console.log(objects[1] == objects[2]); // false
>
>
>
> As I understand, [[Construct]] activated by the `new` calls [[Call]] of the
> function, which produces the `g` generator. I look here:
> http://wiki.ecmascript.org/doku.php?id=strawman:generators and see:
>
> Calling
>
> Let f be a generator function. The semantics of a function call f(x1, ...,
> xn) is:
>
> Let E = a new VariableEnvironment record with mappings for x1 ... xn
> Let S = the current scope chain extended with E
> Let V = a new generator object with
> [[Scope]] = S
> [[Code]] = f.[[Code]]
> [[ExecutionContext]] = null
> [[State]] = “newborn”
> [[Handler]] = the standard generator handler
> Return V
>
>
> So, `g` will be the generator with the needed [[Code]] and empty
> [[ExecutionContext]]. Notice, there nothing is said about `this` value.
>
> In calling `next` (i.e. `send(undefined)`) we get into:
>
> Internal method: send
>
> G.[[Send]]
>
> Let State = G.[[State]]
> If State = “executing” Throw Error
> If State = “closed” Throw Error
> Let X be the first argument
> If State = “newborn”
> If X != undefined Throw TypeError
> Let K = a new execution context as for a function call
> K.currentGenerator := G
> K.scopeChain := G.[[Scope]]
> Push K onto the stack
> Return Execute(G.[[Code]])
> G.[[State]] := “executing”
> Let Result = Resume(G.[[ExecutionContext]], normal, X)
> Return Result
>
> We see that a new context is created but again, nothing is said about its
> `this` value.
>
> When evaluating Execute(G.[[Code]]) we with yield get into:
>
> Yielding
>
> The semantics of evaluating an expression of the form yield e is:
>
> Let V ?= Evaluate(e)
> Let K = the current execution context
> Let O = K.currentGenerator
> O.[[ExecutionContext]] := K
> O.[[State]] := “suspended”
> Pop the current execution context
> Return (normal, V, null)
>
>
> Btw, what does "?=" mean?
>
> Here the K is the context created on in the `send` method (still we haven't
> any info about `this` value).
>
> The following call to `next` will again enter `send` method with `undefined`
> but we already get into:
>
> Resuming generators
>
> Operation Resume(K, completionType, V)
>
> Push K onto the execution context stack
> Let G = K.currentGenerator
> Set the current scope chain to G.[[Scope]]
> Continue executing K as if its last expression produced (completionType,
> V, null)
>
> where we proceed with evaluating previously saved continuation. And again,
> nothing about `this` is said.
>
> As I see, during all these steps always the same K is passed around and
> evaluated. `This` value is a property of the context and K has it, but which?
>
> As was shown in the example above, `this` is set to the newly created object,
> but it's a current SpiderMonkey's behavior; don't know how it correlate with
> this draft spec.
>
> Consider e.g. the following example (tested in SpiderMonkey):
>
> g = new function() {
> yield new Boolean((yield) == this)
> };
>
> console.log(''+ g.send(g.next())); // false
>
> g = new function() {
> yield new Boolean(this == (yield))
> };
>
> console.log(''+ g.send(g.next())); // true
>
> Why in first call `yield` wasn't newly created object and in the second one
> -- it was? What actually _should_ yield without an argument yields?
> `undefined` I guess.
>
> So don't know which behavior is correct and whether it's a strange behavior
> in current SpiderMonkey.
>
> Thanks,
> Dmitry.
> _______________________________________________
> es-discuss mailing list
> [email protected]
> https://mail.mozilla.org/listinfo/es-discuss
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss