yield and new : SpiderMonkey and the draft Spec

Brendan Eich brendan at mozilla.com
Sun Mar 13 13:39:28 PDT 2011


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
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20110313/2352077a/attachment.html>


More information about the es-discuss mailing list