Assignment of global variables deleted by their RHS in strict mode

# Kevin Gibbons (9 years ago)

See the following test262 test: tc39/test262/blob/master/test/language/expressions/assignment/S11.13.1_A5_T5.js (and related tests with update / compound assignment).

In short, it is possible to have a Reference to a global variable which has been deleted. Normally, bare assignments to undeclared variables in strict mode cause ReferenceErrors. However, calling PutValue on a reference to a global variable which has been deleted since the reference was created does not throw a ReferenceError in strict mode, even though, at the time of writing, that variable does not exist.

As far as I can tell, this is true in ES5 as well as ES6, but none of {V8, SpiderMonkey, JavaScriptCore, Nashorn} get it right. This is consistent and makes sense, but is it intentional?

# Mark S. Miller (9 years ago)

On Wed, Aug 26, 2015 at 2:55 PM, Kevin Gibbons <kevin at shapesecurity.com>

wrote:

See the following test262 test: tc39/test262/blob/master/test/language/expressions/assignment/S11.13.1_A5_T5.js (and related tests with update / compound assignment).

In short, it is possible to have a Reference to a global variable which has been deleted. Normally, bare assignments to undeclared variables in strict mode cause ReferenceErrors. However, calling PutValue on a reference to a global variable which has been deleted since the reference was created does not throw a ReferenceError in strict mode, even though, at the time of writing, that variable does not exist.

As far as I can tell, this is true in ES5 as well as ES6, but none of {V8, SpiderMonkey, JavaScriptCore, Nashorn} get it right. This is consistent and makes sense, but is it intentional?

Hmmm, interesting. It was not the intention of the strict mode design to allow this to slip by without a thrown error. If we had explicitly considered this issue during the early strict mode design, we definitely would have made this a thrown error of some sort. ReferenceError sounds good to me, but I can't say that we would not have decided on TypeError. Either seems plausible enough.

I agree that the silent failure implied by the current spec is buggy. Please file a bug against the ES6 spec. We should correct this at least in the errata.

Thanks for raising this! And thanks to André Bargull for writing that test case! I wish I was aware of it earlier.

# Kevin Gibbons (9 years ago)

Filed at ecmascript#4532

# Allen Wirfs-Brock (9 years ago)

On Aug 26, 2015, at 3:17 PM, Mark S. Miller wrote:

On Wed, Aug 26, 2015 at 2:55 PM, Kevin Gibbons <kevin at shapesecurity.com> wrote: See the following test262 test: tc39/test262/blob/master/test/language/expressions/assignment/S11.13.1_A5_T5.js (and related tests with update / compound assignment).

In short, it is possible to have a Reference to a global variable which has been deleted. Normally, bare assignments to undeclared variables in strict mode cause ReferenceErrors. However, calling PutValue on a reference to a global variable which has been deleted since the reference was created does not throw a ReferenceError in strict mode, even though, at the time of writing, that variable does not exist.

As far as I can tell, this is true in ES5 as well as ES6, but none of {V8, SpiderMonkey, JavaScriptCore, Nashorn} get it right. This is consistent and makes sense, but is it intentional?

Hmmm, interesting. It was not the intention of the strict mode design to allow this to slip by without a thrown error. If we had explicitly considered this issue during the early strict mode design, we definitely would have made this a thrown error of some sort. ReferenceError sounds good to me, but I can't say that we would not have decided on TypeError. Either seems plausible enough.

I agree that the silent failure implied by the current spec is buggy. Please file a bug against the ES6 spec. We should correct this at least in the errata.

A fix for this would be in 8.1.1.4.5, insert between the current steps 4 and 5:

4.5 If S is true, then a. Let stillHasBinding be ObjRec.HasBinding(N). b. ReturnIfAbrupt(stillHasBinding). c. If stillHasBinding is false, throw a ReferenceError exception.

# André Bargull (9 years ago)

A fix for this would be in 8.1.1.4.5, insert between the current steps 4 and 5:

4.5 If S is true, then a. Let stillHasBinding be ObjRec.HasBinding(N). b. ReturnIfAbrupt(stillHasBinding). c. If stillHasBinding is false, throw a ReferenceError exception.

I think the check should go into 8.1.1.2.5 SetMutableBinding. That way the similar issue for object environment records gets also fixed:

var scope = {x: 1};

with (scope) {
  (function() {
    "use strict";
    x = (delete scope.x, 2);
  })();
}

Except for SpiderMonkey, all other engines tested (Edge, Nashorn, JSC, V8) already throw a ReferenceError for the above snippet. But that's probably because engines don't implement the 'correct' Reference type semantics (ecmascript#4379).

  • André
# Allen Wirfs-Brock (9 years ago)

On Aug 27, 2015, at 1:03 AM, André Bargull wrote:

A fix for this would be in 8.1.1.4.5, insert between the current steps 4 and 5:

4.5 If S is true, then a. Let stillHasBinding be ObjRec.HasBinding(N). b. ReturnIfAbrupt(stillHasBinding). c. If stillHasBinding is false, throw a ReferenceError exception.

I think the check should go into 8.1.1.2.5 SetMutableBinding. That way the similar issue for object environment records gets also fixed:

var scope = {x: 1};

with (scope) {
 (function() {
   "use strict";
   x = (delete scope.x, 2);
 })();
}

Except for SpiderMonkey, all other engines tested (Edge, Nashorn, JSC, V8) already throw a ReferenceError for the above snippet. But that's probably because engines don't implement the 'correct' Reference type semantics (ecmascript#4379).

  • André

I think you are probably right. In which case it becomes step 2.5 in 8.1.1.2.5