Here is the first draft of the spec for strict mode in ES4. Since ES3.1 will probably also incorporate a strict mode I have tried to be mindful of that in a couple of places (notably with respect to the syntax).
--larsTitle: Strict mode
Strict mode
NAME: "Strict mode" FILE: spec/language/strict-mode.html CATEGORY: ? SOURCES: ? SPEC AUTHOR: Lars DRAFT STATUS: DRAFT 1 - 2008-04-10 REVIEWED AGAINST ES3: N/A REVIEWED AGAINST ERRATA: N/A REVIEWED AGAINST BASE DOC: N/A REVIEWED AGAINST PROPOSALS: NO REVIEWED AGAINST CODE: NO REVIEWED AGAINST TICKETS: NO IMPLEMENTATION STATUS: ? TEST CASE STATUS: ?
Synopsis and background
Syntax
The pragma "use strict" enables strict mode for code within the scope of the pragma.
The pragma takes the form of either an ES4 pragma or a do-nothing
one-armed if statement:
"use" "strict"
"if" "(" "false" ")" "use" "(" "strict" ")"
NOTE The second form of the "use strict" pragma is a concession to ES3.1, because that form of the pragma is compatible with ES3 syntax. It is not recommended for ES4 code.
There are two ways the "use strict" pragma can be used, depending on the context.
In one use, the pragma can occur at the outermost level of the
program, at the top level of a package block, or at the top level of
(arbitrarily nested) top-level blocks that are not dependents of
control flow statements. In these cases, the scope of the pragma
extends from the end of the pragma until the end of the program, the
end of the package block, or the end of the top-level block, or until
a "use standard" pragma is encountered. The "use standard" pragma
takes the form of either an ES4 pragma or a do-nothing one-armed
if statement:
"use" "standard"
"if" "(" "false" ")" "use" "(" "standard" ")"
NOTE The "use standard" pragma is a concession to programs that intermix standard and strict parts. It is restricted to the top level because ambiguities would result if it were to be used inside other scopes.
In another use, the "use strict" pragma can occur inside any class body, function body, or block that is the dependent of a control flow statement. In these cases, the pragma can be preceded in the body or block only by other pragmas, and the "use standard" pragma is not allowed. In these cases, the scope of the pragma is the entire class, function, or block, with all nested functions or blocks.
In the following, when the phrasing is used that a function or a piece of code "is strict" then the meaning is that the scope of some strict mode pragma includes the function or piece of code.
Run-time checks
this never captures the global object
In ES3, when a function is called "as a function" (that is, not as
a method on some receiver object -- the notion is not syntactic, but
can depend on the binding of the function) the value of this
passed to the function is null. The callee (or the call protocol)
substitutes the callee's global object for the null value, so the
value of this observed in the callee is the callee's global
object.
If the callee is strict, however, the null is not converted to
the callee's global object. Instead, if the callee evaluates the
_expression_ this when the value of this is null then a
ReferenceError is thrown.
FIXME We want to make that more precise but the current prose captures the intent well enough.
Writing to properties
If an assignment _expression_ that is strict would write to a read-only property or variable then a ReferenceError is thrown.
Creating global variables
If an assignment _expression_ that is strict would create a new property on the global object (regardless of whether the assignment is to a variable or to a property on an object that turns out to be the global object) then a ReferenceError is thrown.
Deleting properties
If a delete _expression_ that is strict would delete a variable
or property that is a fixture or that is marked as not removable, then
a ReferenceError is thrown.
If a delete _expression_ that is strict would delete a variable
that is not in scope or a property that is not an own property on the
object from which it were to be deleted, then a ReferenceError is
thrown.
Arity checking
If a function that is strict is called with fewer or more arguments than it expects then a TypeError is thrown.
arguments
If a function is strict then its arguments object does not
share storage with the formal parameters of the function, and those
properties of the arguments object that correspond to the formal
parameters, as well as the length property of the arguments
object, are neither writeable nor removable.
If a function is strict and the implementation supports the ES1
style FunctionObject.arguments facility, then a ReferenceError
is thrown if the arguments property is accessed on any instance of
the function.
eval
If the eval operator is strict and attempts to introduce a new binding into its inherited variable binding object then a ReferenceError is thrown (even if that binding object is the global object).
If the eval operator is strict then the program it evaluates is also strict.
NOTE The global eval function is not strict.
Compile-time checks
A SyntaxError is thrown if any of the following conditions are violated.
with
A with statement cannot be strict.
Names
A strict function cannot have duplicate parameter names.
A strict function cannot bind a name by var or by a top-level
function definition if that name is also the name of a parameter to
the function or the name arguments.
A strict function cannot bind a name more than once by var or
by a top-level function definition.
NOTE Duplicate definitions of names by function, const, or
let inside a block is already illegal in the language (I hope).
Strict object initializers cannot have duplicate property names.
Candidates for inclusion
I'm uncertain about the following because they require levels of analysis that I don't want to burden lightweight implementations with. I believe they fit in better with an optional verifier (part of a tool chain). Lightweight variants can be defined but it's unclear how valuable those would be.
Reference before definition
(Heavyweight variant) In a strict function it is a static error if the compiler cannot prove, by definite assignment analysis for example, that a variable has always been initialized at every point where it is referenced.
(Lightweight variant) In a strict function it is a static error if the compiler can prove, by simple forward attribute propagation for example, that a variable is never initialized at some point when it is referenced. Consider for example this:
function f(y) {
let x;
if (y == 1)
return x; // clear error
return x; // clear error
while (true) { // backward edge kills analysis
if (y == 1)
return x; // not an error
}
}
In my opinion the heavyweight variant should not be mandated and the lightweight variant is not very interesting, but it can be interesting to discuss it.
Effect-free statements
Provably effect-free statements cannot occur in strict code.
There is a simple and fairly lightweight analysis here based on attribute synthesis in expressions: constants and references to variables and fields that are known not to invoke getters are effect-free; the results of primitive operations on useless values are effect-free (provided the types are known so that conversions can be taken into account); if an _expression_ statement has an effect-free _expression_ then the statement is effect-free and illegal.
Again it's unclear how useful this is and whether it's not better considered as a part of a verifier.
Partial rationale
In a number of cases the utility of the strict mode restrictions should be self-evident and won't be justified here. In some cases it is not.
this never captures the global object
The utility of this is that functions don't gain access to the
global object accidentally. Since functions can't introduce new
bindings in the global object in strict mode the restriction is more
defense-in-depth than anything (after all, in ES4 the global object is
available through the global variable global).
NOTE Standard mode in ES4 and ES3.1 might already end up throwing an
exception on access to this if the function was called as a
function; that has yet to be settled. However, that kind of a change
is a real compatibility hazard, and allowing the program to opt in to
the change by asking for strict mode is better.
arguments
The utility of making the arguments object read-only is that the aliasing with the formal parameters is unlikely to be desirable as the common case.
The utility of making access to FunctionObject.arguments throw
an exception is that the mechanism, if unchecked, allows arbitrary
functions to look at and modify the arguments and formal
parameters of active methods. This is a privacy and security problem.
NOTE The change that makes the arguments object read-only fixes the problem where arbitrary code can change the formals of an active function, but not the problem where arbitrary can could inspect those formals.
NOTE It's daring of us to standardize restrictions on the behavior of a feature that's not itself specified by the Standard.
One suggestion that was made that I have not adopted in this draft
is that if a reference to arguments is visible in the code of a
strict function then the formals should be made read-only so that the
captured arguments array would always hold the same values as the
formals. The problem with this, of course, is that eval can
reference arguments without that reference being visible when the
function is entered. So what should happen if arguments is
referenced by eval code after one of the formals has been changed?
Is the arguments object captured at that point (not on function
entry), and are the formals made read-only at that point? It doesn't
seem to be worth the bother to do this.
_______________________________________________ Es4-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es4-discuss
