From 61c6f8214bd0c6d72059578e80ce7659aff984bd Mon Sep 17 00:00:00 2001 From: jugglinmike Date: Mon, 19 Apr 2021 10:50:52 -0400 Subject: [PATCH] Add tests for stage 3 proposal "private fields in" (#2963) --- features.txt | 4 ++ .../expressions/in/private-field-in.js | 26 ++++++++++ ...private-field-invalid-assignment-target.js | 31 +++++++++++ ...rivate-field-invalid-identifier-complex.js | 31 +++++++++++ ...private-field-invalid-identifier-simple.js | 25 +++++++++ .../in/private-field-invalid-rhs.js | 26 ++++++++++ ...rivate-field-presence-accessor-shadowed.js | 47 +++++++++++++++++ .../in/private-field-presence-accessor.js | 33 ++++++++++++ .../private-field-presence-field-shadowed.js | 36 +++++++++++++ .../in/private-field-presence-field.js | 26 ++++++++++ .../private-field-presence-method-shadowed.js | 46 +++++++++++++++++ .../in/private-field-presence-method.js | 33 ++++++++++++ .../in/private-field-rhs-await-absent.js | 39 ++++++++++++++ .../in/private-field-rhs-await-present.js | 39 ++++++++++++++ .../in/private-field-rhs-non-object.js | 51 +++++++++++++++++++ .../in/private-field-rhs-unresolvable.js | 30 +++++++++++ .../in/private-field-rhs-yield-absent.js | 33 ++++++++++++ .../in/private-field-rhs-yield-present.js | 36 +++++++++++++ 18 files changed, 592 insertions(+) create mode 100644 test/language/expressions/in/private-field-in.js create mode 100644 test/language/expressions/in/private-field-invalid-assignment-target.js create mode 100644 test/language/expressions/in/private-field-invalid-identifier-complex.js create mode 100644 test/language/expressions/in/private-field-invalid-identifier-simple.js create mode 100644 test/language/expressions/in/private-field-invalid-rhs.js create mode 100644 test/language/expressions/in/private-field-presence-accessor-shadowed.js create mode 100644 test/language/expressions/in/private-field-presence-accessor.js create mode 100644 test/language/expressions/in/private-field-presence-field-shadowed.js create mode 100644 test/language/expressions/in/private-field-presence-field.js create mode 100644 test/language/expressions/in/private-field-presence-method-shadowed.js create mode 100644 test/language/expressions/in/private-field-presence-method.js create mode 100644 test/language/expressions/in/private-field-rhs-await-absent.js create mode 100644 test/language/expressions/in/private-field-rhs-await-present.js create mode 100644 test/language/expressions/in/private-field-rhs-non-object.js create mode 100644 test/language/expressions/in/private-field-rhs-unresolvable.js create mode 100644 test/language/expressions/in/private-field-rhs-yield-absent.js create mode 100644 test/language/expressions/in/private-field-rhs-yield-present.js diff --git a/features.txt b/features.txt index 3183a05d8c..7b93676623 100644 --- a/features.txt +++ b/features.txt @@ -299,3 +299,7 @@ __setter__ IsHTMLDDA host-gc-required + +# Ergonomic brand checks for Private Fields +# https://github.com/tc39/proposal-private-fields-in-in +class-fields-private-in diff --git a/test/language/expressions/in/private-field-in.js b/test/language/expressions/in/private-field-in.js new file mode 100644 index 0000000000..e246c5c43b --- /dev/null +++ b/test/language/expressions/in/private-field-in.js @@ -0,0 +1,26 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Requires the `In` parsing parameter +info: | + Syntax + RelationalExpression[In, Yield, Await]: + [...] + [+In]PrivateIdentifier in ShiftExpression[?Yield, ?Await] +esid: sec-relational-operators-runtime-semantics-evaluation +negative: + phase: parse + type: SyntaxError +features: [class-fields-private, class-fields-private-in] +---*/ + +$DONOTEVALUATE(); + +class C { + #field; + + constructor() { + for (#field in value;;) break; + } +} diff --git a/test/language/expressions/in/private-field-invalid-assignment-target.js b/test/language/expressions/in/private-field-invalid-assignment-target.js new file mode 100644 index 0000000000..df79191eb8 --- /dev/null +++ b/test/language/expressions/in/private-field-invalid-assignment-target.js @@ -0,0 +1,31 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Rejected as assignment target +info: | + 12.10.5 Static Semantics: AllPrivateIdentifiersValid + + AllPrivateIdentifiersValid is an abstract operation which takes names as an argument. + + RelationalExpression:PrivateIdentifierinShiftExpression + + 1. If StringValue of PrivateIdentifier is in names, return true. + 2. Return false. + +esid: sec-relational-operators-runtime-semantics-evaluation +negative: + phase: parse + type: SyntaxError +features: [class-fields-private, class-fields-private-in] +---*/ + +$DONOTEVALUATE(); + +class C { + #field; + + constructor() { + #field in {} = 0; + } +} diff --git a/test/language/expressions/in/private-field-invalid-identifier-complex.js b/test/language/expressions/in/private-field-invalid-identifier-complex.js new file mode 100644 index 0000000000..d31fb0411d --- /dev/null +++ b/test/language/expressions/in/private-field-invalid-identifier-complex.js @@ -0,0 +1,31 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Invalid private identifier - complex case +info: | + 12.10.5 Static Semantics: AllPrivateIdentifiersValid + + AllPrivateIdentifiersValid is an abstract operation which takes names as an argument. + + RelationalExpression:PrivateIdentifierinShiftExpression + + 1. If StringValue of PrivateIdentifier is in names, return true. + 2. Return false. + +esid: sec-relational-operators-runtime-semantics-evaluation +negative: + phase: parse + type: SyntaxError +features: [class-fields-private, class-fields-private-in] +---*/ + +$DONOTEVALUATE(); + +class C { + #a; + + constructor() { + #b in {}; + } +} diff --git a/test/language/expressions/in/private-field-invalid-identifier-simple.js b/test/language/expressions/in/private-field-invalid-identifier-simple.js new file mode 100644 index 0000000000..95933db383 --- /dev/null +++ b/test/language/expressions/in/private-field-invalid-identifier-simple.js @@ -0,0 +1,25 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Invalid private identifier - simple case +info: | + 12.10.5 Static Semantics: AllPrivateIdentifiersValid + + AllPrivateIdentifiersValid is an abstract operation which takes names as an argument. + + RelationalExpression:PrivateIdentifierinShiftExpression + + 1. If StringValue of PrivateIdentifier is in names, return true. + 2. Return false. + +esid: sec-relational-operators-runtime-semantics-evaluation +negative: + phase: parse + type: SyntaxError +features: [class-fields-private, class-fields-private-in] +---*/ + +$DONOTEVALUATE(); + +#name in {}; diff --git a/test/language/expressions/in/private-field-invalid-rhs.js b/test/language/expressions/in/private-field-invalid-rhs.js new file mode 100644 index 0000000000..a21e012519 --- /dev/null +++ b/test/language/expressions/in/private-field-invalid-rhs.js @@ -0,0 +1,26 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Syntactic grammar restricts right-hand side +info: | + Syntax + RelationalExpression[In, Yield, Await]: + [...] + [+In]PrivateIdentifier in ShiftExpression[?Yield, ?Await] +esid: sec-relational-operators-runtime-semantics-evaluation +negative: + phase: parse + type: SyntaxError +features: [class-fields-private, class-fields-private-in] +---*/ + +$DONOTEVALUATE(); + +class C { + #field; + + constructor() { + #field in () => {}; + } +} diff --git a/test/language/expressions/in/private-field-presence-accessor-shadowed.js b/test/language/expressions/in/private-field-presence-accessor-shadowed.js new file mode 100644 index 0000000000..d4d14e1ffa --- /dev/null +++ b/test/language/expressions/in/private-field-presence-accessor-shadowed.js @@ -0,0 +1,47 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Value when private name describes an accessor method +info: | + 7. Let privateName be ? GetValue(privateNameBinding). + 8. Assert: privateName is a Private Name. + [...] + 10. Else, + a. Assert: privateName.[[Kind]] is "method" or "accessor". + b. If PrivateBrandCheck(rval, privateName) is not an abrupt completion, + then return true. + 11. Return false. +esid: sec-relational-operators-runtime-semantics-evaluation +features: [class-static-methods-private, class-fields-private-in] +---*/ + +let Child; +let parentCount = 0; +let childCount = 0; + +class Parent { + get #accessor() { + parentCount += 1; + } + + static init() { + Child = class { + get #accessor() { + childCount += 1; + } + + static isNameIn(value) { + return #accessor in value; + } + }; + } +} + +Parent.init(); + +assert.sameValue(Child.isNameIn(new Parent()), false); +assert.sameValue(parentCount, 0, 'parent accessor not invoked'); +assert.sameValue(Child.isNameIn(new Child()), true); +assert.sameValue(childCount, 0, 'child accessor not invoked'); + diff --git a/test/language/expressions/in/private-field-presence-accessor.js b/test/language/expressions/in/private-field-presence-accessor.js new file mode 100644 index 0000000000..516151c876 --- /dev/null +++ b/test/language/expressions/in/private-field-presence-accessor.js @@ -0,0 +1,33 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Value when private name describes an accessor method +info: | + 7. Let privateName be ? GetValue(privateNameBinding). + 8. Assert: privateName is a Private Name. + [...] + 10. Else, + a. Assert: privateName.[[Kind]] is "method" or "accessor". + b. If PrivateBrandCheck(rval, privateName) is not an abrupt completion, + then return true. + 11. Return false. +esid: sec-relational-operators-runtime-semantics-evaluation +features: [class-static-methods-private, class-fields-private-in] +---*/ + +let count = 0; + +class Class { + get #accessor() { + count += 1; + } + + static isNameIn(value) { + return #accessor in value; + } +} + +assert.sameValue(Class.isNameIn({}), false); +assert.sameValue(Class.isNameIn(new Class()), true); +assert.sameValue(count, 0); diff --git a/test/language/expressions/in/private-field-presence-field-shadowed.js b/test/language/expressions/in/private-field-presence-field-shadowed.js new file mode 100644 index 0000000000..ef7790bb12 --- /dev/null +++ b/test/language/expressions/in/private-field-presence-field-shadowed.js @@ -0,0 +1,36 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Value when private name describes a field +info: | + 7. Let privateName be ? GetValue(privateNameBinding). + 8. Assert: privateName is a Private Name. + 9. If privateName.[[Kind]] is "field", + a. If ! PrivateFieldFind(privateName, rval) is not empty, then return true. + [...] + 11. Return false. +esid: sec-relational-operators-runtime-semantics-evaluation +features: [class-fields-private, class-fields-private-in] +---*/ + +let Child; + +class Parent { + #field; + + static init() { + Child = class { + #field; + + static isNameIn(value) { + return #field in value; + } + }; + } +} + +Parent.init(); + +assert.sameValue(Child.isNameIn(new Parent()), false); +assert.sameValue(Child.isNameIn(new Child()), true); diff --git a/test/language/expressions/in/private-field-presence-field.js b/test/language/expressions/in/private-field-presence-field.js new file mode 100644 index 0000000000..904eb5595b --- /dev/null +++ b/test/language/expressions/in/private-field-presence-field.js @@ -0,0 +1,26 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Value when private name describes a field +info: | + 7. Let privateName be ? GetValue(privateNameBinding). + 8. Assert: privateName is a Private Name. + 9. If privateName.[[Kind]] is "field", + a. If ! PrivateFieldFind(privateName, rval) is not empty, then return true. + [...] + 11. Return false. +esid: sec-relational-operators-runtime-semantics-evaluation +features: [class-fields-private, class-fields-private-in] +---*/ + +class Class { + #field; + + static isNameIn(value) { + return #field in value; + } +} + +assert.sameValue(Class.isNameIn({}), false); +assert.sameValue(Class.isNameIn(new Class()), true); diff --git a/test/language/expressions/in/private-field-presence-method-shadowed.js b/test/language/expressions/in/private-field-presence-method-shadowed.js new file mode 100644 index 0000000000..70cb72d30b --- /dev/null +++ b/test/language/expressions/in/private-field-presence-method-shadowed.js @@ -0,0 +1,46 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Value when private name describes a method +info: | + 7. Let privateName be ? GetValue(privateNameBinding). + 8. Assert: privateName is a Private Name. + [...] + 10. Else, + a. Assert: privateName.[[Kind]] is "method" or "accessor". + b. If PrivateBrandCheck(rval, privateName) is not an abrupt completion, + then return true. + 11. Return false. +esid: sec-relational-operators-runtime-semantics-evaluation +features: [class-methods-private, class-fields-private-in] +---*/ + +let Child; +let parentCount = 0; +let childCount = 0; + +class Parent { + #method() { + parentCount += 1; + } + + static init() { + Child = class { + #method() { + childCount += 1; + } + + static isNameIn(value) { + return #method in value; + } + }; + } +} + +Parent.init(); + +assert.sameValue(Child.isNameIn(new Parent()), false); +assert.sameValue(parentCount, 0, 'parent method not invoked'); +assert.sameValue(Child.isNameIn(new Child()), true); +assert.sameValue(childCount, 0, 'child method not invoked'); diff --git a/test/language/expressions/in/private-field-presence-method.js b/test/language/expressions/in/private-field-presence-method.js new file mode 100644 index 0000000000..c3858651e6 --- /dev/null +++ b/test/language/expressions/in/private-field-presence-method.js @@ -0,0 +1,33 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Value when private name describes a method +info: | + 7. Let privateName be ? GetValue(privateNameBinding). + 8. Assert: privateName is a Private Name. + [...] + 10. Else, + a. Assert: privateName.[[Kind]] is "method" or "accessor". + b. If PrivateBrandCheck(rval, privateName) is not an abrupt completion, + then return true. + 11. Return false. +esid: sec-relational-operators-runtime-semantics-evaluation +features: [class-methods-private, class-fields-private-in] +---*/ + +let count = 0; + +class Class { + #method() { + count += 1; + } + + static isNameIn(value) { + return #method in value; + } +} + +assert.sameValue(Class.isNameIn({}), false); +assert.sameValue(Class.isNameIn(new Class()), true); +assert.sameValue(count, 0); diff --git a/test/language/expressions/in/private-field-rhs-await-absent.js b/test/language/expressions/in/private-field-rhs-await-absent.js new file mode 100644 index 0000000000..7bf9fbf5db --- /dev/null +++ b/test/language/expressions/in/private-field-rhs-await-absent.js @@ -0,0 +1,39 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Parsing observes the `Await` production parameter when absent +info: | + Syntax + RelationalExpression[In, Yield, Await]: + [...] + [+In]PrivateIdentifier in ShiftExpression[?Yield, ?Await] + + [...] + + 1. Let privateIdentifier be the StringValue of PrivateIdentifier. + 2. Let rref be the result of evaluating ShiftExpression. + 3. Let rval be ? GetValue(rref). + 4. If Type(rval) is not Object, throw a TypeError exception. +esid: sec-relational-operators-runtime-semantics-evaluation +features: [class-fields-private, class-fields-private-in] +---*/ + +let value; +function await() { + return value; +} + +class C { + #field; + + static isNameIn() { + return #field in await(null); + } +} + +value = new C(); +assert.sameValue(C.isNameIn(), true); + +value = {}; +assert.sameValue(C.isNameIn(), false); diff --git a/test/language/expressions/in/private-field-rhs-await-present.js b/test/language/expressions/in/private-field-rhs-await-present.js new file mode 100644 index 0000000000..23dbb7e24b --- /dev/null +++ b/test/language/expressions/in/private-field-rhs-await-present.js @@ -0,0 +1,39 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Parsing observes the `Await` production parameter when present +info: | + Syntax + RelationalExpression[In, Yield, Await]: + [...] + [+In]PrivateIdentifier in ShiftExpression[?Yield, ?Await] + + [...] + + 1. Let privateIdentifier be the StringValue of PrivateIdentifier. + 2. Let rref be the result of evaluating ShiftExpression. + 3. Let rval be ? GetValue(rref). + 4. If Type(rval) is not Object, throw a TypeError exception. +esid: sec-relational-operators-runtime-semantics-evaluation +features: [class-fields-private, class-fields-private-in] +flags: [async] +---*/ + +class C { + #field; + + static async isNameIn(value) { + return #field in await(value); + } +} + +C.isNameIn(new C()) + .then(function(result) { + assert.sameValue(result, true); + + return C.isNameIn({}); + }) + .then(function(result) { + assert.sameValue(result, false); + }).then($DONE, $DONE); diff --git a/test/language/expressions/in/private-field-rhs-non-object.js b/test/language/expressions/in/private-field-rhs-non-object.js new file mode 100644 index 0000000000..d540345884 --- /dev/null +++ b/test/language/expressions/in/private-field-rhs-non-object.js @@ -0,0 +1,51 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Algorithm interrupted by non-object right-hand side +info: | + Syntax + RelationalExpression[In, Yield, Await]: + [...] + [+In]PrivateIdentifier in ShiftExpression[?Yield, ?Await] + + [...] + + 1. Let privateIdentifier be the StringValue of PrivateIdentifier. + 2. Let rref be the result of evaluating ShiftExpression. + 3. Let rval be ? GetValue(rref). + 4. If Type(rval) is not Object, throw a TypeError exception. +esid: sec-relational-operators-runtime-semantics-evaluation +features: [class-fields-private, class-fields-private-in] +---*/ + +let caught = null; + +class C { + #field; + + constructor() { + try { + /** + * Using a ShiftExpression to produce the non-object value verifies that + * the implementation uses the operator precedence implied by the + * syntactic grammar. In other words, the following statement should be + * interpreted as: + * + * #field in ({} << 0); + * + * ...rather than: + * + * (#field in {}) << 0; + */ + #field in {} << 0; + } catch (error) { + caught = error; + } + } +} + +new C(); + +assert.notSameValue(caught, null); +assert.sameValue(caught.constructor, TypeError); diff --git a/test/language/expressions/in/private-field-rhs-unresolvable.js b/test/language/expressions/in/private-field-rhs-unresolvable.js new file mode 100644 index 0000000000..7f593d3d2d --- /dev/null +++ b/test/language/expressions/in/private-field-rhs-unresolvable.js @@ -0,0 +1,30 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Algorithm interrupted by unresolvable reference +info: | + 1. Let privateIdentifier be the StringValue of PrivateIdentifier. + 2. Let rref be the result of evaluating ShiftExpression. + 3. Let rval be ? GetValue(rref). +esid: sec-relational-operators-runtime-semantics-evaluation +features: [class-fields-private, class-fields-private-in] +---*/ + +let caught = null; + +class C { + #field; + constructor() { + try { + #field in test262unresolvable; + } catch (error) { + caught = error; + } + } +} + +new C(); + +assert.notSameValue(caught, null); +assert.sameValue(caught.constructor, ReferenceError); diff --git a/test/language/expressions/in/private-field-rhs-yield-absent.js b/test/language/expressions/in/private-field-rhs-yield-absent.js new file mode 100644 index 0000000000..682d13a87d --- /dev/null +++ b/test/language/expressions/in/private-field-rhs-yield-absent.js @@ -0,0 +1,33 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Parsing observes the `Yield` production parameter when present +info: | + Syntax + RelationalExpression[In, Yield, Await]: + [...] + [+In]PrivateIdentifier in ShiftExpression[?Yield, ?Await] + + [...] + + 1. Let privateIdentifier be the StringValue of PrivateIdentifier. + 2. Let rref be the result of evaluating ShiftExpression. + 3. Let rval be ? GetValue(rref). + 4. If Type(rval) is not Object, throw a TypeError exception. +esid: sec-relational-operators-runtime-semantics-evaluation +negative: + phase: parse + type: SyntaxError +features: [class-fields-private, class-fields-private-in] +---*/ + +$DONOTEVALUATE(); + +class C { + #field; + + static method() { + #field in yield; + } +} diff --git a/test/language/expressions/in/private-field-rhs-yield-present.js b/test/language/expressions/in/private-field-rhs-yield-present.js new file mode 100644 index 0000000000..fdb9371d7f --- /dev/null +++ b/test/language/expressions/in/private-field-rhs-yield-present.js @@ -0,0 +1,36 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Parsing observes the `Yield` production parameter when present +info: | + Syntax + RelationalExpression[In, Yield, Await]: + [...] + [+In]PrivateIdentifier in ShiftExpression[?Yield, ?Await] + + [...] + + 1. Let privateIdentifier be the StringValue of PrivateIdentifier. + 2. Let rref be the result of evaluating ShiftExpression. + 3. Let rval be ? GetValue(rref). + 4. If Type(rval) is not Object, throw a TypeError exception. +esid: sec-relational-operators-runtime-semantics-evaluation +features: [class-fields-private, class-fields-private-in] +---*/ + +class C { + #field; + + static *isNameIn() { + return #field in (yield); + } +} + +let iter1 = C.isNameIn(); +iter1.next(); +assert.sameValue(iter1.next(new C()).value, true); + +let iter2 = C.isNameIn(); +iter2.next(); +assert.sameValue(iter2.next({}).value, false);