tc39 / test262

Official ECMAScript Conformance Test Suite
Other
2.4k stars 467 forks source link

Missing tests for bindings that shadow globalThis properties #4332

Open gibson042 opened 6 days ago

gibson042 commented 6 days ago

eval, globalThis, NaN, Infinity, undefined, etc. are valid names for bindings (i.e., they are not keywords) and exist as such on the global object (the last three as non-configurable and non-writable). However, several implementations seem to treat them specially in ways that violate the specification (as seen below for Hermes and Moddable XS and QuickJS). Test262 should have coverage for such cases, although I'm never quite sure where in the hierarchy to put this kind of broad syntax verification... maybe generated tests in test/language/reserved-words?

print('START'); const %s = 1;

This should succeed with all names except NaN/Infinity/undefined, for which there should be a SyntaxError before anything happens because ScriptEvaluation starts with GlobalDeclarationInstantiation, which tests each lexically declared name against Global Environment Record HasRestrictedGlobalProperty and enforces that «A global lexical binding may not be created that has the same name as a non-configurable property of the global object… The global property "undefined" is an example of such a property».

But Hermes fails to throw the required SyntaxError (although it does not create observable changes).

```console ## Source print('START'); const NaN = 1; const Infinity = 1; const undefined = 1; print([NaN, Infinity, undefined].some(v => v === 1) ? 'FAIL WITH EFFECTS' : 'FAIL WITHOUT EFFECTS'); #### Hermes START FAIL WITHOUT EFFECTS ```

{ const %s = 1; print(%s === 1 ? 'PASS' : 'FAIL'); }

Within a block, there should be no restrictions on these names.

But Hermes fails to create the NaN/Infinity/undefined bindings (i.e., it does not shadow the global property), as does Moddable XS (but only when the name is undefined), and QuickJS throws a SyntaxError before evaluating anything when the name is undefined.

```console $ for name in eval globalThis NaN Infinity undefined; do eshost -six "print('START'); { const $name = 1; print($name === 1 ? 'PASS' : 'FAIL'); }" done ## Source print('START'); { const eval = 1; print(eval === 1 ? 'PASS' : 'FAIL'); } #### engine262, GraalJS, Hermes, JavaScriptCore, Moddable XS, QuickJS, SpiderMonkey, V8 START PASS ## Source print('START'); { const globalThis = 1; print(globalThis === 1 ? 'PASS' : 'FAIL'); } #### engine262, GraalJS, Hermes, JavaScriptCore, Moddable XS, QuickJS, SpiderMonkey, V8 START PASS ## Source print('START'); { const NaN = 1; print(NaN === 1 ? 'PASS' : 'FAIL'); } #### engine262, GraalJS, JavaScriptCore, Moddable XS, QuickJS, SpiderMonkey, V8 START PASS #### Hermes START FAIL ## Source print('START'); { const Infinity = 1; print(Infinity === 1 ? 'PASS' : 'FAIL'); } #### engine262, GraalJS, JavaScriptCore, Moddable XS, QuickJS, SpiderMonkey, V8 START PASS #### Hermes START FAIL ## Source print('START'); { const undefined = 1; print(undefined === 1 ? 'PASS' : 'FAIL'); } #### engine262, GraalJS, JavaScriptCore, SpiderMonkey, V8 START PASS #### Hermes, Moddable XS START FAIL #### QuickJS SyntaxError: invalid lexical variable name ```

(function(%s) { print(%s === 1 ? 'PASS' : 'FAIL'); })(1);

Use as a simple function parameter name should be allowed.

But Moddable XS does not bind the argument value to a parameter named undefined.

```console $ for name in eval globalThis NaN Infinity undefined; do eshost -six "(function($name) { print($name === 1 ? 'PASS' : 'FAIL'); })(1);" done ## Source (function(eval) { print(eval === 1 ? 'PASS' : 'FAIL'); })(1); #### engine262, GraalJS, Hermes, JavaScriptCore, Moddable XS, QuickJS, SpiderMonkey, V8 PASS ## Source (function(globalThis) { print(globalThis === 1 ? 'PASS' : 'FAIL'); })(1); #### engine262, GraalJS, Hermes, JavaScriptCore, Moddable XS, QuickJS, SpiderMonkey, V8 PASS ## Source (function(NaN) { print(NaN === 1 ? 'PASS' : 'FAIL'); })(1); #### engine262, GraalJS, Hermes, JavaScriptCore, Moddable XS, QuickJS, SpiderMonkey, V8 PASS ## Source (function(Infinity) { print(Infinity === 1 ? 'PASS' : 'FAIL'); })(1); #### engine262, GraalJS, Hermes, JavaScriptCore, Moddable XS, QuickJS, SpiderMonkey, V8 PASS ## Source (function(undefined) { print(undefined === 1 ? 'PASS' : 'FAIL'); })(1); #### engine262, GraalJS, Hermes, JavaScriptCore, QuickJS, SpiderMonkey, V8 PASS #### Moddable XS FAIL ```

(function({ %s }) { print(%s === 1 ? 'PASS' : 'FAIL'); })({ %s: 1 });

Use as a destructured function parameter name should be allowed.

But Moddable XS does not bind the argument value to a parameter named undefined.

```console $ for name in eval globalThis NaN Infinity undefined; do eshost -six "(function({ $name }) { print($name === 1 ? 'PASS' : 'FAIL'); })({ $name: 1 });" done ## Source (function({ eval }) { print(eval === 1 ? 'PASS' : 'FAIL'); })({ eval: 1 }); #### engine262, GraalJS, Hermes, JavaScriptCore, Moddable XS, QuickJS, SpiderMonkey, V8 PASS ## Source (function({ globalThis }) { print(globalThis === 1 ? 'PASS' : 'FAIL'); })({ globalThis: 1 }); #### engine262, GraalJS, Hermes, JavaScriptCore, Moddable XS, QuickJS, SpiderMonkey, V8 PASS ## Source (function({ NaN }) { print(NaN === 1 ? 'PASS' : 'FAIL'); })({ NaN: 1 }); #### engine262, GraalJS, Hermes, JavaScriptCore, Moddable XS, QuickJS, SpiderMonkey, V8 PASS ## Source (function({ Infinity }) { print(Infinity === 1 ? 'PASS' : 'FAIL'); })({ Infinity: 1 }); #### engine262, GraalJS, Hermes, JavaScriptCore, Moddable XS, QuickJS, SpiderMonkey, V8 PASS ## Source (function({ undefined }) { print(undefined === 1 ? 'PASS' : 'FAIL'); })({ undefined: 1 }); #### engine262, GraalJS, Hermes, JavaScriptCore, QuickJS, SpiderMonkey, V8 PASS #### Moddable XS FAIL ```