Spawn proposal strawman

Kris Kowal kris.kowal at
Tue May 12 12:07:35 PDT 2009

On Mon, May 11, 2009 at 4:21 PM, Brendan Eich <brendan at> wrote:
> On May 11, 2009, at 4:10 PM, Kris Kowal wrote:
>> Perhaps I'm behind on the times, but I'm under the impression that
>> presently the behavior of this function "foo" declaration has no
>> standard behavior:
>> (function () {
>>  function foo() {
>>  }
>> })();
> No, that's fully specified by ES3.

Once again, I've been chasing a JS phantom.  That cuts the complexity
of the options tree roughly in half.  Let's consider:

let asts = {};
let memos = {};

// loading
if (, id))
   return id;
let ast = parse(moduleText);
asts[id] = ast;

// executing
if (, id))
   return id;
let ast = asts[id];
let exports = memo[id] = {};
let require = Require(id);
execute(ast, {require, exports});

Furthermore, let's assume that "execute" enforces "use lexical scope"
and "use strict".

These are the ramifications if I understand correctly:

 * free assignment becomes an error at run time.
 * free variable access, apart from primordials, require, and exports
throw reference errors.
 * the object bearing the primordials has no name.
 * global object has no name.
 * the bottom scope has no name.
 * default "this" for functions and the bottom-scope is "undefined".
 * function statements are local to the module and only accessible lexically.
 * var and let declarations in the bottom scope.
 * "require" and "exports" get injected into the bottom scope.

What scope contains primordials?  Should primordials be injected into
the bottom scope before "require" and "exports" or should their be two
scopes (global, local) in a module?  I see several potential
definitions of execute:

execute(program:AST|String, scope);
 // wherein we create a new global scope and add require and exports
for each program

execute(program:AST|String, local);
 // wherein a shared frozen global scope is implied and a local scope
is pushed above it

execute(program:AST|String, global, local);
 // wherein we reuse the global scope frame and put require and
exports in a scope right above it

In both of these cases, Mark's comments about copying members to a
scope frame instead of using the object itself might apply.  I presume
that this is to avoid following the prototype chain when resolving a
variable.  If globals are shallowly copied into the local/bottom scope
of the module, some things get simpler.  For one, we can freeze the
global object without freezing the bottom of the scope chain, which
would impair module local declarations.  We also wouldn't need two
scopes initially.  However, it would be nominally slower.  I think
that execute should take two arguments either way, since it would be
inconvenient and slower than necessary to do this for every module

 var scope = copy(global);
 scope.require = Require(id);
 scope.exports = memo[exports] = {};
 execute(ast, scope);
  // scope chain is [Frame(scope)]

…since execute effectively hides an implicit copy to the scope frame,
making the explicit copy superfluous, as opposed to:

 var local = {
   require: Require(id),
   exports: memo[exports] = {}
 execute(ast, global, local);
  // scope chain is [Frame(global), Frame(local)]

Is this on the right line of reasoning?

Kris Kowal

More information about the es-discuss mailing list