Spawn proposal strawman

Kris Kowal kris.kowal at cixar.com
Mon May 11 16:10:18 PDT 2009


On Mon, May 11, 2009 at 9:26 AM, Brendan Eich <brendan at mozilla.com> wrote:
> On May 8, 2009, at 8:49 PM, Kris Kowal wrote:
>> "(function (require, exports) {" + text + "/**/\n}"
> Nit-picking a bit on names: require : provide :: import : export -- so
> mixing require and export mixes metaphors. Never stopped me ;-).

I agree about mixing metaphors.  The befuddlement of start : stop ::
begin : end is one that bothers me a lot.  The notion is to desugar
"import" and "export" to these two facets, importing and exporting.
imports : exports would be proper, but doesn't read well in code.  The
reason for using the term "exports" is to ease migration, since:

 exports.a = function a() {};

Is easy to transform textually to:

 export a = function a() {};

So, I'm inclined to stick with "exports" instead of "provide".  The
metaphor would be complete if we used "imports(id)" or "import(id)".
Since "import" is a keyword, it would not be available for the
desugarred syntax.  That leaves "imports".

 const {a} = imports("module");


> What makes functions eval'ed hermetically by the module function occur in a
> statement context? They should be nested function declarations, not
> (sub-)statements. Or I'm missing something.

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() {
   }
})();

If foo gets bound in function block scope, there's no problem (which
is the case in most browsers, I believe), but if it gets bound as a
member of global, that would be a problem, and if it gets bound like a
free assignment, it would only be a problem if free assignment isn't
localized to the module somehow.


> This is a language change. ES1-5 put free variables created by assignment in
> the object at the bottom of the scope chain.

I'm of course in favor of changing as little as possible.  If the
bottom-most scope is unique to the present module instead of the
global object, there's no need for change here.


> Mark is citing a proposal that *removes* the global object from the scope
> chain; that proposal does not fiddle with where declared and free vars go.

Alright, I'm following now.  I'll explain why I think that this would
be sufficient to fix some problems, although perhaps not necessary.


>> Implementations would need to decouple the top of the scope chain and
>> the global object.
>
> Implementations can do this easily, but the issue is language-level: is the
> global object at the bottom of the scope chain? So far, it is.

I've operated on the assumption that the global object was on the
bottom of the scope chain.  There are some concerns about module texts
for parsing and interpreting modules, some of which might be
sufficiently addressed by moving global off the scope chain for module
evaluation, but perhaps not necessarily.

 * free assignment.  I'm less concerned about the behavior of free
assignment.  I'd prefer assignment to global to be explicit, but this
ship may have sailed long ago.  It might be more appropriate for free
assignment to create module locals or module exports, which could be
accomplished by changing the bottom-of-the-scope-chain, or by changing
the behavior of free assignment in the context of a hermetic eval.  In
any case, this is not something I'm deeply concerned with.
 * function statements.  These really must be module local.  I'm not
in-the-know about whether this is a problem or not.  In the case where
hermetic eval runs a program, we'd have to wrap the program in a
function declaration.  In that case, if function statements create
function block scope locals, there's no problem.  If they operate like
free assignment, then there's a problem if the bottom-scope is
"global", but not if it's a module local object.  If hermetic eval
returns a module factory function that runs a program with a given
require and exports object, function statements would occur in the
bottom-scope.  In that case, it would be a problem if the bottom-scope
were "global", whether or not function statements behave like free
assignment or function block scope declarations.
 * return statement.  This should be a parse error in the top most
scope of a module.  If hermetic eval wraps a module's text in a
function declaration, the return statement would not be a parse error,
which would be a problem.  If heremetic eval returns a function that
executes the module with a given require and exports object, then
return would be a parse error in the bottom-scope.
 * injection attack strings.  These are a weakness of using a hermetic
eval that immediately evaluates a module factory function expression
with the module text inside.

In present implementations, it's possible to have globals available as
free variables by replacing the bottom-scope-chain object with one
begotten from globals.  I'd concede that this is a hack.

Here's a pseudo-code representation of our options and their ramifications:


running a module looks like:
   let require = Require(id);
   let exports = memo[id] = {};
   factory(require, exports);

if hermetic eval, like eval, immediately evaluates a
program and returns the last evaluated expression:

   creating a module factory function looks like:
       let factory = eval.hermetic(
           "(function(require,exports){" +
               moduleText +
           "/**/})"
       );

   PROBLEM: "return" statements ARE NOT an error.

   PROBLEM: "}); (function () {" attacks ARE possible.

   if bottom of the scope chain is global:

       if function statements are free assignment:

           PROBLEM: function statements should
           be local to modules.

       if function statements are function block
       local:

           no problem.

   if bottom of the scope chain is local:

       no problem with free assignment.

       if the bottom-scope is frozen:

           no problem

       if the bottom-scope is mutable:

           PROBLEM: module factories cannot be shared
           since data can be communicated among
           instances of the same module via their common
           bottom-scope-object.

if hermetic eval returns a module factory function
that evaluates a program with given a
(require, exports) upon invocation:

   let factory = eval.hermetic(moduleText);

   "return" statements ARE an error.

   "}); (function () {" attacks ARE NOT possible.

   if bottom of the scope chain is global:

       if global is mutable:

           PROBLEM: local variables are shared among
           all modules.

       if global is frozen:

           PROBLEM: local variable declarations are
           run-time errors.

       if function statements are free assignment:

           if free assignment binds to bottom-scope:

               PROBLEM: function statements are global.

           if free assignment binds to module scope:

               no problem.

       if function statements are function block
       local:

           PROBLEM: function statements are global.

   if bottom of the scope chain is local:

       PROBLEM: where do the primordials come from?
       Presumably, the primordials would have to be
       either copied onto the module local scope,
       or the module scope must be begotten from
       globals.

       if function statements are free assignment:

           no problem.

       if function statements are function block
       local:

           no problem.


> I've considered exposing the AST encoder as eval.parse. It's a cute trick to
> use eval as a namespace, tempting in order to minimize compatibility hits in
> adding to the global object. But it feels a little "off" here.

Agreed.  I'm not attached to the name.

Kris Kowal


More information about the es-discuss mailing list