Skip to content

Instantly share code, notes, and snippets.

@kriskowal
Created October 13, 2010 23:29
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kriskowal/625182 to your computer and use it in GitHub Desktop.
Save kriskowal/625182 to your computer and use it in GitHub Desktop.

Module Constructor

This is a proposal for the inclusion of an additional primordial variable to all ECMAScript contexts.

  • There MUST be a primordial function Module(text, fileName, lineNo)
    • text MUST be parsed the body of a strict-mode Function
    • Module MAY throw a SyntaxError
    • fileName is an optional name for the text, for debuggers
    • lineNo defaults to 1, is coerced to Number internally, and represents the line number corresponding to the beginning of the text within the file indicated by fileName
    • Module MAY be called as a constructor or as a function. Its behavior must be identical in either case.
    • Module returns a Function (for illustration: module(scope))
      • scope is internally coerced to an Object
      • when module is called
        • the function described in text is run in a lexical scope that contains
          • the primordials (Non normatively, these include Array and Object but not legacy, embedding-specific values like window; a citation to a comprehensive list in the specification is needed here)
          • preceding the primordials, a variable corresponding to each owned property of scope
          • such that any free variable in text that is neither mentioned in the scope object or is a primordial throws a ReferenceError at the point it is used.
        • the module returns the value returned by the function
      • the Module instance MUST own a requirements property
        • requirements are an Array
        • requirements must contain the string corresponding to each occurrence of the free variable require that exists in text in which require is called as a function with the String as its argument
      • the Module instance MUST own the fileName property corresponding to the fileName given to the Module constructor.
      • the Module instance MUST own the lineNo property corresponding to the lineNo given to the Module constructor.
@ihab
Copy link

ihab commented Oct 14, 2010

(1) By your proposal, if the text in Module(text) contains require('foo') then:

  • Is that an Expression or a Statement?
  • If an Expression, what does it evaluate to at run time? If a Statement, what are its side effects on execution?

(2) What are the properties regarding the primordials (Array, Object, ...)? Are these simply inherited from the calling context verbatim (this being a proposal to reduce boilerplate, if the programmer is worried, they can just ES5 freeze them)?

@ihab
Copy link

ihab commented Oct 14, 2010

(3) In a browser, the text is often not accessible cross-origin. Is it your viewpoint that http://www.w3.org/TR/cors/ will be adequate for this purpose?

@kriskowal
Copy link
Author

  1. There are no additional implications made by this proposal about run-time evaluation. require('foo') is not special at run-time, and runs as a normal expression. This proposal also does not provide any constraints on what "require" is, and does not even require it to be provided in the "scope". Thus, this "Module" construct is generally useful, and provides a decent heuristic for "requirements" if it happens to be used to evaluate a CommonJS module in an asynchronous client-side module loader. A shim for this proposal is easy enough to implement today but such implementations cannot provide debugging help and necessarily have a weaker heuristic for requirements. This is a solid case for progressive enhancement.
  2. Primordials would be inherited from the context in which the Module primordial is acquired. It is the prorogative of the module loader to arrange for a fresh context or to freeze their current context.

@ihab
Copy link

ihab commented Oct 14, 2010

(4) Evaluating the require-ments of a module would appear to require a full parse of the text. Some browser implementors (specifically, Apple/WebKit) have noted that their VMs perform as shallow a parse as possible for efficiency. Would it significantly restrict the usability of your proposal were you to stipulate that require statements must occur as a group at the top of the text, after any use directives but before any other content?

@kriskowal
Copy link
Author

Ihab, in response to (3), text can be acquired in a number of ways and there are a number of moving-parts: development, production, logical modularity, parallel or serial transportation, and security. A thorough analysis is too complicated to provide here, but in short, I think that there are adequate solutions to transporting modules for every combination of requirements, but there is no single solution that is adequate for every combination of requirements.

@kriskowal
Copy link
Author

Ihab, in response to (4), yes, it would be too narrow a constraint for require calls to be limited to some pattern in the header of the code; it would add too much complexity for us meager developers who have the right to assume require calls will work just about anywhere. I trust that there are enough short-circuits implied by the requirements search that it would not significantly affect performance to collect a list of strings in an existing visit of the token stream. The search need not be performed unless the parse is run in conjunction with a Module constructor. The entire search can be aborted in a lexical context in which require is no longer a free variable. And so on.

@erights
Copy link

erights commented Oct 15, 2010

Nowhere does this spec currently say that text must not contain any free variables that are not own properties of scope. Without this constraint, we don't have confinement and we still have the global object at the bottom of the scope chain.

@kriskowal
Copy link
Author

I'm not sure how to express a requirement that would achieve "containment". Does "strict-mode" not sweep the global object off the scope chain, guaranteeing that the only free variables outside the module scope are owned properties of the global object? Will it; is this an orthogonal concern that this specification can ride on? What verbiage would narrow it enough: "when a module function is called, an exception must be thrown if the text contains free variables that are not matched by owned properties of the global record or module scope record? Would that place too high a burden on implementations, requiring free variables to be tracked in the parse tree? Would it not be better to have these remain Reference errors and just tighten the lexical lookup code so it doesn't traverse the prototype of the scope and global objects? That would just leave the issue of freezing primordial object tree, which I think is a separable act.

@erights
Copy link

erights commented Oct 17, 2010

Confinement actually. Simple. Rather than saying that all the own properties of the object become defining variables in the scope object, go the other way: say that all free variables in text are looked in the scope object. If they are not present in the scope object, a ReferenceError is thrown.

@kriskowal
Copy link
Author

@erights, Is the intention that primordials be included on the scope object? This implies that anyone using a module would need to explicitly provide such an object. From the usability perspective, I think I prefer that properties owned by the global scope be implicitly communicated, and anything else fall through at run time with a ReferenceError.

@erights
Copy link

erights commented Oct 20, 2010

The globals defined by ES5 -- such as Array, Object, etc -- could (and probably should) be provided by default, since (once frozen) they convey no authority. Other globals, such as window or document in a browser environment, should not. If they are not explicitly provided on the scope object, then they are denied.

@kriskowal
Copy link
Author

I need a reference to the comprehensive list of ES primordials. Hopefully the revision above addresses the issue of containment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment