diff --git a/features.txt b/features.txt index 81e64d71e68..d671ccf3923 100644 --- a/features.txt +++ b/features.txt @@ -109,6 +109,10 @@ promise-with-resolvers # https://github.com/tc39/proposal-set-methods set-methods +# AsyncContext +# https://github.com/tc39/proposal-async-context +AsyncContext + ## Standard language features # # Language features that have been included in a published version of the diff --git a/test/built-ins/AsyncContext/Snapshot/constructor.js b/test/built-ins/AsyncContext/Snapshot/constructor.js new file mode 100644 index 00000000000..4c131d56539 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/constructor.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot-constructor +description: > + The AsyncContext.Snapshot constructor is the %AsyncContext.Snapshot% + intrinsic object and the initial value of the Snapshot property of the + %AsyncContext% object. +features: [AsyncContext] +---*/ + +assert.sameValue( + typeof AsyncContext.Snapshot, 'function', + 'typeof AsyncContext.Snapshot is function' +); diff --git a/test/built-ins/AsyncContext/Snapshot/instance-extensible.js b/test/built-ins/AsyncContext/Snapshot/instance-extensible.js new file mode 100644 index 00000000000..9ac6cbce85a --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/instance-extensible.js @@ -0,0 +1,11 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot +description: Instances of AsyncContext.Snapshot are extensible +features: [AsyncContext] +---*/ + +var asyncSnapshot = new AsyncContext.Snapshot(); +assert.sameValue(Object.isExtensible(asyncSnapshot), true); diff --git a/test/built-ins/AsyncContext/Snapshot/instance-prototype.js b/test/built-ins/AsyncContext/Snapshot/instance-prototype.js new file mode 100644 index 00000000000..8138ce53156 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/instance-prototype.js @@ -0,0 +1,20 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.prototype +description: > + The initial value of AsyncContext.Snapshot.prototype is the + AsyncContext.Snapshot prototype object. +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +assert.sameValue( + AsyncContext.Snapshot.prototype.isPrototypeOf(new AsyncContext.Snapshot()), true, + 'AsyncContext.Snapshot.prototype.isPrototypeOf(new AsyncContext.Snapshot()) returns true' +); + +verifyNotEnumerable(AsyncContext.Snapshot, 'prototype'); +verifyNotWritable(AsyncContext.Snapshot, 'prototype'); +verifyNotConfigurable(AsyncContext.Snapshot, 'prototype'); diff --git a/test/built-ins/AsyncContext/Snapshot/internal-prototype.js b/test/built-ins/AsyncContext/Snapshot/internal-prototype.js new file mode 100644 index 00000000000..42591148e18 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/internal-prototype.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-properties-of-the-asynccontext-snapshot-constructor +description: > + The AsyncContext.Snapshot constructor has a [[Prototype]] internal slot whose + value is %Function.prototype%. +features: [AsyncContext] +---*/ + +assert.sameValue( + Function.prototype.isPrototypeOf(AsyncContext.Snapshot), + true, + 'Function.prototype.isPrototypeOf(AsyncContext.Snapshot) returns true' +); diff --git a/test/built-ins/AsyncContext/Snapshot/is-a-constructor.js b/test/built-ins/AsyncContext/Snapshot/is-a-constructor.js new file mode 100644 index 00000000000..d1892e08eeb --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/is-a-constructor.js @@ -0,0 +1,12 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot-constructor +description: The AsyncContext.Snapshot constructor implements [[Construct]] +includes: [isConstructor.js] +features: [AsyncContext, Reflect.construct] +---*/ + +assert.sameValue(isConstructor(AsyncContext.Snapshot), true, 'isConstructor(AsyncContext.Snapshot) must return true'); +new AsyncContext.Snapshot(); diff --git a/test/built-ins/AsyncContext/Snapshot/length.js b/test/built-ins/AsyncContext/Snapshot/length.js new file mode 100644 index 00000000000..a8930bc1f57 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/length.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot +description: AsyncContext.Snapshot.length property descriptor +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +verifyProperty(AsyncContext.Snapshot, 'length', { + value: 0, + writable: false, + enumerable: false, + configurable: true +}); diff --git a/test/built-ins/AsyncContext/Snapshot/name.js b/test/built-ins/AsyncContext/Snapshot/name.js new file mode 100644 index 00000000000..538190e47dc --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/name.js @@ -0,0 +1,15 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot +description: AsyncContext.Snapshot.name value and descriptor +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +assert.sameValue(AsyncContext.Snapshot.name, 'Snapshot', 'The value of AsyncContext.Snapshot.name is "Snapshot"'); + +verifyNotEnumerable(AsyncContext.Snapshot, 'name'); +verifyNotWritable(AsyncContext.Snapshot, 'name'); +verifyConfigurable(AsyncContext.Snapshot, 'name'); diff --git a/test/built-ins/AsyncContext/Snapshot/newtarget-prototype-is-not-object.js b/test/built-ins/AsyncContext/Snapshot/newtarget-prototype-is-not-object.js new file mode 100644 index 00000000000..0d33745510b --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/newtarget-prototype-is-not-object.js @@ -0,0 +1,57 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot +description: > + [[Prototype]] defaults to %AsyncContext.Snapshot.prototype% if NewTarget.prototype is not an object. +info: | + AsyncContext.Snapshot ( ) + + ... + 3. Let asyncSnapshot be ? OrdinaryCreateFromConstructor(NewTarget, "%AsyncContext.Snapshot.prototype%", « [[AsyncSnapshotMapping]] »). + ... + 5. Return asyncSnapshot. + + OrdinaryCreateFromConstructor ( constructor, intrinsicDefaultProto [ , internalSlotsList ] ) + + ... + 2. Let proto be ? GetPrototypeFromConstructor(constructor, intrinsicDefaultProto). + 3. Return ObjectCreate(proto, internalSlotsList). + + GetPrototypeFromConstructor ( constructor, intrinsicDefaultProto ) + + 3. Let proto be ? Get(constructor, 'prototype'). + 4. If Type(proto) is not Object, then + a. Let realm be ? GetFunctionRealm(constructor). + b. Set proto to realm's intrinsic object named intrinsicDefaultProto. + 5. Return proto. +features: [AsyncContext, Reflect.construct, Symbol] +---*/ + +var asyncSnapshot; +function newTarget() { } + +newTarget.prototype = undefined; +asyncSnapshot = Reflect.construct(AsyncContext.Snapshot, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncSnapshot), AsyncContext.Snapshot.prototype, 'newTarget.prototype is undefined'); + +newTarget.prototype = null; +asyncSnapshot = Reflect.construct(AsyncContext.Snapshot, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncSnapshot), AsyncContext.Snapshot.prototype, 'newTarget.prototype is null'); + +newTarget.prototype = true; +asyncSnapshot = Reflect.construct(AsyncContext.Snapshot, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncSnapshot), AsyncContext.Snapshot.prototype, 'newTarget.prototype is a Boolean'); + +newTarget.prototype = ''; +asyncSnapshot = Reflect.construct(AsyncContext.Snapshot, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncSnapshot), AsyncContext.Snapshot.prototype, 'newTarget.prototype is a String'); + +newTarget.prototype = Symbol(); +asyncSnapshot = Reflect.construct(AsyncContext.Snapshot, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncSnapshot), AsyncContext.Snapshot.prototype, 'newTarget.prototype is a Symbol'); + +newTarget.prototype = 1; +asyncSnapshot = Reflect.construct(AsyncContext.Snapshot, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncSnapshot), AsyncContext.Snapshot.prototype, 'newTarget.prototype is a Number'); diff --git a/test/built-ins/AsyncContext/Snapshot/prop-desc.js b/test/built-ins/AsyncContext/Snapshot/prop-desc.js new file mode 100644 index 00000000000..0749287e791 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prop-desc.js @@ -0,0 +1,22 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot-constructor +description: > + Property descriptor of AsyncContext.Snapshot +info: | + 17 ECMAScript Standard Built-in Objects: + + Every other data property described in clauses 18 through 26 and in Annex B.2 + has the attributes { [[Writable]]: true, [[Enumerable]]: false, + [[Configurable]]: true } unless otherwise specified. +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +verifyProperty(AsyncContext, 'Snapshot', { + enumerable: false, + writable: true, + configurable: true +}); diff --git a/test/built-ins/AsyncContext/Snapshot/proto-from-ctor-realm.js b/test/built-ins/AsyncContext/Snapshot/proto-from-ctor-realm.js new file mode 100644 index 00000000000..fee87a3a451 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/proto-from-ctor-realm.js @@ -0,0 +1,58 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot +description: Default [[Prototype]] value derived from realm of the newTarget +info: | + AsyncContext.Snapshot ( ) + + ... + 3. Let asyncSnapshot be ? OrdinaryCreateFromConstructor(NewTarget, "%AsyncContext.Snapshot.prototype%", « [[AsyncSnapshotMapping]] »). + ... + 5. Return asyncSnapshot. + + OrdinaryCreateFromConstructor ( constructor, intrinsicDefaultProto [ , internalSlotsList ] ) + + ... + 2. Let proto be ? GetPrototypeFromConstructor(constructor, intrinsicDefaultProto). + 3. Return ObjectCreate(proto, internalSlotsList). + + GetPrototypeFromConstructor ( constructor, intrinsicDefaultProto ) + + 3. Let proto be ? Get(constructor, 'prototype'). + 4. If Type(proto) is not Object, then + a. Let realm be ? GetFunctionRealm(constructor). + b. Set proto to realm's intrinsic object named intrinsicDefaultProto. + 5. Return proto. +features: [AsyncContext, cross-realm, Reflect, Symbol] +---*/ + +var other = $262.createRealm().global; +var newTarget = new other.Function(); +var asyncSnapshot; + +newTarget.prototype = undefined; +asyncSnapshot = Reflect.construct(AsyncContext.Snapshot, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncSnapshot), other.AsyncContext.Snapshot.prototype, 'newTarget.prototype is undefined'); + +newTarget.prototype = null; +asyncSnapshot = Reflect.construct(AsyncContext.Snapshot, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncSnapshot), other.AsyncContext.Snapshot.prototype, 'newTarget.prototype is null'); + +newTarget.prototype = true; +asyncSnapshot = Reflect.construct(AsyncContext.Snapshot, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncSnapshot), other.AsyncContext.Snapshot.prototype, 'newTarget.prototype is a Boolean'); + +newTarget.prototype = ''; +asyncSnapshot = Reflect.construct(AsyncContext.Snapshot, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncSnapshot), other.AsyncContext.Snapshot.prototype, 'newTarget.prototype is a String'); + +newTarget.prototype = Symbol(); +asyncSnapshot = Reflect.construct(AsyncContext.Snapshot, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncSnapshot), other.AsyncContext.Snapshot.prototype, 'newTarget.prototype is a Symbol'); + +newTarget.prototype = 1; +asyncSnapshot = Reflect.construct(AsyncContext.Snapshot, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncSnapshot), other.AsyncContext.Snapshot.prototype, 'newTarget.prototype is a Number'); + diff --git a/test/built-ins/AsyncContext/Snapshot/proto.js b/test/built-ins/AsyncContext/Snapshot/proto.js new file mode 100644 index 00000000000..c9166cafbf1 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/proto.js @@ -0,0 +1,18 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-properties-of-the-asynccontext-snapshot-constructor +description: > + The prototype of AsyncContext.Snapshot is %Function.prototype% +info: | + The value of the [[Prototype]] internal slot of the AsyncContext.Snapshot + constructor is the intrinsic object %Function.prototype%. +features: [AsyncContext] +---*/ + +assert.sameValue( + Object.getPrototypeOf(AsyncContext.Snapshot), + Function.prototype, + 'Object.getPrototypeOf(AsyncContext.Snapshot) returns the value of `Function.prototype`' +); diff --git a/test/built-ins/AsyncContext/Snapshot/prototype-from-newtarget-abrupt.js b/test/built-ins/AsyncContext/Snapshot/prototype-from-newtarget-abrupt.js new file mode 100644 index 00000000000..32a6adcf8b2 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype-from-newtarget-abrupt.js @@ -0,0 +1,41 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot +description: > + Return abrupt from getting the NewTarget prototype +info: | + AsyncContext.Snapshot ( ) + + ... + 3. Let asyncSnapshot be ? OrdinaryCreateFromConstructor(NewTarget, "%AsyncContext.Snapshot.prototype%", « [[AsyncSnapshotMapping]] »). + ... + 5. Return asyncSnapshot. + + OrdinaryCreateFromConstructor ( constructor, intrinsicDefaultProto [ , internalSlotsList ] ) + + ... + 2. Let proto be ? GetPrototypeFromConstructor(constructor, intrinsicDefaultProto). + 3. Return ObjectCreate(proto, internalSlotsList). + + GetPrototypeFromConstructor ( constructor, intrinsicDefaultProto ) + + 3. Let proto be ? Get(constructor, 'prototype'). +features: [AsyncContext, Reflect.construct] +---*/ + +var calls = 0; +var newTarget = function() {}.bind(null); +Object.defineProperty(newTarget, 'prototype', { + get: function() { + calls += 1; + throw new Test262Error(); + } +}); + +assert.throws(Test262Error, function() { + Reflect.construct(AsyncContext.Snapshot, [], newTarget); +}); + +assert.sameValue(calls, 1); diff --git a/test/built-ins/AsyncContext/Snapshot/prototype-from-newtarget-custom.js b/test/built-ins/AsyncContext/Snapshot/prototype-from-newtarget-custom.js new file mode 100644 index 00000000000..7d92966267f --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype-from-newtarget-custom.js @@ -0,0 +1,44 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot +description: > + The [[Prototype]] internal slot is computed from NewTarget. +info: | + AsyncContext.Snapshot ( ) + + ... + 3. Let asyncSnapshot be ? OrdinaryCreateFromConstructor(NewTarget, "%AsyncContext.Snapshot.prototype%", « [[AsyncSnapshotMapping]] »). + ... + 5. Return asyncSnapshot. + + OrdinaryCreateFromConstructor ( constructor, intrinsicDefaultProto [ , internalSlotsList ] ) + + ... + 2. Let proto be ? GetPrototypeFromConstructor(constructor, intrinsicDefaultProto). + 3. Return ObjectCreate(proto, internalSlotsList). + + GetPrototypeFromConstructor ( constructor, intrinsicDefaultProto ) + + 3. Let proto be ? Get(constructor, 'prototype'). + 4. If Type(proto) is not Object, then + a. Let realm be ? GetFunctionRealm(constructor). + b. Set proto to realm's intrinsic object named intrinsicDefaultProto. + 5. Return proto. +features: [AsyncContext, Reflect.construct] +---*/ + +var asyncSnapshot; + +asyncSnapshot = Reflect.construct(AsyncContext.Snapshot, [], Object); +assert.sameValue(Object.getPrototypeOf(asyncSnapshot), Object.prototype, 'NewTarget is built-in Object constructor'); + +var newTarget = function() {}.bind(null); +Object.defineProperty(newTarget, 'prototype', { + get: function() { + return Array.prototype; + } +}); +asyncSnapshot = Reflect.construct(AsyncContext.Snapshot, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncSnapshot), Array.prototype, 'NewTarget is BoundFunction with accessor'); diff --git a/test/built-ins/AsyncContext/Snapshot/prototype-from-newtarget.js b/test/built-ins/AsyncContext/Snapshot/prototype-from-newtarget.js new file mode 100644 index 00000000000..09cfc2d53f5 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype-from-newtarget.js @@ -0,0 +1,33 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot +description: > + The [[Prototype]] internal slot is computed from NewTarget. +info: | + AsyncContext.Snapshot ( ) + + ... + 3. Let asyncSnapshot be ? OrdinaryCreateFromConstructor(NewTarget, "%AsyncContext.Snapshot.prototype%", « [[AsyncSnapshotMapping]] »). + ... + 5. Return asyncSnapshot. + + OrdinaryCreateFromConstructor ( constructor, intrinsicDefaultProto [ , internalSlotsList ] ) + + ... + 2. Let proto be ? GetPrototypeFromConstructor(constructor, intrinsicDefaultProto). + 3. Return ObjectCreate(proto, internalSlotsList). + + GetPrototypeFromConstructor ( constructor, intrinsicDefaultProto ) + + 3. Let proto be ? Get(constructor, 'prototype'). + 4. If Type(proto) is not Object, then + a. Let realm be ? GetFunctionRealm(constructor). + b. Set proto to realm's intrinsic object named intrinsicDefaultProto. + 5. Return proto. +features: [AsyncContext] +---*/ + +var asyncSnapshot = new AsyncContext.Snapshot(); +assert.sameValue(Object.getPrototypeOf(asyncSnapshot), AsyncContext.Snapshot.prototype); diff --git a/test/built-ins/AsyncContext/Snapshot/prototype/Symbol.toStringTag.js b/test/built-ins/AsyncContext/Snapshot/prototype/Symbol.toStringTag.js new file mode 100644 index 00000000000..29e8ac4eaa5 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype/Symbol.toStringTag.js @@ -0,0 +1,22 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.prototype-@@tostringtag +description: > + `Symbol.toStringTag` property descriptor +info: | + The initial value of the @@toStringTag property is the String value + "AsyncContext.Snapshot". + + This property has the attributes { [[Writable]]: false, [[Enumerable]]: + false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [AsyncContext, Symbol.toStringTag] +---*/ + +assert.sameValue(AsyncContext.Snapshot.prototype[Symbol.toStringTag], 'AsyncContext.Snapshot'); + +verifyNotEnumerable(AsyncContext.Snapshot.prototype, Symbol.toStringTag); +verifyNotWritable(AsyncContext.Snapshot.prototype, Symbol.toStringTag); +verifyConfigurable(AsyncContext.Snapshot.prototype, Symbol.toStringTag); diff --git a/test/built-ins/AsyncContext/Snapshot/prototype/constructor.js b/test/built-ins/AsyncContext/Snapshot/prototype/constructor.js new file mode 100644 index 00000000000..776dc493049 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype/constructor.js @@ -0,0 +1,19 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.prototype.constructor +description: AsyncContext.Snapshot.prototype.constructor value and descriptor +info: | + The initial value of AsyncContext.Snapshot.prototype.constructor is + %AsyncContext.Snapshot%. +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +assert.sameValue(AsyncContext.Snapshot.prototype.constructor, AsyncContext.Snapshot); +assert.sameValue((new AsyncContext.Snapshot()).constructor, AsyncContext.Snapshot); + +verifyNotEnumerable(AsyncContext.Snapshot.prototype, 'constructor'); +verifyWritable(AsyncContext.Snapshot.prototype, 'constructor'); +verifyConfigurable(AsyncContext.Snapshot.prototype, 'constructor'); diff --git a/test/built-ins/AsyncContext/Snapshot/prototype/prop-desc.js b/test/built-ins/AsyncContext/Snapshot/prototype/prop-desc.js new file mode 100644 index 00000000000..5f5fbb3cd91 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype/prop-desc.js @@ -0,0 +1,18 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.prototype +description: AsyncContext.Snapshot.prototype property attributes. +info: | + This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, + [[Configurable]]: false }. +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +verifyProperty(AsyncContext.Snapshot, 'prototype', { + writable: false, + enumerable: false, + configurable: false +}); diff --git a/test/built-ins/AsyncContext/Snapshot/prototype/proto.js b/test/built-ins/AsyncContext/Snapshot/prototype/proto.js new file mode 100644 index 00000000000..e24338b0445 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype/proto.js @@ -0,0 +1,15 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-properties-of-the-asynccontext-snapshot-prototype-object +description: | + The prototype of AsyncContext.Snapshot.prototype is Object.prototype. +info: | + The value of the [[Prototype]] internal slot of the AsyncContext.Snapshot + prototype object is the intrinsic object %Object.prototype%. +features: [AsyncContext] +---*/ + +var proto = Object.getPrototypeOf(AsyncContext.Snapshot.prototype); +assert.sameValue(proto, Object.prototype); diff --git a/test/built-ins/AsyncContext/Snapshot/prototype/run/calls-callback-with-args.js b/test/built-ins/AsyncContext/Snapshot/prototype/run/calls-callback-with-args.js new file mode 100644 index 00000000000..ca75675d19e --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype/run/calls-callback-with-args.js @@ -0,0 +1,36 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.prototype.run +description: > + Calls the first parameter as a function, passing any arguments after the + first to it. +info: | + AsyncContext.Snapshot.prototype.run ( func, ...args ) + + 4. Let result be Completion(Call(func, undefined, args)). + ... + 6. Return result. +features: [AsyncContext] +---*/ + +const asyncSnapshot = new AsyncContext.Snapshot(); + +const obj = {}; +const symbol = Symbol(); + +let called = false; + +asyncSnapshot.run(callback, 42, "bar", obj, symbol); + +assert(called, 'The `callback` function was called.'); + +function callback() { + assert.sameValue(arguments.length, 4); + assert.sameValue(arguments[0], 42); + assert.sameValue(arguments[1], "bar"); + assert.sameValue(arguments[2], obj); + assert.sameValue(arguments[3], symbol); + called = true; +} diff --git a/test/built-ins/AsyncContext/Snapshot/prototype/run/calls-callback-with-undefined-this.js b/test/built-ins/AsyncContext/Snapshot/prototype/run/calls-callback-with-undefined-this.js new file mode 100644 index 00000000000..afdbab821fa --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype/run/calls-callback-with-undefined-this.js @@ -0,0 +1,21 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.prototype.run +description: > + Calls the second parameter as a function, with its this value being + undefined. +info: | + AsyncContext.Snapshot.prototype.run ( func, ...args ) + + 4. Let result be Completion(Call(func, undefined, args)). +features: [AsyncContext] +---*/ + +const asyncSnapshot = new AsyncContext.Snapshot(); + +asyncSnapshot.run(function () { + "use strict"; + assert.sameValue(this, undefined); +}); diff --git a/test/built-ins/AsyncContext/Snapshot/prototype/run/context-is-not-object.js b/test/built-ins/AsyncContext/Snapshot/prototype/run/context-is-not-object.js new file mode 100644 index 00000000000..62e2c9cd92e --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype/run/context-is-not-object.js @@ -0,0 +1,44 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.prototype.run +description: > + Throws a TypeError if `this` is not an Object. +info: | + AsyncContext.Snapshot.prototype.run ( func, ...args ) + + 1. Let asyncSnapshot be the this value. + 2. Perform ? RequireInternalSlot(asyncSnapshot, [[AsyncSnapshotMapping]]). + ... + + RequireInternalSlot(O, internalSlot) + + 1. If O is not an Object, throw a TypeError exception. + ... +features: [AsyncContext, Symbol] +---*/ + +assert.throws(TypeError, function () { + AsyncContext.Snapshot.prototype.run.call(1); +}); + +assert.throws(TypeError, function () { + AsyncContext.Snapshot.prototype.run.call(true); +}); + +assert.throws(TypeError, function () { + AsyncContext.Snapshot.prototype.run.call(''); +}); + +assert.throws(TypeError, function () { + AsyncContext.Snapshot.prototype.run.call(null); +}); + +assert.throws(TypeError, function () { + AsyncContext.Snapshot.prototype.run.call(undefined); +}); + +assert.throws(TypeError, function () { + AsyncContext.Snapshot.prototype.run.call(Symbol()); +}); diff --git a/test/built-ins/AsyncContext/Snapshot/prototype/run/context-is-not-snapshot-object.js b/test/built-ins/AsyncContext/Snapshot/prototype/run/context-is-not-snapshot-object.js new file mode 100644 index 00000000000..17268753fd4 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype/run/context-is-not-snapshot-object.js @@ -0,0 +1,30 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.prototype.run +description: > + Throws a TypeError if `this` does not have an [[AsyncSnapshotMapping]] + internal slot. +info: | + AsyncContext.Snapshot.prototype.run ( func, ...args ) + + 1. Let asyncContext be the this value. + 2. Perform ? RequireInternalSlot(asyncSnapshot, [[AsyncSnapshotMapping]]). + ... + + RequireInternalSlot(O, internalSlot) + + ... + 2. If O does not have an internalSlot internal slot, throw a TypeError exception. + ... +features: [AsyncContext] +---*/ + +assert.throws(TypeError, function () { + AsyncContext.Snapshot.prototype.run.call({}); +}); + +assert.throws(TypeError, function () { + AsyncContext.Snapshot.prototype.run.call([]); +}); diff --git a/test/built-ins/AsyncContext/Snapshot/prototype/run/context-is-variable-object-throws.js b/test/built-ins/AsyncContext/Snapshot/prototype/run/context-is-variable-object-throws.js new file mode 100644 index 00000000000..f7a6ebb7255 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype/run/context-is-variable-object-throws.js @@ -0,0 +1,25 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.prototype.run +description: > + Throws a TypeError if `this` is an AsyncContext.Variable object. +info: | + AsyncContext.Snapshot.prototype.run ( func, ...args ) + + 1. Let asyncSnapshot be the this value. + 2. Perform ? RequireInternalSlot(asyncSnapshot, [[AsyncSnapshotMapping]]). + ... + + RequireInternalSlot(O, internalSlot) + + ... + 2. If O does not have an internalSlot internal slot, throw a TypeError exception. + ... +features: [AsyncContext] +---*/ + +assert.throws(TypeError, function () { + AsyncContext.Snapshot.prototype.run.call(new AsyncContext.Variable()); +}); diff --git a/test/built-ins/AsyncContext/Snapshot/prototype/run/length.js b/test/built-ins/AsyncContext/Snapshot/prototype/run/length.js new file mode 100644 index 00000000000..a4ebd58f050 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype/run/length.js @@ -0,0 +1,24 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.prototype.run +description: > + AsyncContext.Snapshot.prototype.run.length value and descriptor. +info: | + AsyncContext.Snapshot.prototype.run ( func, ...args ) + + 17 ECMAScript Standard Built-in Objects + +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +assert.sameValue( + AsyncContext.Snapshot.prototype.run.length, 1, + 'The value of `AsyncContext.Snapshot.prototype.run.length` is `1`' +); + +verifyNotEnumerable(AsyncContext.Snapshot.prototype.run, 'length'); +verifyNotWritable(AsyncContext.Snapshot.prototype.run, 'length'); +verifyConfigurable(AsyncContext.Snapshot.prototype.run, 'length'); diff --git a/test/built-ins/AsyncContext/Snapshot/prototype/run/name.js b/test/built-ins/AsyncContext/Snapshot/prototype/run/name.js new file mode 100644 index 00000000000..3471d2b851d --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype/run/name.js @@ -0,0 +1,24 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.prototype.run +description: > + AsyncContext.Snapshot.prototype.run.name value and descriptor. +info: | + AsyncContext.Snapshot.prototype.run ( func, ...args ) + + 17 ECMAScript Standard Built-in Objects + +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +assert.sameValue( + AsyncContext.Snapshot.prototype.run.name, 'run', + 'The value of `AsyncContext.Snapshot.prototype.run.name` is `"run"`' +); + +verifyNotEnumerable(AsyncContext.Snapshot.prototype.run, 'name'); +verifyNotWritable(AsyncContext.Snapshot.prototype.run, 'name'); +verifyConfigurable(AsyncContext.Snapshot.prototype.run, 'name'); diff --git a/test/built-ins/AsyncContext/Snapshot/prototype/run/not-a-constructor.js b/test/built-ins/AsyncContext/Snapshot/prototype/run/not-a-constructor.js new file mode 100644 index 00000000000..86fa96b3488 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype/run/not-a-constructor.js @@ -0,0 +1,30 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-ecmascript-standard-built-in-objects +description: > + AsyncContext.Snapshot.prototype.run does not implement [[Construct]], is not + new-able +info: | + ECMAScript Function Objects + + Built-in function objects that are not identified as constructors do not + implement the [[Construct]] internal method unless otherwise specified in + the description of a particular function. + + sec-evaluatenew + + ... + 7. If IsConstructor(constructor) is false, throw a TypeError exception. + ... +includes: [isConstructor.js] +features: [Reflect.construct, AsyncContext, arrow-function] +---*/ + +assert.sameValue(isConstructor(AsyncContext.Snapshot.prototype.run), false, 'isConstructor(AsyncContext.Snapshot.prototype.run) must return false'); + +assert.throws(TypeError, () => { + let s = new AsyncContext.Snapshot(); new s.run(() => { }); +}, '`let s = new AsyncContext.Snapshot(); new s.run(() => {});` throws TypeError'); + diff --git a/test/built-ins/AsyncContext/Snapshot/prototype/run/restores-previous-context-snapshot-after-callback-throw.js b/test/built-ins/AsyncContext/Snapshot/prototype/run/restores-previous-context-snapshot-after-callback-throw.js new file mode 100644 index 00000000000..97b99ab9ef0 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype/run/restores-previous-context-snapshot-after-callback-throw.js @@ -0,0 +1,50 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.prototype.run +description: > + Switches back into the context snapshot active at the time of calling `run` + after calling the callback, even if it throws an exception. +info: | + AsyncContext.Snapshot.prototype.run ( func, ...args ) + + 3. Let previousContextMapping be AsyncContextSwap(asyncSnapshot.[[AsyncSnapshotMapping]]). + 4. Let result be Completion(Call(func, undefined, args)). + 5. AsyncContextSwap(previousContextMapping). + 6. Return result. + + AsyncContextSwap ( snapshotMapping ) + + 1. Let agentRecord be the surrounding agent's Agent Record. + 2. Let asyncContextMapping be agentRecord.[[AsyncContextMapping]]. + 3. Set agentRecord.[[AsyncContextMapping]] to snapshotMapping. + 4. Return asyncContextMapping. +features: [AsyncContext] +---*/ + +function CustomError() { } + +const asyncVar = new AsyncContext.Variable(); + +const snapshot = asyncVar.run(42, () => new AsyncContext.Snapshot()); + +asyncVar.run("foo", () => { + + try { + snapshot.run(() => { + throw new CustomError(); + }) + } catch (e) { + if (!(e instanceof CustomError)) { + throw e; + } + } + + assert.sameValue( + asyncVar.get(), + "foo", + 'The value of `asyncVar.get()` should be `"foo"`' + ); + +}); diff --git a/test/built-ins/AsyncContext/Snapshot/prototype/run/restores-previous-context-snapshot-after-callback.js b/test/built-ins/AsyncContext/Snapshot/prototype/run/restores-previous-context-snapshot-after-callback.js new file mode 100644 index 00000000000..fe0060e9030 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype/run/restores-previous-context-snapshot-after-callback.js @@ -0,0 +1,59 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.prototype.run +description: > + Switches back into the context snapshot active at the time of calling `run` + after calling the callback. +info: | + AsyncContext.Snapshot.prototype.run ( func, ...args ) + + 3. Let previousContextMapping be AsyncContextSwap(asyncSnapshot.[[AsyncSnapshotMapping]]). + 4. Let result be Completion(Call(func, undefined, args)). + 5. AsyncContextSwap(previousContextMapping). + 6. Return result. + + AsyncContextSwap ( snapshotMapping ) + + 1. Let agentRecord be the surrounding agent's Agent Record. + 2. Let asyncContextMapping be agentRecord.[[AsyncContextMapping]]. + 3. Set agentRecord.[[AsyncContextMapping]] to snapshotMapping. + 4. Return asyncContextMapping. +features: [AsyncContext] +---*/ + +const asyncVar1 = new AsyncContext.Variable(); +const asyncVar2 = new AsyncContext.Variable(); + +const snapshot = asyncVar1.run(42, () => new AsyncContext.Snapshot()); + +const asyncVar3 = new AsyncContext.Variable(); + +asyncVar1.run("foo", () => { + asyncVar2.run("bar", () => { + asyncVar3.run("baz", () => { + + snapshot.run(() => { }); + + assert.sameValue( + asyncVar1.get(), + "foo", + 'The value of `asyncVar1.get()` is `"foo"`' + ); + + assert.sameValue( + asyncVar2.get(), + "bar", + 'The value of `asyncVar2.get()` is `"bar"`' + ); + + assert.sameValue( + asyncVar3.get(), + "baz", + 'The value of `asyncVar3.get()` is `"baz"`' + ); + + }); + }); +}); diff --git a/test/built-ins/AsyncContext/Snapshot/prototype/run/restores-snapshot.js b/test/built-ins/AsyncContext/Snapshot/prototype/run/restores-snapshot.js new file mode 100644 index 00000000000..991786a392f --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype/run/restores-snapshot.js @@ -0,0 +1,60 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.prototype.run +description: > + Restores the snapshot when calling the callback. +info: | + AsyncContext.Snapshot.prototype.run ( func, ...args ) + + 3. Let previousContextMapping be + AsyncContextSwap(asyncSnapshopt.[[AsyncSnapshotMapping]]). + 4. Let result be Completion(Call(func, undefined, args)). + + AsyncContextSwap ( snapshotMapping ) + + 1. Let agentRecord be the surrounding agent's Agent Record. + 2. Let asyncContextMapping be agentRecord.[[AsyncContextMapping]]. + 3. Set agentRecord.[[AsyncContextMapping]] to snapshotMapping. + 4. Return asyncContextMapping. +features: [AsyncContext] +---*/ + +const asyncVar1 = new AsyncContext.Variable(); +const asyncVar2 = new AsyncContext.Variable(); +const asyncVar3 = new AsyncContext.Variable(); + +let asyncSnapshot; + +asyncVar1.run("foo", () => { + asyncVar2.run("bar", () => { + asyncVar3.run("baz", () => { + + asyncSnapshot = new AsyncContext.Snapshot(); + + }); + }); +}); + +asyncSnapshot.run(() => { + + assert.sameValue( + asyncVar1.get(), + "foo", + 'The value of `asyncVar1.get()` is `"foo"`' + ); + + assert.sameValue( + asyncVar2.get(), + "bar", + 'The value of `asyncVar2.get()` is `"bar"`' + ); + + assert.sameValue( + asyncVar3.get(), + "baz", + 'The value of `asyncVar3.get()` is `"baz"`' + ); + +}); diff --git a/test/built-ins/AsyncContext/Snapshot/prototype/run/restores-unset-variables.js b/test/built-ins/AsyncContext/Snapshot/prototype/run/restores-unset-variables.js new file mode 100644 index 00000000000..180d5307247 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype/run/restores-unset-variables.js @@ -0,0 +1,87 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.prototype.run +description: > + For any AsyncContext.Variable objects that were unset in the snapshot, and + that were set in the current snapshot at the time of calling `run`, they are + unset in the context in which the callback is called. + + This happens even for AsyncContext.Variable instances that were did not yet + exist when the AsyncContext.Snapshot was created. +info: | + AsyncContext.Snapshot.prototype.run ( func, ...args ) + + 3. Let previousContextMapping be + AsyncContextSwap(asyncSnapshopt.[[AsyncSnapshotMapping]]). + 4. Let result be Completion(Call(func, undefined, args)). + 5. AsyncContextSwap(previousContextMapping). + + AsyncContextSwap ( snapshotMapping ) + + 1. Let agentRecord be the surrounding agent's Agent Record. + 2. Let asyncContextMapping be agentRecord.[[AsyncContextMapping]]. + 3. Set agentRecord.[[AsyncContextMapping]] to snapshotMapping. + 4. Return asyncContextMapping. +features: [AsyncContext] +---*/ + +const symbol = Symbol(); + +const asyncVar1 = new AsyncContext.Variable(); +const asyncVar2 = new AsyncContext.Variable({ defaultValue: symbol }); +const asyncVar3 = new AsyncContext.Variable(); + +let asyncSnapshot; + +asyncVar1.run("foo", () => { + asyncSnapshot = new AsyncContext.Snapshot(); +}); + +const newAsyncVar1 = new AsyncContext.Variable({ defaultValue: symbol }); +const newAsyncVar2 = new AsyncContext.Variable(); + +asyncVar2.run("bar", () => { + asyncVar3.run("baz", () => { + newAsyncVar1.run("fizz", () => { + newAsyncVar2.run("buzz", () => { + + asyncSnapshot.run(() => { + + assert.sameValue( + asyncVar1.get(), + "foo", + 'The value of `asyncVar1.get()` is `"foo"`' + ); + + assert.sameValue( + asyncVar2.get(), + symbol, + 'The value of `asyncVar2.get()` is `symbol`' + ); + + assert.sameValue( + asyncVar3.get(), + undefined, + 'The value of `asyncVar3.get()` is `"undefined`' + ); + + assert.sameValue( + newAsyncVar1.get(), + symbol, + 'The value of `newAsyncVar1.get()` is `symbol`' + ); + + assert.sameValue( + newAsyncVar2.get(), + undefined, + 'The value of `newAsyncVar2.get()` is `"undefined`' + ); + + }); + + }); + }); + }); +}); diff --git a/test/built-ins/AsyncContext/Snapshot/prototype/run/returned-promise-does-not-resolve-in-snapshot.js b/test/built-ins/AsyncContext/Snapshot/prototype/run/returned-promise-does-not-resolve-in-snapshot.js new file mode 100644 index 00000000000..c5c31d015e0 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype/run/returned-promise-does-not-resolve-in-snapshot.js @@ -0,0 +1,37 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.prototype.run +description: > + When it returns a promise, the async context snapshot when the promise + resolves will not be the one inside of the callback. +info: | + TODO +flags: [async] +features: [AsyncContext] +---*/ + +const asyncVar = new AsyncContext.Variable(); + +const asyncSnapshot = asyncVar.run("bar", () => new AsyncContext.Snapshot()); + +let resolve; + +asyncVar.run("foo", () => { + + asyncSnapshot.run(async () => { + assert.sameValue(asyncVar.get(), "bar"); + + await new Promise(resolveFn => { + resolve = resolveFn; + }); + + assert.sameValue(asyncVar.get(), "bar"); + }).then(() => { + assert.sameValue(asyncVar.get(), "foo"); + }).then($DONE, $DONE); + +}); + +resolve(); diff --git a/test/built-ins/AsyncContext/Snapshot/prototype/run/returns-async-callback-return-value.js b/test/built-ins/AsyncContext/Snapshot/prototype/run/returns-async-callback-return-value.js new file mode 100644 index 00000000000..2e744be8ffa --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype/run/returns-async-callback-return-value.js @@ -0,0 +1,32 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.prototype.run +description: > + If the first parameter is an async function, it calls it and returns its + result value, which is a promise. +info: | + AsyncContext.Snapshot.prototype.run ( func, ...args ) + + 4 Let result be Completion(Call(func, undefined, args)). + ... + 6. Return result. +flags: [async] +includes: [asyncHelpers.js] +features: [AsyncContext] +---*/ + +const asyncSnapshot = new AsyncContext.Snapshot(); + +const obj = {}; + +asyncTest(async function () { + const ret = asyncSnapshot.run(async () => obj); + assert( + ret instanceof Promise, + 'The return value of `asyncSnapshot.run(async () => obj) is a promise' + ); + + assert.sameValue(await ret, obj, '`ret` resolves to `obj`'); +}); diff --git a/test/built-ins/AsyncContext/Snapshot/prototype/run/returns-callback-return-value.js b/test/built-ins/AsyncContext/Snapshot/prototype/run/returns-callback-return-value.js new file mode 100644 index 00000000000..fc95863ab2a --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype/run/returns-callback-return-value.js @@ -0,0 +1,31 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.prototype.run +description: > + Calls the first parameter as a function, and returns its return value. +info: | + AsyncContext.Snapshot.prototype.run ( func, ...args ) + + 4. Let result be Completion(Call(func, undefined, args)). + ... + 6. Return result. +features: [AsyncContext] +---*/ + +const asyncSnapshot = new AsyncContext.Snapshot(); + +const obj = {}; + +assert.sameValue( + asyncSnapshot.run(() => obj), + obj, + 'The return value of `asyncSnapshot.run(() => obj)` is `obj`' +); + +const returnedPromise = asyncSnapshot.run(async () => obj); +assert( + returnedPromise instanceof Promise, + 'The return value of `asyncSnapshot.run(async () => obj) is a promise' +); diff --git a/test/built-ins/AsyncContext/Snapshot/prototype/run/run.js b/test/built-ins/AsyncContext/Snapshot/prototype/run/run.js new file mode 100644 index 00000000000..bfa6b1176ea --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype/run/run.js @@ -0,0 +1,23 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.prototype.run +description: > + AsyncContext.Snapshot.prototype.run ( func, ...args ) + + 17 ECMAScript Standard Built-in Objects + +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +assert.sameValue( + typeof AsyncContext.Snapshot.prototype.run, + 'function', + 'typeof AsyncContext.Snapshot.prototype.run is "function"' +); + +verifyNotEnumerable(AsyncContext.Snapshot.prototype, 'run'); +verifyWritable(AsyncContext.Snapshot.prototype, 'run'); +verifyConfigurable(AsyncContext.Snapshot.prototype, 'run'); diff --git a/test/built-ins/AsyncContext/Snapshot/prototype/run/throws-if-callback-is-not-callable.js b/test/built-ins/AsyncContext/Snapshot/prototype/run/throws-if-callback-is-not-callable.js new file mode 100644 index 00000000000..79aff0bf2f4 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype/run/throws-if-callback-is-not-callable.js @@ -0,0 +1,53 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.prototype.run +description: > + Throws if the first parameter is not callable. +info: | + AsyncContext.Snapshot.prototype.run ( func, ...args ) + + 4. Let result be Completion(Call(func, undefined, args)). + ... + 6. Return result. + + Call ( F, V [ , argumentsList ] ) + + 2. If IsCallable(F) is false, throw a TypeError exception. +features: [AsyncContext] +---*/ + +const asyncSnapshot = new AsyncContext.Snapshot(); + +assert.throws(TypeError, () => { + asyncSnapshot.run("foo"); +}); + +assert.throws(TypeError, () => { + asyncSnapshot.run("foo", 1); +}); + +assert.throws(TypeError, () => { + asyncSnapshot.run("foo", true); +}); + +assert.throws(TypeError, () => { + asyncSnapshot.run("foo", ''); +}); + +assert.throws(TypeError, () => { + asyncSnapshot.run("foo", null); +}); + +assert.throws(TypeError, () => { + asyncSnapshot.run("foo", undefined); +}); + +assert.throws(TypeError, () => { + asyncSnapshot.run("foo", Symbol()); +}); + +assert.throws(TypeError, () => { + asyncSnapshot.run("foo", {}); +}); diff --git a/test/built-ins/AsyncContext/Snapshot/prototype/run/throws-if-callback-throws.js b/test/built-ins/AsyncContext/Snapshot/prototype/run/throws-if-callback-throws.js new file mode 100644 index 00000000000..5d864310a8f --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/prototype/run/throws-if-callback-throws.js @@ -0,0 +1,25 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.prototype.run +description: > + Throws if calling the first parameter throws. +info: | + AsyncContext.Snapshot.prototype.run ( func, ...args ) + + 4. Let result be Completion(Call(func, undefined, args)). + ... + 6. Return result. +features: [AsyncContext] +---*/ + +function CustomError() { } + +const asyncSnapshot = new AsyncContext.Snapshot(); + +assert.throws(CustomError, () => { + asyncSnapshot.run(() => { + throw new CustomError(); + }); +}); diff --git a/test/built-ins/AsyncContext/Snapshot/returns-new-object-from-constructor.js b/test/built-ins/AsyncContext/Snapshot/returns-new-object-from-constructor.js new file mode 100644 index 00000000000..cbb54496d17 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/returns-new-object-from-constructor.js @@ -0,0 +1,37 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot +description: > + Returns a new ordinary object from the FinalizationRegistry constructor +info: | + AsyncContext.Snapshot ( ) + + ... + 3. Let asyncSnapshot be ? OrdinaryCreateFromConstructor(NewTarget, "%AsyncContext.Snapshot.prototype%", « [[AsyncSnapshotMapping]] »). + ... + 5. Return asyncSnapshot. + + OrdinaryCreateFromConstructor ( constructor, intrinsicDefaultProto [ , internalSlotsList ] ) + + ... + 2. Let proto be ? GetPrototypeFromConstructor(constructor, intrinsicDefaultProto). + 3. Return ObjectCreate(proto, internalSlotsList). +features: [AsyncContext, for-of] +---*/ + +var asyncSnapshot = new AsyncContext.Snapshot(); + +assert.sameValue(Object.getPrototypeOf(asyncSnapshot), AsyncContext.Snapshot.prototype); +assert.sameValue(asyncSnapshot instanceof AsyncContext.Snapshot, true, 'instanceof'); + +for (let key of Object.getOwnPropertyNames(asyncSnapshot)) { + assert(false, `should not set any own named properties: ${key}`); +} + +for (let key of Object.getOwnPropertySymbols(asyncSnapshot)) { + assert(false, `should not set any own symbol properties: ${String(key)}`); +} + +assert.sameValue(Object.getPrototypeOf(asyncSnapshot), AsyncContext.Snapshot.prototype); diff --git a/test/built-ins/AsyncContext/Snapshot/undefined-newtarget-throws.js b/test/built-ins/AsyncContext/Snapshot/undefined-newtarget-throws.js new file mode 100644 index 00000000000..8c9ff6c50c9 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/undefined-newtarget-throws.js @@ -0,0 +1,17 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot +description: Throws a TypeError if NewTarget is undefined. +features: [AsyncContext] +---*/ + +assert.sameValue( + typeof AsyncContext.Snapshot, 'function', + 'typeof AsyncContext.Snapshot is function' +); + +assert.throws(TypeError, function() { + AsyncContext.Snapshot(); +}); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/does-not-throw-if-callback-name-getownproperty-throws.js b/test/built-ins/AsyncContext/Snapshot/wrap/does-not-throw-if-callback-name-getownproperty-throws.js new file mode 100644 index 00000000000..8564641c964 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/does-not-throw-if-callback-name-getownproperty-throws.js @@ -0,0 +1,35 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.wrap +description: > + Does not throw if calling HasOwnProperty on the argument's `name` + property throws. +info: | + AsyncContext.Snapshot.wrap ( fn ) + + 5. Perform ? CopyNameAndLength(wrapped, fn, "wrapped"). + + CopyNameAndLength ( F, Target[, prefix[, argCount ] ] ) + + 6. Let targetName be ? Get(Target, "name"). + +features: [AsyncContext] +---*/ + +function CustomError() { } + +function originalCallback() { } + +const proxyCallback = new Proxy(originalCallback, { + getOwnPropertyDescriptor(target, property) { + assert.sameValue(target, originalCallback); + if (property === 'length') { + return Object.getOwnPropertyDescriptor(originalCallback, 'length'); + } + throw new CustomError(); + } +}); + +AsyncContext.Snapshot.wrap(proxyCallback); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/does-not-throw-if-inherited-callback-length-getter-throws.js b/test/built-ins/AsyncContext/Snapshot/wrap/does-not-throw-if-inherited-callback-length-getter-throws.js new file mode 100644 index 00000000000..5c2a5ea8162 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/does-not-throw-if-inherited-callback-length-getter-throws.js @@ -0,0 +1,34 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.wrap +description: > + Does not throw if getting the argument's `length` property throws, + if the property is inherited. +info: | + AsyncContext.Snapshot.wrap ( fn ) + + 5. Perform ? CopyNameAndLength(wrapped, fn, "wrapped"). + + CopyNameAndLength ( F, Target[, prefix[, argCount ] ] ) + + 3. Let targetHasLength be ? HasOwnProperty(Target, "length"). + 4. If targetHasLength is true, then + a. Let targetLen be ? Get(Target, "length"). + +features: [AsyncContext] +---*/ + +function CustomError() { } + +function callback() { } +delete callback.name; + +Object.setPrototypeOf(callback, { + get length() { + throw new CustomError(); + } +}); + +AsyncContext.Snapshot.wrap(callback); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/length.js b/test/built-ins/AsyncContext/Snapshot/wrap/length.js new file mode 100644 index 00000000000..90b23296548 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/length.js @@ -0,0 +1,24 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot-wrap +description: > + AsyncContext.Snapshot.wrap.length value and descriptor. +info: | + AsyncContext.Snapshot.wrap ( fn ) + + 17 ECMAScript Standard Built-in Objects + +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +assert.sameValue( + AsyncContext.Snapshot.wrap.length, 1, + 'The value of `AsyncContext.Snapshot.wrap.length` is `1`' +); + +verifyNotEnumerable(AsyncContext.Snapshot.wrap, 'length'); +verifyNotWritable(AsyncContext.Snapshot.wrap, 'length'); +verifyConfigurable(AsyncContext.Snapshot.wrap, 'length'); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/name.js b/test/built-ins/AsyncContext/Snapshot/wrap/name.js new file mode 100644 index 00000000000..b6a51ee14ae --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/name.js @@ -0,0 +1,24 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot-wrap +description: > + AsyncContext.Snapshot.wrap.name value and descriptor. +info: | + AsyncContext.Snapshot.wrap ( fn ) + + 17 ECMAScript Standard Built-in Objects + +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +assert.sameValue( + AsyncContext.Snapshot.wrap.name, 'wrap', + 'The value of `AsyncContext.Snapshot.wrap.name` is `"wrap"`' +); + +verifyNotEnumerable(AsyncContext.Snapshot.wrap, 'name'); +verifyNotWritable(AsyncContext.Snapshot.wrap, 'name'); +verifyConfigurable(AsyncContext.Snapshot.wrap, 'name'); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/not-a-constructor.js b/test/built-ins/AsyncContext/Snapshot/wrap/not-a-constructor.js new file mode 100644 index 00000000000..aaa4dea0df3 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/not-a-constructor.js @@ -0,0 +1,29 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-ecmascript-standard-built-in-objects +description: > + AsyncContext.Snapshot.wrap does not implement [[Construct]], is not new-able +info: | + ECMAScript Function Objects + + Built-in function objects that are not identified as constructors do not + implement the [[Construct]] internal method unless otherwise specified in + the description of a particular function. + + sec-evaluatenew + + ... + 7. If IsConstructor(constructor) is false, throw a TypeError exception. + ... +includes: [isConstructor.js] +features: [Reflect.construct, AsyncContext, arrow-function] +---*/ + +assert.sameValue(isConstructor(AsyncContext.Snapshot.wrap), false, 'isConstructor(AsyncContext.Snapshot.wrap) must return false'); + +assert.throws(TypeError, () => { + new AsyncContext.Snapshot.wrap(() => { }); +}, '`new AsyncContext.Snapshot.wrap(() => {})` throws TypeError'); + diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/throws-if-callback-is-not-callable.js b/test/built-ins/AsyncContext/Snapshot/wrap/throws-if-callback-is-not-callable.js new file mode 100644 index 00000000000..d0769c3f383 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/throws-if-callback-is-not-callable.js @@ -0,0 +1,45 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.wrap +description: > + Throws if the argument is not callable. +info: | + AsyncContext.Snapshot.wrap ( fn ) + + 1. If IsCallable(fn) is false, throw a TypeError exception. +features: [AsyncContext] +---*/ + +assert.throws(TypeError, () => { + AsyncContext.Snapshot.wrap(); +}); + +assert.throws(TypeError, () => { + AsyncContext.Snapshot.wrap(1); +}); + +assert.throws(TypeError, () => { + AsyncContext.Snapshot.wrap(true); +}); + +assert.throws(TypeError, () => { + AsyncContext.Snapshot.wrap(''); +}); + +assert.throws(TypeError, () => { + AsyncContext.Snapshot.wrap(null); +}); + +assert.throws(TypeError, () => { + AsyncContext.Snapshot.wrap(undefined); +}); + +assert.throws(TypeError, () => { + AsyncContext.Snapshot.wrap(Symbol()); +}); + +assert.throws(TypeError, () => { + AsyncContext.Snapshot.wrap({}); +}); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/throws-if-callback-length-getownproperty-throws.js b/test/built-ins/AsyncContext/Snapshot/wrap/throws-if-callback-length-getownproperty-throws.js new file mode 100644 index 00000000000..56e160fdec1 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/throws-if-callback-length-getownproperty-throws.js @@ -0,0 +1,34 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.wrap +description: > + Throws if calling HasOwnProperty on the argument's `length` property throws. +info: | + AsyncContext.Snapshot.wrap ( fn ) + + 5. Perform ? CopyNameAndLength(wrapped, fn, "wrapped"). + + CopyNameAndLength ( F, Target[, prefix[, argCount ] ] ) + + 3. Let targetHasLength be ? HasOwnProperty(Target, "length"). + +features: [AsyncContext] +---*/ + +function CustomError() { } + +function originalCallback() { } + +const proxyCallback = new Proxy(originalCallback, { + getOwnPropertyDescriptor(target, property) { + assert.sameValue(target, originalCallback); + assert.sameValue(property, 'length'); + throw new CustomError(); + } +}); + +assert.throws(CustomError, () => { + AsyncContext.Snapshot.wrap(proxyCallback); +}); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/throws-if-inherited-callback-name-getter-throws.js b/test/built-ins/AsyncContext/Snapshot/wrap/throws-if-inherited-callback-name-getter-throws.js new file mode 100644 index 00000000000..968b9fcbe64 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/throws-if-inherited-callback-name-getter-throws.js @@ -0,0 +1,34 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.wrap +description: > + Throws if getting the argument's `length` property throws, if the + property is inherited. +info: | + AsyncContext.Snapshot.wrap ( fn ) + + 5. Perform ? CopyNameAndLength(wrapped, fn, "wrapped"). + + CopyNameAndLength ( F, Target[, prefix[, argCount ] ] ) + + 6. Let targetName be ? Get(Target, "name"). + +features: [AsyncContext] +---*/ + +function CustomError() { } + +function callback() { } +delete callback.name; + +Object.setPrototypeOf(callback, { + get name() { + throw new CustomError(); + } +}); + +assert.throws(CustomError, () => { + AsyncContext.Snapshot.wrap(callback); +}); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/throws-if-own-callback-length-getter-throws.js b/test/built-ins/AsyncContext/Snapshot/wrap/throws-if-own-callback-length-getter-throws.js new file mode 100644 index 00000000000..e97f4d378ea --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/throws-if-own-callback-length-getter-throws.js @@ -0,0 +1,34 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.wrap +description: > + Throws if getting the argument's `length` property throws, if it is + an own property. +info: | + AsyncContext.Snapshot.wrap ( fn ) + + 5. Perform ? CopyNameAndLength(wrapped, fn, "wrapped"). + + CopyNameAndLength ( F, Target[, prefix[, argCount ] ] ) + + 3. Let targetHasLength be ? HasOwnProperty(Target, "length"). + 4. If targetHasLength is true, then + a. Let targetLen be ? Get(Target, "length"). + +features: [AsyncContext] +---*/ + +function CustomError() { } + +function callback() { } +Object.defineProperty(callback, 'length', { + get() { + throw new CustomError(); + } +}); + +assert.throws(CustomError, () => { + AsyncContext.Snapshot.wrap(callback); +}); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/throws-if-own-callback-name-getter-throws.js b/test/built-ins/AsyncContext/Snapshot/wrap/throws-if-own-callback-name-getter-throws.js new file mode 100644 index 00000000000..c5f6c328e3d --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/throws-if-own-callback-name-getter-throws.js @@ -0,0 +1,32 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.wrap +description: > + Throws if getting the argument's `name` property throws, if it is + an own property. +info: | + AsyncContext.Snapshot.wrap ( fn ) + + 5. Perform ? CopyNameAndLength(wrapped, fn, "wrapped"). + + CopyNameAndLength ( F, Target[, prefix[, argCount ] ] ) + + 6. Let targetName be ? Get(Target, "name"). + +features: [AsyncContext] +---*/ + +function CustomError() { } + +function callback() { } +Object.defineProperty(callback, 'name', { + get() { + throw new CustomError(); + } +}); + +assert.throws(CustomError, () => { + AsyncContext.Snapshot.wrap(callback); +}); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/wrap.js b/test/built-ins/AsyncContext/Snapshot/wrap/wrap.js new file mode 100644 index 00000000000..588d4bf667e --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/wrap.js @@ -0,0 +1,23 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot-wrap +description: > + AsyncContext.Snapshot.wrap ( fn ) + + 17 ECMAScript Standard Built-in Objects + +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +assert.sameValue( + typeof AsyncContext.Snapshot.wrap, + 'function', + 'typeof AsyncContext.Snapshot.wrap is "function"' +); + +verifyNotEnumerable(AsyncContext.Snapshot, 'wrap'); +verifyWritable(AsyncContext.Snapshot, 'wrap'); +verifyConfigurable(AsyncContext.Snapshot, 'wrap'); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/calls-callback-in-snapshot.js b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/calls-callback-in-snapshot.js new file mode 100644 index 00000000000..66c79c380d2 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/calls-callback-in-snapshot.js @@ -0,0 +1,69 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.wrap +description: > + Stores the current snapshot and restores it when the returned callback is + called. +info: | + AsyncContext.Snapshot.wrap ( fn ) + + 2. Let snapshot be AsyncContextSnapshot(). + 3. Let closure be a new Abstract Closure with parameters (...args) that captures fn and snapshot and performs the following steps when called: + ... + b. Let previousContextMapping be AsyncContextSwap(snapshot). + c. Let result be Completion(Call(fn, thisArgument, args)). + ... + + AsyncContextSnapshot ( ) + + 1. Let agentRecord be the surrounding agent's Agent Record. + 2. Return agentRecord.[[AsyncContextMapping]]. + + AsyncContextSwap ( snapshotMapping ) + + 1. Let agentRecord be the surrounding agent's Agent Record. + 2. Let asyncContextMapping be agentRecord.[[AsyncContextMapping]]. + 3. Set agentRecord.[[AsyncContextMapping]] to snapshotMapping. + 4. Return asyncContextMapping. +features: [AsyncContext] +---*/ + +const asyncVar1 = new AsyncContext.Variable(); +const asyncVar2 = new AsyncContext.Variable(); +const asyncVar3 = new AsyncContext.Variable(); + +function originalFunction() { + assert.sameValue( + asyncVar1.get(), + "foo", + 'The value of `asyncVar1.get()` is `"foo"`' + ); + + assert.sameValue( + asyncVar2.get(), + "bar", + 'The value of `asyncVar2.get()` is `"bar"`' + ); + + assert.sameValue( + asyncVar3.get(), + "baz", + 'The value of `asyncVar3.get()` is `"baz"`' + ); +} + +let wrappedFunction; + +asyncVar1.run("foo", () => { + asyncVar2.run("bar", () => { + asyncVar3.run("baz", () => { + + wrappedFunction = AsyncContext.Snapshot.wrap(originalFunction); + + }); + }); +}); + +wrappedFunction(); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/calls-callback-with-args.js b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/calls-callback-with-args.js new file mode 100644 index 00000000000..a9f0058b64d --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/calls-callback-with-args.js @@ -0,0 +1,28 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.wrap +description: > + The returned function calls the passed callback with the arguments passed to + the returned function. +info: | + AsyncContext.Snapshot.wrap ( fn ) + + 3. Let closure be a new Abstract Closure with parameters (...args) that captures fn and snapshot and performs the following steps when called: + ... + c. Let result be Completion(Call(fn, thisArgument, args)). + ... + 9. Return CreateBuiltinFunction(closure, length, name, « », realm, prototype, "wrapped"). +features: [AsyncContext] +---*/ + +function callback(a, b, c) { + assert.sameValue(a, 'foo', 'The first argument must be "foo".'); + assert.sameValue(b, 'bar', 'The second argument must be "bar".'); + assert.sameValue(c, 'baz', 'The second argument must be "baz".'); +} + +const wrapped = AsyncContext.Snapshot.wrap(callback); + +wrapped('foo', 'bar', 'baz'); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/calls-callback-with-this.js b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/calls-callback-with-this.js new file mode 100644 index 00000000000..151cee1f4ff --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/calls-callback-with-this.js @@ -0,0 +1,33 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.wrap +description: > + The returned function calls the passed callback with the this value that the + returned function was called with. +info: | + AsyncContext.Snapshot.wrap ( fn ) + + 3. Let closure be a new Abstract Closure with parameters (...args) that captures fn and snapshot and performs the following steps when called: + a. Let thisArgument be the this value. + ... + c. Let result be Completion(Call(fn, thisArgument, args)). + ... + 9. Return CreateBuiltinFunction(closure, length, name, « », realm, prototype, "wrapped"). +features: [AsyncContext] +---*/ + +const thisValue = {}; + +function callback() { + assert.sameValue( + this, + thisValue, + 'Callback is called with `thisValue` as its this value.' + ); +} + +const wrapped = AsyncContext.Snapshot.wrap(callback); + +wrapped.apply(thisValue); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/calls-callback.js b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/calls-callback.js new file mode 100644 index 00000000000..edfaf7010cc --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/calls-callback.js @@ -0,0 +1,29 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.wrap +description: > + The returned function calls the passed callback. +info: | + AsyncContext.Snapshot.wrap ( fn ) + + 3. Let closure be a new Abstract Closure with parameters (...args) that captures fn and snapshot and performs the following steps when called: + ... + c. Let result be Completion(Call(fn, thisArgument, args)). + ... + 9. Return CreateBuiltinFunction(closure, length, name, « », realm, prototype, "wrapped"). +features: [AsyncContext] +---*/ + +let timesCalled = 0; + +function callback() { + timesCalled++; +} + +const wrapped = AsyncContext.Snapshot.wrap(callback); + +wrapped(); + +assert.sameValue(timesCalled, 1, 'The callback must be called once.'); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/has-prototype-from-current-realm.js b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/has-prototype-from-current-realm.js new file mode 100644 index 00000000000..0e64ba88306 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/has-prototype-from-current-realm.js @@ -0,0 +1,37 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.wrap +description: > + The returned function's prototype is the current realm's %Function.prototype% + intrinsic. +info: | + AsyncContext.Snapshot.wrap ( fn ) + + 7. Let realm be the current Realm record. + 8. Let prototype be realm.[[Intrinsics]].[[%Function.prototype%]]. + 9. Return CreateBuiltinFunction(closure, length, name, « », realm, prototype, "wrapped"). + +features: [AsyncContext] +---*/ + +const realm1 = $262.createRealm(); +const realm2 = $262.createRealm(); +const realm3 = $262.createRealm(); + +const callback = realm1.evalScript( + "(function callback() { })" +); + +realm3.global.callback = callback; +realm3.global.wrap = realm2.global.AsyncContext.Snapshot.wrap; +const wrapped = realm3.evalScript( + "globalThis.wrap(globalThis.callback)" +); + +assert.sameValue( + Object.getPrototypeOf(wrapped), + realm2.global.Function.prototype, + 'The prototype of wrapped is realm2\'s %Function.prototype%' +); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/is-function.js b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/is-function.js new file mode 100644 index 00000000000..bf440cdacf8 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/is-function.js @@ -0,0 +1,28 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.wrap +description: > + Returns a function object. +info: | + AsyncContext.Snapshot.wrap ( fn ) + + 7. Let realm be the current Realm record. + 8. Let prototype be realm.[[Intrinsics]].[[%Function.prototype%]]. + 9. Return CreateBuiltinFunction(closure, length, name, « », realm, prototype, "wrapped"). + +features: [AsyncContext] +---*/ + +function callback() { } + +const wrapped = AsyncContext.Snapshot.wrap(callback); + +assert.sameValue(typeof wrapped, 'function', 'typeof wrapped is "function"'); + +assert.sameValue( + Object.getPrototypeOf(wrapped), + Function.prototype, + 'The prototype of wrapped is %Function.prototype%' +); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/length.js b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/length.js new file mode 100644 index 00000000000..4dcd8826fc9 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/length.js @@ -0,0 +1,120 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.wrap +description: > + Returned function's `length` property. +info: | + AsyncContext.Snapshot.wrap ( fn ) + + 5. Perform ? CopyNameAndLength(wrapped, fn, "wrapped"). + + CopyNameAndLength ( F, Target[, prefix[, argCount ] ] ) + + 1. If argCount is not present, set argCount to 0. + 2. Let L be 0. + 3. Let targetHasLength be ? HasOwnProperty(Target, "length"). + 4. If targetHasLength is true, then + a. Let targetLen be ? Get(Target, "length"). + b. If targetLen is a Number, then + i. If targetLen is +∞𝔽, then + 1. Set L to +∞. + ii. Else if targetLen is -∞𝔽, then + 1. Set L to 0. + iii. Else, + 1. Let targetLenAsInt be ! ToIntegerOrInfinity(targetLen). + 2. Assert: targetLenAsInt is finite. + 3. Set L to max(targetLenAsInt - argCount, 0). + 5. Perform SetFunctionLength(F, L). + + ToIntegerOrInfinity ( argument ) + + 1. Let number be ? ToNumber(argument). + 2. If number is one of NaN, +0𝔽, or -0𝔽, return 0. + 3. If number is +∞𝔽, return +∞. + 4. If number is -∞𝔽, return -∞. + 5. Return truncate(ℝ(number)). + + SetFunctionLength ( F, length ) + + 2. Perform ! DefinePropertyOrThrow(F, "length", PropertyDescriptor { [[Value]]: 𝔽(length), [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }). + +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +function callback(_a, _b) { } + +function assertLength(expected) { + const wrapped = AsyncContext.Snapshot.wrap(callback); + verifyProperty(wrapped, 'length', { + value: expected, + writable: false, + enumerable: false, + configurable: true + }); +} + +function setAndAssertLength(lengthToSet, expected) { + Object.defineProperty(callback, 'length', { + value: lengthToSet, + writable: true, + enumerable: true, + configurable: true + }); + assertLength(expected); +} + +assertLength(callback.length); + +setAndAssertLength(0, 0); + +setAndAssertLength(42, 42); + +setAndAssertLength(-42, 0); + +setAndAssertLength(Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY); + +setAndAssertLength(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER); + +setAndAssertLength(Number.MAX_SAFE_INTEGER * 120, Number.MAX_SAFE_INTEGER * 120); + +setAndAssertLength(-0, 0); + +setAndAssertLength(-0.2, 0); + +setAndAssertLength(Number.NEGATIVE_INFINITY, 0); + +setAndAssertLength(Number.NaN, 0); + +setAndAssertLength(7.2, 7); + +setAndAssertLength(7.9, 7); + +setAndAssertLength(undefined, 0); + +setAndAssertLength("42", 0); + +setAndAssertLength(Symbol(), 0); + +setAndAssertLength(42n, 0); + +setAndAssertLength(new Number(42), 0); + +setAndAssertLength({ + valueOf() { + return 42; + } +}, 0); + +delete callback.length; +assertLength(0); + +// Inherited length is ignored +Object.defineProperty( + Object.getPrototypeOf(callback), + "length", + { value: 2 } +); +assertLength(0); \ No newline at end of file diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/name.js b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/name.js new file mode 100644 index 00000000000..a9d0c408305 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/name.js @@ -0,0 +1,85 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.wrap +description: > + Returned function's `name` property. +info: | + AsyncContext.Snapshot.wrap ( fn ) + + 5. Perform ? CopyNameAndLength(wrapped, fn, "wrapped"). + + CopyNameAndLength ( F, Target[, prefix[, argCount ] ] ) + + 6. Let targetName be ? Get(Target, "name"). + 7. If targetName is not a String, set targetName to the empty String. + 8. If prefix is present, then + a. Perform SetFunctionName(F, targetName, prefix). + ... + + SetFunctionName ( F, name [ , prefix ] ) + + 5. If prefix is present, then + a. Set name to the string-concatenation of prefix, the code unit 0x0020 (SPACE), and name. + ... + 6. Perform ! DefinePropertyOrThrow(F, "name", PropertyDescriptor { [[Value]]: name, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }). + +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +function callback() { } + +function assertName(expected) { + const wrapped = AsyncContext.Snapshot.wrap(callback); + verifyProperty(wrapped, 'name', { + value: expected, + writable: false, + enumerable: false, + configurable: true + }); +} + +function setAndAssertName(nameToSet, expected) { + Object.defineProperty(callback, 'name', { + value: nameToSet, + writable: true, + enumerable: true, + configurable: true + }); + assertName(expected); +} + +assertName("wrapped callback"); + +setAndAssertName("foo", "wrapped foo"); + +setAndAssertName(42, "wrapped "); + +setAndAssertName(undefined, "wrapped "); + +delete callback.name; +assertName("wrapped "); + +Object.defineProperty( + Object.getPrototypeOf(callback), + "name", + { value: "inherited" } +); +assertName("wrapped inherited"); + +setAndAssertName(Symbol(), "wrapped "); + +setAndAssertName(new String("foo"), "wrapped "); + +setAndAssertName({}, "wrapped "); + +setAndAssertName( + { + toString() { + assert(false, "The name's toString function must not be called."); + } + }, + "wrapped " +); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/not-a-constructor.js b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/not-a-constructor.js new file mode 100644 index 00000000000..a131bfd970a --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/not-a-constructor.js @@ -0,0 +1,41 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.wrap +description: > + The returned function is not a constructor, even if the original callback is. +info: | + ECMAScript Function Objects + + Built-in function objects that are not identified as constructors do not + implement the [[Construct]] internal method unless otherwise specified in + the description of a particular function. + + sec-evaluatenew + + ... + 7. If IsConstructor(constructor) is false, throw a TypeError exception. + ... +includes: [isConstructor.js] +features: [Reflect.construct, AsyncContext, arrow-function] +---*/ + +function Constructor() { } + +// Constructor is indeed a constructor +new Constructor(); +assert.sameValue(isConstructor(Constructor), true, 'Constructor must be a constructor'); + +// Checking that the function returned by wrap is not one. +const wrapped = AsyncContext.Snapshot.wrap(Constructor); + +assert.sameValue( + isConstructor(AsyncContext.Snapshot.wrap), + false, + 'isConstructor(wrapped) must return false' +); + +assert.throws(TypeError, () => { + new wrapped(); +}, '`new wrapped()` throws TypeError'); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/restores-previous-context-snapshot-after-callback-throw.js b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/restores-previous-context-snapshot-after-callback-throw.js new file mode 100644 index 00000000000..964eac8acf0 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/restores-previous-context-snapshot-after-callback-throw.js @@ -0,0 +1,56 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.wrap +description: > + Switches back into the context snapshot active at the time of calling the + wrapped function, after it calls the original callback, even if it throws an + exception. +info: | + AsyncContext.Snapshot.wrap ( fn ) + + 3. Let closure be a new Abstract Closure with parameters (...args) that captures fn and snapshot and performs the following steps when called: + ... + b. Let previousContextMapping be AsyncContextSwap(snapshot). + c. Let result be Completion(Call(fn, thisArgument, args)). + d. AsyncContextSwap(previousContextMapping). + ... + 9. Return CreateBuiltinFunction(closure, length, name, « », realm, prototype, "wrapped"). + + AsyncContextSwap ( snapshotMapping ) + + 1. Let agentRecord be the surrounding agent's Agent Record. + 2. Let asyncContextMapping be agentRecord.[[AsyncContextMapping]]. + 3. Set agentRecord.[[AsyncContextMapping]] to snapshotMapping. + 4. Return asyncContextMapping. +features: [AsyncContext] +---*/ + +function CustomError() { } + +const asyncVar = new AsyncContext.Variable(); + +const wrapped = asyncVar.run(42, () => { + return AsyncContext.Snapshot.wrap(() => { + throw new CustomError(); + }); +}); + +asyncVar.run("foo", () => { + + try { + wrapped(); + } catch (e) { + if (!(e instanceof CustomError)) { + throw e; + } + } + + assert.sameValue( + asyncVar.get(), + "foo", + 'The value of `asyncVar.get()` should be `"foo"`' + ); + +}); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/restores-previous-context-snapshot-after-callback.js b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/restores-previous-context-snapshot-after-callback.js new file mode 100644 index 00000000000..036cfe97107 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/restores-previous-context-snapshot-after-callback.js @@ -0,0 +1,64 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.wrap +description: > + Switches back into the context snapshot active at the time of calling the + wrapped function, after it calls the original callback. +info: | + AsyncContext.Snapshot.wrap ( fn ) + + 3. Let closure be a new Abstract Closure with parameters (...args) that captures fn and snapshot and performs the following steps when called: + ... + b. Let previousContextMapping be AsyncContextSwap(snapshot). + c. Let result be Completion(Call(fn, thisArgument, args)). + d. AsyncContextSwap(previousContextMapping). + ... + 9. Return CreateBuiltinFunction(closure, length, name, « », realm, prototype, "wrapped"). + + AsyncContextSwap ( snapshotMapping ) + + 1. Let agentRecord be the surrounding agent's Agent Record. + 2. Let asyncContextMapping be agentRecord.[[AsyncContextMapping]]. + 3. Set agentRecord.[[AsyncContextMapping]] to snapshotMapping. + 4. Return asyncContextMapping. +features: [AsyncContext] +---*/ + +const asyncVar1 = new AsyncContext.Variable(); +const asyncVar2 = new AsyncContext.Variable(); + +const wrapped = asyncVar1.run(42, () => { + return AsyncContext.Snapshot.wrap(() => { }); +}); + +const asyncVar3 = new AsyncContext.Variable(); + +asyncVar1.run("foo", () => { + asyncVar2.run("bar", () => { + asyncVar3.run("baz", () => { + + wrapped(); + + assert.sameValue( + asyncVar1.get(), + "foo", + 'The value of `asyncVar1.get()` is `"foo"`' + ); + + assert.sameValue( + asyncVar2.get(), + "bar", + 'The value of `asyncVar2.get()` is `"bar"`' + ); + + assert.sameValue( + asyncVar3.get(), + "baz", + 'The value of `asyncVar3.get()` is `"baz"`' + ); + + }); + }); +}); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/restores-snapshot.js b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/restores-snapshot.js new file mode 100644 index 00000000000..6c1334326d1 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/restores-snapshot.js @@ -0,0 +1,65 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.wrap +description: > + Calling the returned function restores the snapshot active when wrap was + called. +info: | + AsyncContext.Snapshot.wrap ( fn ) + + 2. Let snapshot be AsyncContextSnapshot(). + 3. Let closure be a new Abstract Closure with parameters (...args) that captures fn and snapshot and performs the following steps when called: + ... + b. Let previousContextMapping be AsyncContextSwap(snapshot). + c. Let result be Completion(Call(fn, thisArgument, args)). + ... + 9. Return CreateBuiltinFunction(closure, length, name, « », realm, prototype, "wrapped"). + + AsyncContextSwap ( snapshotMapping ) + + 1. Let agentRecord be the surrounding agent's Agent Record. + 2. Let asyncContextMapping be agentRecord.[[AsyncContextMapping]]. + 3. Set agentRecord.[[AsyncContextMapping]] to snapshotMapping. + 4. Return asyncContextMapping. +features: [AsyncContext] +---*/ + +const asyncVar1 = new AsyncContext.Variable(); +const asyncVar2 = new AsyncContext.Variable(); +const asyncVar3 = new AsyncContext.Variable(); + +function callback() { + assert.sameValue( + asyncVar1.get(), + "foo", + 'The value of `asyncVar1.get()` is `"foo"`' + ); + + assert.sameValue( + asyncVar2.get(), + "bar", + 'The value of `asyncVar2.get()` is `"bar"`' + ); + + assert.sameValue( + asyncVar3.get(), + "baz", + 'The value of `asyncVar3.get()` is `"baz"`' + ); +} + +let wrapped; + +asyncVar1.run("foo", () => { + asyncVar2.run("bar", () => { + asyncVar3.run("baz", () => { + + wrapped = AsyncContext.Snapshot.wrap(callback); + + }); + }); +}); + +wrapped(); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/restores-unset-variables.js b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/restores-unset-variables.js new file mode 100644 index 00000000000..5162e9884f2 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/restores-unset-variables.js @@ -0,0 +1,94 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.wrap +description: > + For any AsyncContext.Variable objects that were unset when wrap was called, + and that were set in the current snapshot at the time of calling the wrapped + function, they are unset in the context inside the callback. + + This happens even for AsyncContext.Variable instances that were did not yet + exist when the AsyncContext.Snapshot was created. +info: | + AsyncContext.Snapshot.wrap ( fn ) + + 2. Let snapshot be AsyncContextSnapshot(). + 3. Let closure be a new Abstract Closure with parameters (...args) that captures fn and snapshot and performs the following steps when called: + ... + b. Let previousContextMapping be AsyncContextSwap(snapshot). + c. Let result be Completion(Call(fn, thisArgument, args)). + ... + 9. Return CreateBuiltinFunction(closure, length, name, « », realm, prototype, "wrapped"). + + AsyncContextSwap ( snapshotMapping ) + + 1. Let agentRecord be the surrounding agent's Agent Record. + 2. Let asyncContextMapping be agentRecord.[[AsyncContextMapping]]. + 3. Set agentRecord.[[AsyncContextMapping]] to snapshotMapping. + 4. Return asyncContextMapping. +features: [AsyncContext] +---*/ + +const symbol = Symbol(); + +const asyncVar1 = new AsyncContext.Variable(); +const asyncVar2 = new AsyncContext.Variable({ defaultValue: symbol }); +const asyncVar3 = new AsyncContext.Variable(); + +// Will only be constructed after calling `wrap`. +let newAsyncVar1; +let newAsyncVar2; + +function callback() { + assert.sameValue( + asyncVar1.get(), + "foo", + 'The value of `asyncVar1.get()` is `"foo"`' + ); + + assert.sameValue( + asyncVar2.get(), + symbol, + 'The value of `asyncVar2.get()` is `symbol`' + ); + + assert.sameValue( + asyncVar3.get(), + undefined, + 'The value of `asyncVar3.get()` is `"undefined`' + ); + + assert.sameValue( + newAsyncVar1.get(), + symbol, + 'The value of `newAsyncVar1.get()` is `symbol`' + ); + + assert.sameValue( + newAsyncVar2.get(), + undefined, + 'The value of `newAsyncVar2.get()` is `"undefined`' + ); +} + +let wrapped; + +asyncVar1.run("foo", () => { + wrapped = AsyncContext.Snapshot.wrap(callback); +}); + +newAsyncVar1 = new AsyncContext.Variable({ defaultValue: symbol }); +newAsyncVar2 = new AsyncContext.Variable(); + +asyncVar2.run("bar", () => { + asyncVar3.run("baz", () => { + newAsyncVar1.run("fizz", () => { + newAsyncVar2.run("buzz", () => { + + wrapped(); + + }); + }); + }); +}); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/returned-promise-does-not-resolve-in-snapshot.js b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/returned-promise-does-not-resolve-in-snapshot.js new file mode 100644 index 00000000000..ac986f1e0cf --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/returned-promise-does-not-resolve-in-snapshot.js @@ -0,0 +1,39 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.wrap +description: > + When the function returns a promise, the async context snapshot when the + promise resolves will not be the wrapped snapshot. +info: | + TODO +flags: [async] +features: [AsyncContext] +---*/ + +const asyncVar = new AsyncContext.Variable(); + +let resolve; + +async function callback() { + assert.sameValue(asyncVar.get(), "bar"); + + await new Promise(resolveFn => { + resolve = resolveFn; + }); + + assert.sameValue(asyncVar.get(), "bar"); +} + +const wrapped = asyncVar.run("bar", () => AsyncContext.Snapshot.wrap(callback)); + +asyncVar.run("foo", () => { + + wrapped().then(() => { + assert.sameValue(asyncVar.get(), "foo"); + }).then($DONE, $DONE); + +}); + +resolve(); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/returns-async-callback-return-value.js b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/returns-async-callback-return-value.js new file mode 100644 index 00000000000..9ae2507a6a8 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/returns-async-callback-return-value.js @@ -0,0 +1,36 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.wrap +description: > + If the passed callback is an async function, the returned function calls it + and returns its return value, which is a promise. +info: | + AsyncContext.Snapshot.wrap ( fn ) + + 3. Let closure be a new Abstract Closure with parameters (...args) that captures fn and snapshot and performs the following steps when called: + ... + c. Let result be Completion(Call(fn, thisArgument, args)). + ... + e. Return result. + ... + 9. Return CreateBuiltinFunction(closure, length, name, « », realm, prototype, "wrapped"). +flags: [async] +includes: [asyncHelpers.js] +features: [AsyncContext] +---*/ + +const obj = {}; + +const wrapped = AsyncContext.Snapshot.wrap(async () => obj); + +asyncTest(async function () { + const ret = wrapped(); + assert( + ret instanceof Promise, + 'The return value of `wrapped()` is a promise' + ); + + assert.sameValue(await ret, obj, '`ret` resolves to `obj`'); +}); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/returns-callback-return-value.js b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/returns-callback-return-value.js new file mode 100644 index 00000000000..b842fc65248 --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/returns-callback-return-value.js @@ -0,0 +1,27 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.wrap +description: > + The returned function returns the value the passed callback returns. +info: | + AsyncContext.Snapshot.wrap ( fn ) + + 3. Let closure be a new Abstract Closure with parameters (...args) that captures fn and snapshot and performs the following steps when called: + ... + c. Let result be Completion(Call(fn, thisArgument, args)). + ... + e. Return result. + ... + 9. Return CreateBuiltinFunction(closure, length, name, « », realm, prototype, "wrapped"). +features: [AsyncContext] +---*/ + +function callback() { + return 42; +} + +const wrapped = AsyncContext.Snapshot.wrap(callback); + +assert.sameValue(wrapped(), 42, 'The callback must return 42.'); diff --git a/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/throws-if-callback-throws.js b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/throws-if-callback-throws.js new file mode 100644 index 00000000000..47ef2484aae --- /dev/null +++ b/test/built-ins/AsyncContext/Snapshot/wrap/wrapped-function/throws-if-callback-throws.js @@ -0,0 +1,29 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-snapshot.wrap +description: > + The returned function throws if the callback throws, with the same thrown + value. +info: | + AsyncContext.Snapshot.wrap ( fn ) + + 3. Let closure be a new Abstract Closure with parameters (...args) that captures fn and snapshot and performs the following steps when called: + c. Let result be Completion(Call(fn, thisArgument, args)). + ... + e. Return result. + ... + 9. Return CreateBuiltinFunction(closure, length, name, « », realm, prototype, "wrapped"). +features: [AsyncContext] +---*/ + +function CustomError() { } + +const wrapped = AsyncContext.Snapshot.wrap(() => { + throw new CustomError(); +}); + +assert.throws(CustomError, () => { + wrapped(); +}); diff --git a/test/built-ins/AsyncContext/Symbol.toStringTag.js b/test/built-ins/AsyncContext/Symbol.toStringTag.js new file mode 100644 index 00000000000..e10643305a5 --- /dev/null +++ b/test/built-ins/AsyncContext/Symbol.toStringTag.js @@ -0,0 +1,22 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-@@tostringtag +description: > + `Symbol.toStringTag` property descriptor +info: | + The initial value of the @@toStringTag property is the String value + "AsyncContext". + + This property has the attributes { [[Writable]]: false, [[Enumerable]]: + false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [AsyncContext, Symbol.toStringTag] +---*/ + +assert.sameValue(AsyncContext[Symbol.toStringTag], 'AsyncContext'); + +verifyNotEnumerable(AsyncContext, Symbol.toStringTag); +verifyNotWritable(AsyncContext, Symbol.toStringTag); +verifyConfigurable(AsyncContext, Symbol.toStringTag); diff --git a/test/built-ins/AsyncContext/Variable/constructor-defaultValue-defaults-to-undefined.js b/test/built-ins/AsyncContext/Variable/constructor-defaultValue-defaults-to-undefined.js new file mode 100644 index 00000000000..9cc6c54b0c7 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/constructor-defaultValue-defaults-to-undefined.js @@ -0,0 +1,63 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable +description: > + When the AsyncContext.Variable constructor is not passed an options bag + with a `defaultValue` property, it sets [[AsyncVariableDefaultValue]] to + `undefined`. +info: | + AsyncContext.Variable ( options ) + + 3. Let defaultValue be undefined. + 4. If options is an Object, then + ... + c. Set defaultValue to ? Get(options, "defaultValue"). + ... + 7. Set asyncVariable.[[AsyncVariableDefaultValue]] to defaultValue. + 8. Return asyncVariable. +features: [AsyncContext] +---*/ + +assert.sameValue( + new AsyncContext.Variable().get(), + undefined, + '`new AsyncContext.Variable().get()` returns undefined' +); + +assert.sameValue( + new AsyncContext.Variable(42).get(), + undefined, + '`new AsyncContext.Variable(42).get()` returns undefined' +); + +assert.sameValue( + new AsyncContext.Variable(null).get(), + undefined, + '`new AsyncContext.Variable(null).get()` returns undefined' +); + +assert.sameValue( + new AsyncContext.Variable(Symbol()).get(), + undefined, + '`new AsyncContext.Variable(Symbol()).get()` returns undefined' +); + +assert.sameValue( + new AsyncContext.Variable({}).get(), + undefined, + '`new AsyncContext.Variable({}).get()` returns undefined' +); + +assert.sameValue( + new AsyncContext.Variable([]).get(), + undefined, + '`new AsyncContext.Variable([]).get()` returns undefined' +); + +assert.sameValue( + new AsyncContext.Variable({ name: "foo" }).get(), + undefined, + '`new AsyncContext.Variable({ name: "foo" }).get()` returns undefined' +); diff --git a/test/built-ins/AsyncContext/Variable/constructor-defaultValue.js b/test/built-ins/AsyncContext/Variable/constructor-defaultValue.js new file mode 100644 index 00000000000..571df252a19 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/constructor-defaultValue.js @@ -0,0 +1,54 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable +description: > + When the AsyncContext.Variable constructor is passed an options bag with + a `defaultValue` property, it sets [[AsyncVariableDefaultValue]] to that + property's value. +info: | + AsyncContext.Variable ( options ) + + 4. If options is an Object, then + ... + c. Set defaultValue to ? Get(options, "defaultValue"). + ... + 7. Set asyncVariable.[[AsyncVariableDefaultValue]] to defaultValue. + 8. Return asyncVariable. +features: [AsyncContext] +---*/ + +assert.sameValue( + new AsyncContext.Variable({ defaultValue: undefined }).get(), + undefined, + '`new AsyncContext.Variable({ defaultValue: undefined }).get()` returns `undefined`' +); + +assert.sameValue( + new AsyncContext.Variable({ defaultValue: "foo" }).get(), + 'foo', + '`new AsyncContext.Variable({ defaultValue: "foo" }).get()` returns `"foo"`' +); + +assert.sameValue( + new AsyncContext.Variable({ defaultValue: 42 }).get(), + 42, + '`new AsyncContext.Variable({ defaultValue: 42 }).get()` returns `42`' +); + +const symbol = Symbol(); + +assert.sameValue( + new AsyncContext.Variable({ defaultValue: symbol }).get(), + symbol, + '`new AsyncContext.Variable({ defaultValue: symbol }).get()` returns `symbol`' +); + +const obj = {}; + +assert.sameValue( + new AsyncContext.Variable({ defaultValue: obj }).get(), + obj, + '`new AsyncContext.Variable({ defaultValue: obj }).get()` returns `obj`' +); diff --git a/test/built-ins/AsyncContext/Variable/constructor-name-defaults-to-empty-string.js b/test/built-ins/AsyncContext/Variable/constructor-name-defaults-to-empty-string.js new file mode 100644 index 00000000000..6fe86071c94 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/constructor-name-defaults-to-empty-string.js @@ -0,0 +1,66 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable +description: > + When the AsyncContext.Variable constructor is not passed an options bag + with a `name` property, it sets [[AsyncVariableName]] to the empty string. +info: | + AsyncContext.Variable ( options ) + + 2. Let nameStr be the empty String. + ... + 4. If options is an Object, then + a. Let namePresent be ? HasProperty(options, "name"). + b. If namePresent is true, then + i. Let name be ? Get(options, "name"). + ii. Set nameStr to ? ToString(name). + ... + 6. Set asyncVariable.[[AsyncVariableName]] to nameStr. + ... + 8. Return asyncVariable. +features: [AsyncContext] +---*/ + +assert.sameValue( + new AsyncContext.Variable().name, + '', + '`new AsyncContext.Variable().name` returns the empty string' +); + +assert.sameValue( + new AsyncContext.Variable(42).name, + '', + '`new AsyncContext.Variable(42).name` returns the empty string' +); + +assert.sameValue( + new AsyncContext.Variable(null).name, + '', + '`new AsyncContext.Variable(null).name` returns the empty string' +); + +assert.sameValue( + new AsyncContext.Variable(Symbol()).name, + '', + '`new AsyncContext.Variable(Symbol()).name` returns the empty string' +); + +assert.sameValue( + new AsyncContext.Variable({}).name, + '', + '`new AsyncContext.Variable({}).name` returns the empty string' +); + +assert.sameValue( + new AsyncContext.Variable([]).name, + '', + '`new AsyncContext.Variable([]).name` returns the empty string' +); + +assert.sameValue( + new AsyncContext.Variable({ defaultValue: 42 }).name, + '', + '`new AsyncContext.Variable({ defaultValue: 42 }).name` returns the empty string' +); diff --git a/test/built-ins/AsyncContext/Variable/constructor-name.js b/test/built-ins/AsyncContext/Variable/constructor-name.js new file mode 100644 index 00000000000..560a144c02d --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/constructor-name.js @@ -0,0 +1,57 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable +description: > + When the AsyncContext.Variable constructor is passed an options bag with + a `name` property, it sets [[AsyncVariableName]] to the stringification of + that property. +info: | + AsyncContext.Variable ( options ) + + 4. If options is an Object, then + a. Let namePresent be ? HasProperty(options, "name"). + b. If namePresent is true, then + i. Let name be ? Get(options, "name"). + ii. Set nameStr to ? ToString(name). + ... + 6. Set asyncVariable.[[AsyncVariableName]] to nameStr. + ... + 8. Return asyncVariable. +features: [AsyncContext] +---*/ + +function roundtripName(name) { + return new AsyncContext.Variable({ name }).name; +} + +assert.sameValue( + roundtripName("foo"), + 'foo', + '`roundtripName("foo")` returns `"foo"`' +); + +assert.sameValue( + roundtripName(42), + '42', + '`roundtripName(42)` returns `"42"`' +); + +assert.sameValue( + roundtripName(undefined), + 'undefined', + '`roundtripName(undefined)` returns `"undefined"`' +); + +const objectWithStringifier = { + toString() { + return "bar"; + } +} + +assert.sameValue( + roundtripName(objectWithStringifier), + 'bar', + '`roundtripName(objectWithStringifier)` returns `"bar"`' +); diff --git a/test/built-ins/AsyncContext/Variable/constructor-throwing-defaultValue-getter.js b/test/built-ins/AsyncContext/Variable/constructor-throwing-defaultValue-getter.js new file mode 100644 index 00000000000..4884ab6471b --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/constructor-throwing-defaultValue-getter.js @@ -0,0 +1,29 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable +description: > + The AsyncContext.Variable constructor throws when passed an options bag + whose `defaultValue` getter throws. +info: | + AsyncContext.Variable ( options ) + + 4. If options is an Object, then + ... + c. Set defaultValue to ? Get(options, "defaultValue"). + ... +features: [AsyncContext] +---*/ + +function CustomError() { } + +const options = { + get defaultValue() { + throw new CustomError(); + } +}; + +assert.throws(CustomError, () => { + new AsyncContext.Variable(options); +}); diff --git a/test/built-ins/AsyncContext/Variable/constructor-throwing-name-getter.js b/test/built-ins/AsyncContext/Variable/constructor-throwing-name-getter.js new file mode 100644 index 00000000000..0babe5c8556 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/constructor-throwing-name-getter.js @@ -0,0 +1,30 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable +description: > + The AsyncContext.Variable constructor throws when passed an options bag + whose `name` getter throws. +info: | + AsyncContext.Variable ( options ) + + 4. If options is an Object, then + a. Let namePresent be ? HasProperty(options, "name"). + b. If namePresent is true, then + i. Let name be ? Get(options, "name"). + ... +features: [AsyncContext] +---*/ + +function CustomError() { } + +const options = { + get name() { + throw new CustomError(); + } +}; + +assert.throws(CustomError, () => { + new AsyncContext.Variable(options); +}); diff --git a/test/built-ins/AsyncContext/Variable/constructor-throwing-name-hasproperty.js b/test/built-ins/AsyncContext/Variable/constructor-throwing-name-hasproperty.js new file mode 100644 index 00000000000..524338b85a7 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/constructor-throwing-name-hasproperty.js @@ -0,0 +1,31 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable +description: > + The AsyncContext.Variable constructor throws when passed an options bag + where `HasProperty` of `"name"` throws. +info: | + AsyncContext.Variable ( options ) + + 4. If options is an Object, then + a. Let namePresent be ? HasProperty(options, "name"). + ... +features: [AsyncContext] +---*/ + +function CustomError() { } + +const options = new Proxy({}, { + has(_target, prop) { + if (prop === "name") { + throw new CustomError(); + } + return false; + } +}); + +assert.throws(CustomError, () => { + new AsyncContext.Variable(options); +}); diff --git a/test/built-ins/AsyncContext/Variable/constructor-throwing-name-tostring.js b/test/built-ins/AsyncContext/Variable/constructor-throwing-name-tostring.js new file mode 100644 index 00000000000..ff0271a3899 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/constructor-throwing-name-tostring.js @@ -0,0 +1,41 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable +description: > + The AsyncContext.Variable constructor throws when passed an options bag + with a `name` property which throws when stringified. +info: | + AsyncContext.Variable ( options ) + + 4. If options is an Object, then + a. Let namePresent be ? HasProperty(options, "name"). + b. If namePresent is true, then + i. Let name be ? Get(options, "name"). + ii. Set nameStr to ? ToString(name). + ... +features: [AsyncContext] +---*/ + +assert.throws( + TypeError, + () => { + new AsyncContext.Variable({ name: Symbol() }); + }, + 'The AsyncContext.Variable constructor throws when `name` is a symbol' +); + +function CustomError() { } + +const options = { + name: { + toString() { + throw new CustomError(); + } + } +}; + +assert.throws(CustomError, () => { + new AsyncContext.Variable(options); +}); diff --git a/test/built-ins/AsyncContext/Variable/constructor.js b/test/built-ins/AsyncContext/Variable/constructor.js new file mode 100644 index 00000000000..419383f865b --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/constructor.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable-constructor +description: > + The AsyncContext.Variable constructor is the %AsyncContext.Variable% + intrinsic object and the initial value of the Variable property of the + %AsyncContext% object. +features: [AsyncContext] +---*/ + +assert.sameValue( + typeof AsyncContext.Variable, 'function', + 'typeof AsyncContext.Variable is function' +); diff --git a/test/built-ins/AsyncContext/Variable/instance-extensible.js b/test/built-ins/AsyncContext/Variable/instance-extensible.js new file mode 100644 index 00000000000..de52e718ab5 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/instance-extensible.js @@ -0,0 +1,11 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable +description: Instances of AsyncContext.Variable are extensible +features: [AsyncContext] +---*/ + +var asyncVariable = new AsyncContext.Variable(); +assert.sameValue(Object.isExtensible(asyncVariable), true); diff --git a/test/built-ins/AsyncContext/Variable/instance-prototype.js b/test/built-ins/AsyncContext/Variable/instance-prototype.js new file mode 100644 index 00000000000..64407fd8334 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/instance-prototype.js @@ -0,0 +1,20 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype +description: > + The initial value of AsyncContext.Variable.prototype is the + AsyncContext.Variable prototype object. +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +assert.sameValue( + AsyncContext.Variable.prototype.isPrototypeOf(new AsyncContext.Variable()), true, + 'AsyncContext.Variable.prototype.isPrototypeOf(new AsyncContext.Variable()) returns true' +); + +verifyNotEnumerable(AsyncContext.Variable, 'prototype'); +verifyNotWritable(AsyncContext.Variable, 'prototype'); +verifyNotConfigurable(AsyncContext.Variable, 'prototype'); diff --git a/test/built-ins/AsyncContext/Variable/internal-prototype.js b/test/built-ins/AsyncContext/Variable/internal-prototype.js new file mode 100644 index 00000000000..14ca72270ef --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/internal-prototype.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-properties-of-the-asynccontext-variable-constructor +description: > + The AsyncContext.Variable constructor has a [[Prototype]] internal slot whose + value is %Function.prototype%. +features: [AsyncContext] +---*/ + +assert.sameValue( + Function.prototype.isPrototypeOf(AsyncContext.Variable), + true, + 'Function.prototype.isPrototypeOf(AsyncContext.Variable) returns true' +); diff --git a/test/built-ins/AsyncContext/Variable/is-a-constructor.js b/test/built-ins/AsyncContext/Variable/is-a-constructor.js new file mode 100644 index 00000000000..ae09a8d0393 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/is-a-constructor.js @@ -0,0 +1,12 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable-constructor +description: The AsyncContext.Variable constructor implements [[Construct]] +includes: [isConstructor.js] +features: [AsyncContext, Reflect.construct] +---*/ + +assert.sameValue(isConstructor(AsyncContext.Variable), true, 'isConstructor(AsyncContext.Variable) must return true'); +new AsyncContext.Variable(); diff --git a/test/built-ins/AsyncContext/Variable/length.js b/test/built-ins/AsyncContext/Variable/length.js new file mode 100644 index 00000000000..028a48f37a5 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/length.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable +description: AsyncContext.Variable.length property descriptor +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +verifyProperty(AsyncContext.Variable, 'length', { + value: 1, + writable: false, + enumerable: false, + configurable: true +}); diff --git a/test/built-ins/AsyncContext/Variable/name.js b/test/built-ins/AsyncContext/Variable/name.js new file mode 100644 index 00000000000..dd8afe3debf --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/name.js @@ -0,0 +1,15 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable +description: AsyncContext.Variable.name value and descriptor +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +assert.sameValue(AsyncContext.Variable.name, 'Variable', 'The value of AsyncContext.Variable.name is "Variable"'); + +verifyNotEnumerable(AsyncContext.Variable, 'name'); +verifyNotWritable(AsyncContext.Variable, 'name'); +verifyConfigurable(AsyncContext.Variable, 'name'); diff --git a/test/built-ins/AsyncContext/Variable/newtarget-prototype-is-not-object.js b/test/built-ins/AsyncContext/Variable/newtarget-prototype-is-not-object.js new file mode 100644 index 00000000000..3b6575ab153 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/newtarget-prototype-is-not-object.js @@ -0,0 +1,57 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable +description: > + [[Prototype]] defaults to %AsyncContext.Variable.prototype% if NewTarget.prototype is not an object. +info: | + AsyncContext.Variable ( options ) + + ... + 5. Let asyncVariable be ? OrdinaryCreateFromConstructor(NewTarget, "%AsyncContext.Variable.prototype%", « [[AsyncVariableName]], [[AsyncVariableDefaultValue]] »). + ... + 8. Return asyncVariable. + + OrdinaryCreateFromConstructor ( constructor, intrinsicDefaultProto [ , internalSlotsList ] ) + + ... + 2. Let proto be ? GetPrototypeFromConstructor(constructor, intrinsicDefaultProto). + 3. Return ObjectCreate(proto, internalSlotsList). + + GetPrototypeFromConstructor ( constructor, intrinsicDefaultProto ) + + 3. Let proto be ? Get(constructor, 'prototype'). + 4. If Type(proto) is not Object, then + a. Let realm be ? GetFunctionRealm(constructor). + b. Set proto to realm's intrinsic object named intrinsicDefaultProto. + 5. Return proto. +features: [AsyncContext, Reflect.construct, Symbol] +---*/ + +var asyncVariable; +function newTarget() { } + +newTarget.prototype = undefined; +asyncVariable = Reflect.construct(AsyncContext.Variable, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncVariable), AsyncContext.Variable.prototype, 'newTarget.prototype is undefined'); + +newTarget.prototype = null; +asyncVariable = Reflect.construct(AsyncContext.Variable, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncVariable), AsyncContext.Variable.prototype, 'newTarget.prototype is null'); + +newTarget.prototype = true; +asyncVariable = Reflect.construct(AsyncContext.Variable, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncVariable), AsyncContext.Variable.prototype, 'newTarget.prototype is a Boolean'); + +newTarget.prototype = ''; +asyncVariable = Reflect.construct(AsyncContext.Variable, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncVariable), AsyncContext.Variable.prototype, 'newTarget.prototype is a String'); + +newTarget.prototype = Symbol(); +asyncVariable = Reflect.construct(AsyncContext.Variable, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncVariable), AsyncContext.Variable.prototype, 'newTarget.prototype is a Symbol'); + +newTarget.prototype = 1; +asyncVariable = Reflect.construct(AsyncContext.Variable, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncVariable), AsyncContext.Variable.prototype, 'newTarget.prototype is a Number'); diff --git a/test/built-ins/AsyncContext/Variable/prop-desc.js b/test/built-ins/AsyncContext/Variable/prop-desc.js new file mode 100644 index 00000000000..288f301b533 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prop-desc.js @@ -0,0 +1,22 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable-constructor +description: > + Property descriptor of AsyncContext.Variable +info: | + 17 ECMAScript Standard Built-in Objects: + + Every other data property described in clauses 18 through 26 and in Annex B.2 + has the attributes { [[Writable]]: true, [[Enumerable]]: false, + [[Configurable]]: true } unless otherwise specified. +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +verifyProperty(AsyncContext, 'Variable', { + enumerable: false, + writable: true, + configurable: true +}); diff --git a/test/built-ins/AsyncContext/Variable/proto-from-ctor-realm.js b/test/built-ins/AsyncContext/Variable/proto-from-ctor-realm.js new file mode 100644 index 00000000000..2ad0648dd61 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/proto-from-ctor-realm.js @@ -0,0 +1,58 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable +description: Default [[Prototype]] value derived from realm of the newTarget +info: | + AsyncContext.Variable ( options ) + + ... + 5. Let asyncVariable be ? OrdinaryCreateFromConstructor(NewTarget, "%AsyncContext.Variable.prototype%", « [[AsyncVariableName]], [[AsyncVariableDefaultValue]] »). + ... + 8. Return asyncVariable. + + OrdinaryCreateFromConstructor ( constructor, intrinsicDefaultProto [ , internalSlotsList ] ) + + ... + 2. Let proto be ? GetPrototypeFromConstructor(constructor, intrinsicDefaultProto). + 3. Return ObjectCreate(proto, internalSlotsList). + + GetPrototypeFromConstructor ( constructor, intrinsicDefaultProto ) + + 3. Let proto be ? Get(constructor, 'prototype'). + 4. If Type(proto) is not Object, then + a. Let realm be ? GetFunctionRealm(constructor). + b. Set proto to realm's intrinsic object named intrinsicDefaultProto. + 5. Return proto. +features: [AsyncContext, cross-realm, Reflect, Symbol] +---*/ + +var other = $262.createRealm().global; +var newTarget = new other.Function(); +var asyncVariable; + +newTarget.prototype = undefined; +asyncVariable = Reflect.construct(AsyncContext.Variable, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncVariable), other.AsyncContext.Variable.prototype, 'newTarget.prototype is undefined'); + +newTarget.prototype = null; +asyncVariable = Reflect.construct(AsyncContext.Variable, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncVariable), other.AsyncContext.Variable.prototype, 'newTarget.prototype is null'); + +newTarget.prototype = true; +asyncVariable = Reflect.construct(AsyncContext.Variable, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncVariable), other.AsyncContext.Variable.prototype, 'newTarget.prototype is a Boolean'); + +newTarget.prototype = ''; +asyncVariable = Reflect.construct(AsyncContext.Variable, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncVariable), other.AsyncContext.Variable.prototype, 'newTarget.prototype is a String'); + +newTarget.prototype = Symbol(); +asyncVariable = Reflect.construct(AsyncContext.Variable, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncVariable), other.AsyncContext.Variable.prototype, 'newTarget.prototype is a Symbol'); + +newTarget.prototype = 1; +asyncVariable = Reflect.construct(AsyncContext.Variable, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncVariable), other.AsyncContext.Variable.prototype, 'newTarget.prototype is a Number'); + diff --git a/test/built-ins/AsyncContext/Variable/proto.js b/test/built-ins/AsyncContext/Variable/proto.js new file mode 100644 index 00000000000..50b9860e980 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/proto.js @@ -0,0 +1,18 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-properties-of-the-asynccontext-variable-constructor +description: > + The prototype of AsyncContext.Variable is %Function.prototype% +info: | + The value of the [[Prototype]] internal slot of the AsyncContext.Variable + constructor is the intrinsic object %Function.prototype%. +features: [AsyncContext] +---*/ + +assert.sameValue( + Object.getPrototypeOf(AsyncContext.Variable), + Function.prototype, + 'Object.getPrototypeOf(AsyncContext.Variable) returns the value of `Function.prototype`' +); diff --git a/test/built-ins/AsyncContext/Variable/prototype-from-newtarget-abrupt.js b/test/built-ins/AsyncContext/Variable/prototype-from-newtarget-abrupt.js new file mode 100644 index 00000000000..4441a1c0aaf --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype-from-newtarget-abrupt.js @@ -0,0 +1,41 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable +description: > + Return abrupt from getting the NewTarget prototype +info: | + AsyncContext.Variable ( options ) + + ... + 5. Let asyncVariable be ? OrdinaryCreateFromConstructor(NewTarget, "%AsyncContext.Variable.prototype%", « [[AsyncVariableName]], [[AsyncVariableDefaultValue]] »). + ... + 8. Return asyncVariable. + + OrdinaryCreateFromConstructor ( constructor, intrinsicDefaultProto [ , internalSlotsList ] ) + + ... + 2. Let proto be ? GetPrototypeFromConstructor(constructor, intrinsicDefaultProto). + 3. Return ObjectCreate(proto, internalSlotsList). + + GetPrototypeFromConstructor ( constructor, intrinsicDefaultProto ) + + 3. Let proto be ? Get(constructor, 'prototype'). +features: [AsyncContext, Reflect.construct] +---*/ + +var calls = 0; +var newTarget = function() {}.bind(null); +Object.defineProperty(newTarget, 'prototype', { + get: function() { + calls += 1; + throw new Test262Error(); + } +}); + +assert.throws(Test262Error, function() { + Reflect.construct(AsyncContext.Variable, [], newTarget); +}); + +assert.sameValue(calls, 1); diff --git a/test/built-ins/AsyncContext/Variable/prototype-from-newtarget-custom.js b/test/built-ins/AsyncContext/Variable/prototype-from-newtarget-custom.js new file mode 100644 index 00000000000..e719ab0f088 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype-from-newtarget-custom.js @@ -0,0 +1,44 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable +description: > + The [[Prototype]] internal slot is computed from NewTarget. +info: | + AsyncContext.Variable ( options ) + + ... + 5. Let asyncVariable be ? OrdinaryCreateFromConstructor(NewTarget, "%AsyncContext.Variable.prototype%", « [[AsyncVariableName]], [[AsyncVariableDefaultValue]] »). + ... + 8. Return asyncVariable. + + OrdinaryCreateFromConstructor ( constructor, intrinsicDefaultProto [ , internalSlotsList ] ) + + ... + 2. Let proto be ? GetPrototypeFromConstructor(constructor, intrinsicDefaultProto). + 3. Return ObjectCreate(proto, internalSlotsList). + + GetPrototypeFromConstructor ( constructor, intrinsicDefaultProto ) + + 3. Let proto be ? Get(constructor, 'prototype'). + 4. If Type(proto) is not Object, then + a. Let realm be ? GetFunctionRealm(constructor). + b. Set proto to realm's intrinsic object named intrinsicDefaultProto. + 5. Return proto. +features: [AsyncContext, Reflect.construct] +---*/ + +var asyncVariable; + +asyncVariable = Reflect.construct(AsyncContext.Variable, [], Object); +assert.sameValue(Object.getPrototypeOf(asyncVariable), Object.prototype, 'NewTarget is built-in Object constructor'); + +var newTarget = function() {}.bind(null); +Object.defineProperty(newTarget, 'prototype', { + get: function() { + return Array.prototype; + } +}); +asyncVariable = Reflect.construct(AsyncContext.Variable, [], newTarget); +assert.sameValue(Object.getPrototypeOf(asyncVariable), Array.prototype, 'NewTarget is BoundFunction with accessor'); diff --git a/test/built-ins/AsyncContext/Variable/prototype-from-newtarget.js b/test/built-ins/AsyncContext/Variable/prototype-from-newtarget.js new file mode 100644 index 00000000000..b61a4e8f813 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype-from-newtarget.js @@ -0,0 +1,33 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable +description: > + The [[Prototype]] internal slot is computed from NewTarget. +info: | + AsyncContext.Variable ( options ) + + ... + 5. Let asyncVariable be ? OrdinaryCreateFromConstructor(NewTarget, "%AsyncContext.Variable.prototype%", « [[AsyncVariableName]], [[AsyncVariableDefaultValue]] »). + ... + 8. Return asyncVariable. + + OrdinaryCreateFromConstructor ( constructor, intrinsicDefaultProto [ , internalSlotsList ] ) + + ... + 2. Let proto be ? GetPrototypeFromConstructor(constructor, intrinsicDefaultProto). + 3. Return ObjectCreate(proto, internalSlotsList). + + GetPrototypeFromConstructor ( constructor, intrinsicDefaultProto ) + + 3. Let proto be ? Get(constructor, 'prototype'). + 4. If Type(proto) is not Object, then + a. Let realm be ? GetFunctionRealm(constructor). + b. Set proto to realm's intrinsic object named intrinsicDefaultProto. + 5. Return proto. +features: [AsyncContext] +---*/ + +var asyncVariable = new AsyncContext.Variable(); +assert.sameValue(Object.getPrototypeOf(asyncVariable), AsyncContext.Variable.prototype); diff --git a/test/built-ins/AsyncContext/Variable/prototype/Symbol.toStringTag.js b/test/built-ins/AsyncContext/Variable/prototype/Symbol.toStringTag.js new file mode 100644 index 00000000000..ae866bd7030 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/Symbol.toStringTag.js @@ -0,0 +1,22 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype-@@tostringtag +description: > + `Symbol.toStringTag` property descriptor +info: | + The initial value of the @@toStringTag property is the String value + "AsyncContext.Variable". + + This property has the attributes { [[Writable]]: false, [[Enumerable]]: + false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [AsyncContext, Symbol.toStringTag] +---*/ + +assert.sameValue(AsyncContext.Variable.prototype[Symbol.toStringTag], 'AsyncContext.Variable'); + +verifyNotEnumerable(AsyncContext.Variable.prototype, Symbol.toStringTag); +verifyNotWritable(AsyncContext.Variable.prototype, Symbol.toStringTag); +verifyConfigurable(AsyncContext.Variable.prototype, Symbol.toStringTag); diff --git a/test/built-ins/AsyncContext/Variable/prototype/constructor.js b/test/built-ins/AsyncContext/Variable/prototype/constructor.js new file mode 100644 index 00000000000..c4ecb1c9f00 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/constructor.js @@ -0,0 +1,19 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.constructor +description: AsyncContext.Variable.prototype.constructor value and descriptor +info: | + The initial value of AsyncContext.Variable.prototype.constructor is + %AsyncContext.Variable%. +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +assert.sameValue(AsyncContext.Variable.prototype.constructor, AsyncContext.Variable); +assert.sameValue((new AsyncContext.Variable()).constructor, AsyncContext.Variable); + +verifyNotEnumerable(AsyncContext.Variable.prototype, 'constructor'); +verifyWritable(AsyncContext.Variable.prototype, 'constructor'); +verifyConfigurable(AsyncContext.Variable.prototype, 'constructor'); diff --git a/test/built-ins/AsyncContext/Variable/prototype/get/context-is-not-object.js b/test/built-ins/AsyncContext/Variable/prototype/get/context-is-not-object.js new file mode 100644 index 00000000000..9ee63ef13a5 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/get/context-is-not-object.js @@ -0,0 +1,44 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.get +description: > + Throws a TypeError if `this` is not an Object. +info: | + AsyncContext.Variable.prototype.get ( ) + + 1. Let asyncVariable be the this value. + 2. Perform ? RequireInternalSlot(asyncVariable, [[AsyncVariableDefaultValue]]). + ... + + RequireInternalSlot(O, internalSlot) + + 1. If O is not an Object, throw a TypeError exception. + ... +features: [AsyncContext, Symbol] +---*/ + +assert.throws(TypeError, function () { + AsyncContext.Variable.prototype.get.call(1); +}); + +assert.throws(TypeError, function () { + AsyncContext.Variable.prototype.get.call(true); +}); + +assert.throws(TypeError, function () { + AsyncContext.Variable.prototype.get.call(''); +}); + +assert.throws(TypeError, function () { + AsyncContext.Variable.prototype.get.call(null); +}); + +assert.throws(TypeError, function () { + AsyncContext.Variable.prototype.get.call(undefined); +}); + +assert.throws(TypeError, function () { + AsyncContext.Variable.prototype.get.call(Symbol()); +}); diff --git a/test/built-ins/AsyncContext/Variable/prototype/get/context-is-not-variable-object.js b/test/built-ins/AsyncContext/Variable/prototype/get/context-is-not-variable-object.js new file mode 100644 index 00000000000..bf8fdd62e6b --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/get/context-is-not-variable-object.js @@ -0,0 +1,30 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.get +description: > + Throws a TypeError if `this` does not have an [[AsyncVariableDefaultValue]] + internal slot. +info: | + AsyncContext.Variable.prototype.get ( ) + + 1. Let asyncVariable be the this value. + 2. Perform ? RequireInternalSlot(asyncVariable, [[AsyncVariableDefaultValue]]). + ... + + RequireInternalSlot(O, internalSlot) + + ... + 2. If O does not have an internalSlot internal slot, throw a TypeError exception. + ... +features: [AsyncContext] +---*/ + +assert.throws(TypeError, function () { + AsyncContext.Variable.prototype.get.call({}); +}); + +assert.throws(TypeError, function () { + AsyncContext.Variable.prototype.get.call([]); +}); diff --git a/test/built-ins/AsyncContext/Variable/prototype/get/context-is-snapshot-object-throws.js b/test/built-ins/AsyncContext/Variable/prototype/get/context-is-snapshot-object-throws.js new file mode 100644 index 00000000000..d6d8b770b8b --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/get/context-is-snapshot-object-throws.js @@ -0,0 +1,25 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.get +description: > + Throws a TypeError if `this` is an AsyncContext.Snapshot object. +info: | + AsyncContext.Variable.prototype.get ( ) + + 1. Let asyncVariable be the this value. + 2. Perform ? RequireInternalSlot(asyncVariable, [[AsyncVariableDefaultValue]]). + ... + + RequireInternalSlot(O, internalSlot) + + ... + 2. If O does not have an internalSlot internal slot, throw a TypeError exception. + ... +features: [AsyncContext] +---*/ + +assert.throws(TypeError, function () { + AsyncContext.Variable.prototype.get.call(new AsyncContext.Snapshot()); +}); diff --git a/test/built-ins/AsyncContext/Variable/prototype/get/get.js b/test/built-ins/AsyncContext/Variable/prototype/get/get.js new file mode 100644 index 00000000000..7a037736958 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/get/get.js @@ -0,0 +1,23 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.get +description: > + AsyncContext.Variable.prototype.get ( ) + + 17 ECMAScript Standard Built-in Objects + +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +assert.sameValue( + typeof AsyncContext.Variable.prototype.get, + 'function', + 'typeof AsyncContext.Variable.prototype.get is "function"' +); + +verifyNotEnumerable(AsyncContext.Variable.prototype, 'get'); +verifyWritable(AsyncContext.Variable.prototype, 'get'); +verifyConfigurable(AsyncContext.Variable.prototype, 'get'); diff --git a/test/built-ins/AsyncContext/Variable/prototype/get/length.js b/test/built-ins/AsyncContext/Variable/prototype/get/length.js new file mode 100644 index 00000000000..49d296c4ec6 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/get/length.js @@ -0,0 +1,24 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.get +description: > + AsyncContext.Variable.prototype.get.length value and descriptor. +info: | + AsyncContext.Variable.prototype.get ( ) + + 17 ECMAScript Standard Built-in Objects + +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +assert.sameValue( + AsyncContext.Variable.prototype.get.length, 0, + 'The value of `AsyncContext.Variable.prototype.get.length` is `0`' +); + +verifyNotEnumerable(AsyncContext.Variable.prototype.get, 'length'); +verifyNotWritable(AsyncContext.Variable.prototype.get, 'length'); +verifyConfigurable(AsyncContext.Variable.prototype.get, 'length'); diff --git a/test/built-ins/AsyncContext/Variable/prototype/get/name.js b/test/built-ins/AsyncContext/Variable/prototype/get/name.js new file mode 100644 index 00000000000..2e41b72e062 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/get/name.js @@ -0,0 +1,24 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.get +description: > + AsyncContext.Variable.prototype.get.name value and descriptor. +info: | + AsyncContext.Variable.prototype.get ( ) + + 17 ECMAScript Standard Built-in Objects + +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +assert.sameValue( + AsyncContext.Variable.prototype.get.name, 'get', + 'The value of `AsyncContext.Variable.prototype.get.name` is `"get"`' +); + +verifyNotEnumerable(AsyncContext.Variable.prototype.get, 'name'); +verifyNotWritable(AsyncContext.Variable.prototype.get, 'name'); +verifyConfigurable(AsyncContext.Variable.prototype.get, 'name'); diff --git a/test/built-ins/AsyncContext/Variable/prototype/get/not-a-constructor.js b/test/built-ins/AsyncContext/Variable/prototype/get/not-a-constructor.js new file mode 100644 index 00000000000..4ed1a7a51ca --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/get/not-a-constructor.js @@ -0,0 +1,30 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-ecmascript-standard-built-in-objects +description: > + AsyncContext.Variable.prototype.get does not implement [[Construct]], is not + new-able +info: | + ECMAScript Function Objects + + Built-in function objects that are not identified as constructors do not + implement the [[Construct]] internal method unless otherwise specified in + the description of a particular function. + + sec-evaluatenew + + ... + 7. If IsConstructor(constructor) is false, throw a TypeError exception. + ... +includes: [isConstructor.js] +features: [Reflect.construct, AsyncContext, arrow-function] +---*/ + +assert.sameValue(isConstructor(AsyncContext.Variable.prototype.get), false, 'isConstructor(AsyncContext.Variable.prototype.get) must return false'); + +assert.throws(TypeError, () => { + let v = new AsyncContext.Variable(); new v.get(); +}, '`let v = new AsyncContext.Variable(); new v.get();` throws TypeError'); + diff --git a/test/built-ins/AsyncContext/Variable/prototype/get/returns-default-value-outside-run.js b/test/built-ins/AsyncContext/Variable/prototype/get/returns-default-value-outside-run.js new file mode 100644 index 00000000000..af7b86a42f3 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/get/returns-default-value-outside-run.js @@ -0,0 +1,33 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.get +description: > + When called outside of any `AsyncContext.Variable.prototype.run` or + `AsyncContext.Snapshot.prototype.run` callback, it returns + [[AsyncVariableDefaultValue]]. +info: | + AsyncContext.Variable.prototype.get ( ) + + 3. Let agentRecord be the surrounding agent's Agent Record. + 4. Let asyncContextMapping be agentRecord.[[AsyncContextMapping]]. + 5. For each Async Context Mapping Record p of asyncContextMapping, do + a. If SameValueZero(p.[[AsyncContextKey]], asyncVariable) is true, return p.[[AsyncContextValue]]. + 6. Return asyncVariable.[[AsyncVariableDefaultValue]]. + + An agent record's [[AsyncContextMapping]] field is initially an empty list, + and this only changes in `AsyncContext.Variable.prototype.run` and + `AsyncContext.Snapshot.prototype.run`. +features: [AsyncContext] +---*/ + +const defaultValue = Symbol(); + +const asyncVariable = new AsyncContext.Variable({ defaultValue }); + +assert.sameValue( + asyncVariable.get(), + defaultValue, + 'The value of `asyncVariable.get()` is `defaultValue`' +) diff --git a/test/built-ins/AsyncContext/Variable/prototype/get/returns-undefined-if-set-in-run.js b/test/built-ins/AsyncContext/Variable/prototype/get/returns-undefined-if-set-in-run.js new file mode 100644 index 00000000000..771d424d030 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/get/returns-undefined-if-set-in-run.js @@ -0,0 +1,28 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.get +description: > + When called in the callback passed to `AsyncContext.Variable.prototype.run`, + it returns the first argument to `run`, even if that is `undefined` and the + `AsyncContext.Variable` instance has a different default value. +info: | + AsyncContext.Variable.prototype.get ( ) + + 3. Let agentRecord be the surrounding agent's Agent Record. + 4. Let asyncContextMapping be agentRecord.[[AsyncContextMapping]]. + 5. For each Async Context Mapping Record p of asyncContextMapping, do + a. If SameValueZero(p.[[AsyncContextKey]], asyncVariable) is true, return p.[[AsyncContextValue]]. +features: [AsyncContext] +---*/ + +const asyncVariable = new AsyncContext.Variable({ defaultValue: 42 }); + +asyncVariable.run(undefined, () => { + assert.sameValue( + asyncVariable.get(), + undefined, + 'The value of `asyncVariable.get()` is `undefined`' + ); +}); diff --git a/test/built-ins/AsyncContext/Variable/prototype/get/returns-value-set-in-run.js b/test/built-ins/AsyncContext/Variable/prototype/get/returns-value-set-in-run.js new file mode 100644 index 00000000000..974cf9d5202 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/get/returns-value-set-in-run.js @@ -0,0 +1,39 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.get +description: > + When called in the callback passed to `AsyncContext.Variable.prototype.run`, + it returns the first argument to `run`. +info: | + AsyncContext.Variable.prototype.get ( ) + + 3. Let agentRecord be the surrounding agent's Agent Record. + 4. Let asyncContextMapping be agentRecord.[[AsyncContextMapping]]. + 5. For each Async Context Mapping Record p of asyncContextMapping, do + a. If SameValueZero(p.[[AsyncContextKey]], asyncVariable) is true, return p.[[AsyncContextValue]]. + + AsyncContext.Variable.prototype.run ( value, func, ...args ) + + 4. Let asyncContextMapping be a new empty List. + ... + 7. Let p be the Async Context Mapping Record { [[AsyncContextKey]]: asyncVariable, [[AsyncContextValue]]: value }. + 8. Append p to asyncContextMapping. + 9. AsyncContextSwap(asyncContextMapping). + 10. Let result be Completion(Call(func, undefined, args)). + ... +features: [AsyncContext] +---*/ + +const value = Symbol(); + +const asyncVariable = new AsyncContext.Variable(); + +asyncVariable.run(value, () => { + assert.sameValue( + asyncVariable.get(), + value, + 'The value of `asyncVariable.get()` is `value`' + ); +}); diff --git a/test/built-ins/AsyncContext/Variable/prototype/name/does-not-have-internal-slot-snapshot.js b/test/built-ins/AsyncContext/Variable/prototype/name/does-not-have-internal-slot-snapshot.js new file mode 100644 index 00000000000..9175c804785 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/name/does-not-have-internal-slot-snapshot.js @@ -0,0 +1,27 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.name +description: > + Throws a TypeError if `this` object does not have an [[AsyncVariableName]] + internal slot, which also applies to AsyncContext.Snapshot objects. +info: | + get AsyncContext.Variable.prototype.name + + 1. Let asyncVariable be the this value. + 2. Perform ? RequireInternalSlot(asyncVariable, [[AsyncVariableName]]). + ... +features: [AsyncContext] +---*/ + +var descriptor = Object.getOwnPropertyDescriptor(AsyncContext.Variable.prototype, 'name'); + +var asyncVariable = new AsyncContext.Variable(); + +// Does not throw +descriptor.get.call(asyncVariable); + +assert.throws(TypeError, function () { + descriptor.get.call(new AsyncContext.Snapshot()); +}); diff --git a/test/built-ins/AsyncContext/Variable/prototype/name/does-not-have-internal-slot.js b/test/built-ins/AsyncContext/Variable/prototype/name/does-not-have-internal-slot.js new file mode 100644 index 00000000000..1f0a0065c33 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/name/does-not-have-internal-slot.js @@ -0,0 +1,26 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.name +description: > + Throws a TypeError if `this` object does not have an [[AsyncVariableName]] internal slot. +info: | + get AsyncContext.Variable.prototype.name + + 1. Let asyncVariable be the this value. + 2. Perform ? RequireInternalSlot(asyncVariable, [[AsyncVariableName]]). + ... +features: [AsyncContext] +---*/ + +var descriptor = Object.getOwnPropertyDescriptor(AsyncContext.Variable.prototype, 'name'); + +var asyncVariable = new AsyncContext.Variable(); + +// Does not throw +descriptor.get.call(asyncVariable); + +assert.throws(TypeError, function () { + descriptor.get.call([]); +}); diff --git a/test/built-ins/AsyncContext/Variable/prototype/name/length.js b/test/built-ins/AsyncContext/Variable/prototype/name/length.js new file mode 100644 index 00000000000..1a4b890be29 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/name/length.js @@ -0,0 +1,25 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.name +description: > + AsyncContext.Variable.prototype.name.length value and descriptor. +info: | + get AsyncContext.Variable.prototype.name + + 17 ECMAScript Standard Built-in Objects +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +var descriptor = Object.getOwnPropertyDescriptor(AsyncContext.Variable.prototype, 'name'); + +assert.sameValue( + descriptor.get.length, 0, + 'The value of `AsyncContext.Variable.prototype.name.length` is `0`' +); + +verifyNotEnumerable(descriptor.get, 'length'); +verifyNotWritable(descriptor.get, 'length'); +verifyConfigurable(descriptor.get, 'length'); diff --git a/test/built-ins/AsyncContext/Variable/prototype/name/name-getter.js b/test/built-ins/AsyncContext/Variable/prototype/name/name-getter.js new file mode 100644 index 00000000000..e507516a775 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/name/name-getter.js @@ -0,0 +1,31 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.name +description: > + Property type and descriptor. +info: | + get AsyncContext.Variable.prototype.name + + 17 ECMAScript Standard Built-in Objects + +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +var descriptor = Object.getOwnPropertyDescriptor(AsyncContext.Variable.prototype, 'name'); + +assert.sameValue( + typeof descriptor.get, + 'function', + 'typeof descriptor.get is function' +); +assert.sameValue( + typeof descriptor.set, + 'undefined', + 'typeof descriptor.set is undefined' +); + +verifyNotEnumerable(AsyncContext.Variable.prototype, 'name'); +verifyConfigurable(AsyncContext.Variable.prototype, 'name'); diff --git a/test/built-ins/AsyncContext/Variable/prototype/name/name.js b/test/built-ins/AsyncContext/Variable/prototype/name/name.js new file mode 100644 index 00000000000..29bdbd8006f --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/name/name.js @@ -0,0 +1,29 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.name +description: > + AsyncContext.Variable.prototype.name.name value and descriptor. +info: | + get AsyncContext.Variable.prototype.name + + 17 ECMAScript Standard Built-in Objects + + Functions that are specified as get or set accessor functions of built-in + properties have "get " or "set " prepended to the property name string. + +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +var descriptor = Object.getOwnPropertyDescriptor(AsyncContext.Variable.prototype, 'name'); + +assert.sameValue(descriptor.get.name, + 'get name', + 'The value of `descriptor.get.name` is `get name`' +); + +verifyNotEnumerable(descriptor.get, 'name'); +verifyNotWritable(descriptor.get, 'name'); +verifyConfigurable(descriptor.get, 'name'); diff --git a/test/built-ins/AsyncContext/Variable/prototype/name/returns-name-passed-to-constructor.js b/test/built-ins/AsyncContext/Variable/prototype/name/returns-name-passed-to-constructor.js new file mode 100644 index 00000000000..256a66830a5 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/name/returns-name-passed-to-constructor.js @@ -0,0 +1,22 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.name +description: > + Returns the name that was passed to the AsyncContext.Variable constructor, + converted to a string. +info: | + get AsyncContext.Variable.prototype.name + + 3. Return asyncVariable.[[AsyncVariableName]]. +features: [AsyncContext] +---*/ + +const asyncVar = new AsyncContext.Variable({ name: "foo" }); + +assert.sameValue( + asyncVar.name, + "foo", + 'The value of `asyncVar.name` is `"foo"`' +); diff --git a/test/built-ins/AsyncContext/Variable/prototype/name/this-not-object-throw.js b/test/built-ins/AsyncContext/Variable/prototype/name/this-not-object-throw.js new file mode 100644 index 00000000000..64bb60e86d5 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/name/this-not-object-throw.js @@ -0,0 +1,51 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.name +description: > + Throws a TypeError if `this` is not an Object. +info: | + get AsyncContext.Variable.prototype.name + + 1. Let asyncVariable be the this value. + 2. Perform ? RequireInternalSlot(asyncVariable, [[AsyncVariableDefaultValue]]). + ... + + RequireInternalSlot ( O, internalSlot ) + + 1. If O is not an Object, throw a TypeError exception. + ... + +features: [Symbol, AsyncContext] +---*/ + +var descriptor = Object.getOwnPropertyDescriptor(AsyncContext.Variable.prototype, 'name'); + +assert.throws(TypeError, function () { + descriptor.get.call(1); +}); + +assert.throws(TypeError, function () { + descriptor.get.call(false); +}); + +assert.throws(TypeError, function () { + descriptor.get.call(1); +}); + +assert.throws(TypeError, function () { + descriptor.get.call(''); +}); + +assert.throws(TypeError, function () { + descriptor.get.call(undefined); +}); + +assert.throws(TypeError, function () { + descriptor.get.call(null); +}); + +assert.throws(TypeError, function () { + descriptor.get.call(Symbol()); +}); diff --git a/test/built-ins/AsyncContext/Variable/prototype/prop-desc.js b/test/built-ins/AsyncContext/Variable/prototype/prop-desc.js new file mode 100644 index 00000000000..9989dd3cf51 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/prop-desc.js @@ -0,0 +1,18 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype +description: AsyncContext.Variable.prototype property attributes. +info: | + This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, + [[Configurable]]: false }. +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +verifyProperty(AsyncContext.Variable, 'prototype', { + writable: false, + enumerable: false, + configurable: false +}); diff --git a/test/built-ins/AsyncContext/Variable/prototype/proto.js b/test/built-ins/AsyncContext/Variable/prototype/proto.js new file mode 100644 index 00000000000..1a46ae35246 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/proto.js @@ -0,0 +1,15 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-properties-of-the-asynccontext-variable-prototype-object +description: | + The prototype of AsyncContext.Variable.prototype is Object.prototype. +info: | + The value of the [[Prototype]] internal slot of the AsyncContext.Variable + prototype object is the intrinsic object %Object.prototype%. +features: [AsyncContext] +---*/ + +var proto = Object.getPrototypeOf(AsyncContext.Variable.prototype); +assert.sameValue(proto, Object.prototype); diff --git a/test/built-ins/AsyncContext/Variable/prototype/run/calls-callback-with-args.js b/test/built-ins/AsyncContext/Variable/prototype/run/calls-callback-with-args.js new file mode 100644 index 00000000000..9ff4535fefe --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/run/calls-callback-with-args.js @@ -0,0 +1,36 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.run +description: > + Calls the second parameter as a function, passing any arguments after the + second to it. +info: | + AsyncContext.Variable.prototype.run ( value, func, ...args ) + + 10. Let result be Completion(Call(func, undefined, args)). + ... + 12. Return result. +features: [AsyncContext] +---*/ + +const asyncVar = new AsyncContext.Variable(); + +const obj = {}; +const symbol = Symbol(); + +let called = false; + +asyncVar.run("foo", callback, 42, "bar", obj, symbol); + +assert(called, 'The `callback` function was called.'); + +function callback() { + assert.sameValue(arguments.length, 4); + assert.sameValue(arguments[0], 42); + assert.sameValue(arguments[1], "bar"); + assert.sameValue(arguments[2], obj); + assert.sameValue(arguments[3], symbol); + called = true; +} diff --git a/test/built-ins/AsyncContext/Variable/prototype/run/calls-callback-with-undefined-this.js b/test/built-ins/AsyncContext/Variable/prototype/run/calls-callback-with-undefined-this.js new file mode 100644 index 00000000000..c05b4c29452 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/run/calls-callback-with-undefined-this.js @@ -0,0 +1,21 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.run +description: > + Calls the second parameter as a function, with its this value being + undefined. +info: | + AsyncContext.Variable.prototype.run ( value, func, ...args ) + + 10. Let result be Completion(Call(func, undefined, args)). +features: [AsyncContext] +---*/ + +const asyncVar = new AsyncContext.Variable(); + +asyncVar.run("foo", function () { + "use strict"; + assert.sameValue(this, undefined); +}); diff --git a/test/built-ins/AsyncContext/Variable/prototype/run/context-is-not-object.js b/test/built-ins/AsyncContext/Variable/prototype/run/context-is-not-object.js new file mode 100644 index 00000000000..0f8f611b82a --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/run/context-is-not-object.js @@ -0,0 +1,44 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.run +description: > + Throws a TypeError if `this` is not an Object. +info: | + AsyncContext.Variable.prototype.run ( value, func, ...args ) + + 1. Let asyncVariable be the this value. + 2. Perform ? RequireInternalSlot(asyncVariable, [[AsyncVariableName]]). + ... + + RequireInternalSlot(O, internalSlot) + + 1. If O is not an Object, throw a TypeError exception. + ... +features: [AsyncContext, Symbol] +---*/ + +assert.throws(TypeError, function () { + AsyncContext.Variable.prototype.run.call(1); +}); + +assert.throws(TypeError, function () { + AsyncContext.Variable.prototype.run.call(true); +}); + +assert.throws(TypeError, function () { + AsyncContext.Variable.prototype.run.call(''); +}); + +assert.throws(TypeError, function () { + AsyncContext.Variable.prototype.run.call(null); +}); + +assert.throws(TypeError, function () { + AsyncContext.Variable.prototype.run.call(undefined); +}); + +assert.throws(TypeError, function () { + AsyncContext.Variable.prototype.run.call(Symbol()); +}); diff --git a/test/built-ins/AsyncContext/Variable/prototype/run/context-is-not-variable-object.js b/test/built-ins/AsyncContext/Variable/prototype/run/context-is-not-variable-object.js new file mode 100644 index 00000000000..0c754365f47 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/run/context-is-not-variable-object.js @@ -0,0 +1,30 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.run +description: > + Throws a TypeError if `this` does not have an [[AsyncVariableName]] internal + slot. +info: | + AsyncContext.Variable.prototype.run ( value, func, ...args ) + + 1. Let asyncVariable be the this value. + 2. Perform ? RequireInternalSlot(asyncVariable, [[AsyncVariableName]]). + ... + + RequireInternalSlot(O, internalSlot) + + ... + 2. If O does not have an internalSlot internal slot, throw a TypeError exception. + ... +features: [AsyncContext] +---*/ + +assert.throws(TypeError, function () { + AsyncContext.Variable.prototype.run.call({}); +}); + +assert.throws(TypeError, function () { + AsyncContext.Variable.prototype.run.call([]); +}); diff --git a/test/built-ins/AsyncContext/Variable/prototype/run/context-is-snapshot-object-throws.js b/test/built-ins/AsyncContext/Variable/prototype/run/context-is-snapshot-object-throws.js new file mode 100644 index 00000000000..bac755835a9 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/run/context-is-snapshot-object-throws.js @@ -0,0 +1,25 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.run +description: > + Throws a TypeError if `this` is an AsyncContext.Snapshot object. +info: | + AsyncContext.Variable.prototype.run ( value, func, ...args ) + + 1. Let asyncVariable be the this value. + 2. Perform ? RequireInternalSlot(asyncVariable, [[AsyncVariableName]]). + ... + + RequireInternalSlot(O, internalSlot) + + ... + 2. If O does not have an internalSlot internal slot, throw a TypeError exception. + ... +features: [AsyncContext] +---*/ + +assert.throws(TypeError, function () { + AsyncContext.Variable.prototype.run.call(new AsyncContext.Snapshot()); +}); diff --git a/test/built-ins/AsyncContext/Variable/prototype/run/length.js b/test/built-ins/AsyncContext/Variable/prototype/run/length.js new file mode 100644 index 00000000000..f1bc7b00c32 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/run/length.js @@ -0,0 +1,24 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.run +description: > + AsyncContext.Variable.prototype.run.length value and descriptor. +info: | + AsyncContext.Variable.prototype.run ( value, func, ...args ) + + 17 ECMAScript Standard Built-in Objects + +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +assert.sameValue( + AsyncContext.Variable.prototype.run.length, 2, + 'The value of `AsyncContext.Variable.prototype.run.length` is `2`' +); + +verifyNotEnumerable(AsyncContext.Variable.prototype.run, 'length'); +verifyNotWritable(AsyncContext.Variable.prototype.run, 'length'); +verifyConfigurable(AsyncContext.Variable.prototype.run, 'length'); diff --git a/test/built-ins/AsyncContext/Variable/prototype/run/name.js b/test/built-ins/AsyncContext/Variable/prototype/run/name.js new file mode 100644 index 00000000000..1399342509b --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/run/name.js @@ -0,0 +1,24 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.run +description: > + AsyncContext.Variable.prototype.run.name value and descriptor. +info: | + AsyncContext.Variable.prototype.run ( value, func, ...args ) + + 17 ECMAScript Standard Built-in Objects + +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +assert.sameValue( + AsyncContext.Variable.prototype.run.name, 'run', + 'The value of `AsyncContext.Variable.prototype.run.name` is `"run"`' +); + +verifyNotEnumerable(AsyncContext.Variable.prototype.run, 'name'); +verifyNotWritable(AsyncContext.Variable.prototype.run, 'name'); +verifyConfigurable(AsyncContext.Variable.prototype.run, 'name'); diff --git a/test/built-ins/AsyncContext/Variable/prototype/run/not-a-constructor.js b/test/built-ins/AsyncContext/Variable/prototype/run/not-a-constructor.js new file mode 100644 index 00000000000..e9c7d9e8ec0 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/run/not-a-constructor.js @@ -0,0 +1,30 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-ecmascript-standard-built-in-objects +description: > + AsyncContext.Variable.prototype.run does not implement [[Construct]], is not + new-able +info: | + ECMAScript Function Objects + + Built-in function objects that are not identified as constructors do not + implement the [[Construct]] internal method unless otherwise specified in + the description of a particular function. + + sec-evaluatenew + + ... + 7. If IsConstructor(constructor) is false, throw a TypeError exception. + ... +includes: [isConstructor.js] +features: [Reflect.construct, AsyncContext, arrow-function] +---*/ + +assert.sameValue(isConstructor(AsyncContext.Variable.prototype.run), false, 'isConstructor(AsyncContext.Variable.prototype.run) must return false'); + +assert.throws(TypeError, () => { + let v = new AsyncContext.Variable(); new v.run(0, () => { }); +}, '`let v = new AsyncContext.Variable(); new v.run(0, () => {});` throws TypeError'); + diff --git a/test/built-ins/AsyncContext/Variable/prototype/run/restores-context-snapshot-after-callback-returns-promise.js b/test/built-ins/AsyncContext/Variable/prototype/run/restores-context-snapshot-after-callback-returns-promise.js new file mode 100644 index 00000000000..09b3463e494 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/run/restores-context-snapshot-after-callback-returns-promise.js @@ -0,0 +1,51 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.run +description: > + Switches back into the previous context snapshot after calling the callback. + This happens synchronously, regardless of whether the callback returns a + promise. +info: | + AsyncContext.Variable.prototype.run ( value, func, ...args ) + + 3. Let previousContextMapping be AsyncContextSnapshot(). + ... + 10. Let result be Completion(Call(func, undefined, args)). + 11. AsyncContextSwap(previousContextMapping). + 12. Return result. + + AsyncContextSwap ( snapshotMapping ) + + 1. Let agentRecord be the surrounding agent's Agent Record. + ... + 3. Set agentRecord.[[AsyncContextMapping]] to snapshotMapping. + + AsyncContextSnapshot ( ) + + 1. Let agentRecord be the surrounding agent's Agent Record. + 2. Return agentRecord.[[AsyncContextMapping]]. +features: [AsyncContext] +---*/ + +const asyncVar = new AsyncContext.Variable(); + +asyncVar.run("foo", () => { + asyncVar.run("42", () => { + // Never resolves. + return new Promise(() => { }); + }); + + assert.sameValue( + asyncVar.get(), + "foo", + 'The value of `asyncVar.get()` inside the first `run` and after the second one should be `"foo"`' + ); +}); + +assert.sameValue( + asyncVar.get(), + undefined, + 'The value of `asyncVar.get()` after the first `run` should be `undefined`' +); diff --git a/test/built-ins/AsyncContext/Variable/prototype/run/restores-context-snapshot-after-callback-takes-snapshot.js b/test/built-ins/AsyncContext/Variable/prototype/run/restores-context-snapshot-after-callback-takes-snapshot.js new file mode 100644 index 00000000000..d73c487e5f6 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/run/restores-context-snapshot-after-callback-takes-snapshot.js @@ -0,0 +1,51 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.run +description: > + Switches back into the previous context snapshot after calling the callback, + even when an AsyncContext.Snapshot() is created inside the callback. +info: | + AsyncContext.Variable.prototype.run ( value, func, ...args ) + + 3. Let previousContextMapping be AsyncContextSnapshot(). + ... + 10. Let result be Completion(Call(func, undefined, args)). + 11. AsyncContextSwap(previousContextMapping). + 12. Return result. + + AsyncContextSwap ( snapshotMapping ) + + 1. Let agentRecord be the surrounding agent's Agent Record. + ... + 3. Set agentRecord.[[AsyncContextMapping]] to snapshotMapping. + + AsyncContextSnapshot ( ) + + 1. Let agentRecord be the surrounding agent's Agent Record. + 2. Return agentRecord.[[AsyncContextMapping]]. +features: [AsyncContext] +---*/ + +const asyncVar = new AsyncContext.Variable(); + +let snapshot; + +asyncVar.run("foo", () => { + asyncVar.run("42", () => { + snapshot = new AsyncContext.Snapshot(); + }); + + assert.sameValue( + asyncVar.get(), + "foo", + 'The value of `asyncVar.get()` inside the first `run` and after the second one should be `"foo"`' + ); +}); + +assert.sameValue( + asyncVar.get(), + undefined, + 'The value of `asyncVar.get()` after the first `run` should be `undefined`' +); diff --git a/test/built-ins/AsyncContext/Variable/prototype/run/restores-context-snapshot-after-callback-throw.js b/test/built-ins/AsyncContext/Variable/prototype/run/restores-context-snapshot-after-callback-throw.js new file mode 100644 index 00000000000..1cd0e7bc6c5 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/run/restores-context-snapshot-after-callback-throw.js @@ -0,0 +1,53 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.run +description: > + Switches back into the previous context snapshot after calling the callback, + even if it throws an exception. +info: | + AsyncContext.Variable.prototype.run ( value, func, ...args ) + + 3. Let previousContextMapping be AsyncContextSnapshot(). + ... + 10. Let result be Completion(Call(func, undefined, args)). + 11. AsyncContextSwap(previousContextMapping). + 12. Return result. + + AsyncContextSwap ( snapshotMapping ) + + 1. Let agentRecord be the surrounding agent's Agent Record. + ... + 3. Set agentRecord.[[AsyncContextMapping]] to snapshotMapping. + + AsyncContextSnapshot ( ) + + 1. Let agentRecord be the surrounding agent's Agent Record. + 2. Return agentRecord.[[AsyncContextMapping]]. +features: [AsyncContext] +---*/ + +function CustomError() { } + +const asyncVar = new AsyncContext.Variable(); + +asyncVar.run("foo", () => { + + try { + asyncVar.run("bar", () => { + throw new CustomError(); + }) + } catch (e) { + if (!(e instanceof CustomError)) { + throw e; + } + } + + assert.sameValue( + asyncVar.get(), + "foo", + 'The value of `asyncVar.get()` inside the first `run` and after the second one should be `"foo"`' + ); + +}); diff --git a/test/built-ins/AsyncContext/Variable/prototype/run/restores-context-snapshot-after-callback.js b/test/built-ins/AsyncContext/Variable/prototype/run/restores-context-snapshot-after-callback.js new file mode 100644 index 00000000000..00b692905e3 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/run/restores-context-snapshot-after-callback.js @@ -0,0 +1,46 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.run +description: > + Switches back into the previous context snapshot after calling the callback. +info: | + AsyncContext.Variable.prototype.run ( value, func, ...args ) + + 3. Let previousContextMapping be AsyncContextSnapshot(). + ... + 10. Let result be Completion(Call(func, undefined, args)). + 11. AsyncContextSwap(previousContextMapping). + 12. Return result. + + AsyncContextSwap ( snapshotMapping ) + + 1. Let agentRecord be the surrounding agent's Agent Record. + ... + 3. Set agentRecord.[[AsyncContextMapping]] to snapshotMapping. + + AsyncContextSnapshot ( ) + + 1. Let agentRecord be the surrounding agent's Agent Record. + 2. Return agentRecord.[[AsyncContextMapping]]. +features: [AsyncContext] +---*/ + +const asyncVar = new AsyncContext.Variable(); + +asyncVar.run("foo", () => { + asyncVar.run("42", () => { }); + + assert.sameValue( + asyncVar.get(), + "foo", + 'The value of `asyncVar.get()` inside the first `run` and after the second one should be `"foo"`' + ); +}); + +assert.sameValue( + asyncVar.get(), + undefined, + 'The value of `asyncVar.get()` after the first `run` should be `undefined`' +); diff --git a/test/built-ins/AsyncContext/Variable/prototype/run/returned-promise-does-not-resolve-in-snapshot.js b/test/built-ins/AsyncContext/Variable/prototype/run/returned-promise-does-not-resolve-in-snapshot.js new file mode 100644 index 00000000000..60912444bbe --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/run/returned-promise-does-not-resolve-in-snapshot.js @@ -0,0 +1,35 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.run +description: > + When it returns a promise, the async context snapshot when the promise + resolves will not be the one inside of the callback. +info: | + TODO +flags: [async] +features: [AsyncContext] +---*/ + +const asyncVar = new AsyncContext.Variable(); + +let resolve; + +asyncVar.run("foo", () => { + + asyncVar.run("bar", async () => { + assert.sameValue(asyncVar.get(), "bar"); + + await new Promise(resolveFn => { + resolve = resolveFn; + }); + + assert.sameValue(asyncVar.get(), "bar"); + }).then(() => { + assert.sameValue(asyncVar.get(), "foo"); + }).then($DONE, $DONE); + +}); + +resolve(); diff --git a/test/built-ins/AsyncContext/Variable/prototype/run/returns-async-callback-return-value.js b/test/built-ins/AsyncContext/Variable/prototype/run/returns-async-callback-return-value.js new file mode 100644 index 00000000000..cb189f86dae --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/run/returns-async-callback-return-value.js @@ -0,0 +1,32 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.run +description: > + If the second parameter is an async function, it calls it and returns its + result value, which is a promise. +info: | + AsyncContext.Variable.prototype.run ( value, func, ...args ) + + 10. Let result be Completion(Call(func, undefined, args)). + ... + 12. Return result. +flags: [async] +includes: [asyncHelpers.js] +features: [AsyncContext] +---*/ + +const asyncVar = new AsyncContext.Variable(); + +const obj = {}; + +asyncTest(async function () { + const ret = asyncVar.run("foo", async () => obj); + assert( + ret instanceof Promise, + 'The return value of `asyncVar.run("foo", async () => obj) is a promise' + ); + + assert.sameValue(await ret, obj, '`ret` resolves to `obj`'); +}); diff --git a/test/built-ins/AsyncContext/Variable/prototype/run/returns-callback-return-value.js b/test/built-ins/AsyncContext/Variable/prototype/run/returns-callback-return-value.js new file mode 100644 index 00000000000..b90c04040b3 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/run/returns-callback-return-value.js @@ -0,0 +1,31 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.run +description: > + Calls the second parameter as a function, and returns its return value. +info: | + AsyncContext.Variable.prototype.run ( value, func, ...args ) + + 10. Let result be Completion(Call(func, undefined, args)). + ... + 12. Return result. +features: [AsyncContext] +---*/ + +const asyncVar = new AsyncContext.Variable(); + +const obj = {}; + +assert.sameValue( + asyncVar.run("foo", () => obj), + obj, + 'The return value of `asyncVar.run("foo", () => obj)` is `obj`' +); + +const returnedPromise = asyncVar.run("foo", async () => obj); +assert( + returnedPromise instanceof Promise, + 'The return value of `asyncVar.run("foo", async () => obj) is a promise' +); diff --git a/test/built-ins/AsyncContext/Variable/prototype/run/run.js b/test/built-ins/AsyncContext/Variable/prototype/run/run.js new file mode 100644 index 00000000000..19ba3d9660f --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/run/run.js @@ -0,0 +1,23 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.run +description: > + AsyncContext.Variable.prototype.run ( value, func, ...args ) + + 17 ECMAScript Standard Built-in Objects + +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +assert.sameValue( + typeof AsyncContext.Variable.prototype.run, + 'function', + 'typeof AsyncContext.Variable.prototype.run is "function"' +); + +verifyNotEnumerable(AsyncContext.Variable.prototype, 'run'); +verifyWritable(AsyncContext.Variable.prototype, 'run'); +verifyConfigurable(AsyncContext.Variable.prototype, 'run'); diff --git a/test/built-ins/AsyncContext/Variable/prototype/run/swaps-context-snapshot-before-callback.js b/test/built-ins/AsyncContext/Variable/prototype/run/swaps-context-snapshot-before-callback.js new file mode 100644 index 00000000000..43eeb7b1804 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/run/swaps-context-snapshot-before-callback.js @@ -0,0 +1,42 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.run +description: > + Switches into a context snapshot containing the given value when calling the + callback. +info: | + AsyncContext.Variable.prototype.run ( value, func, ...args ) + + 4. Let asyncContextMapping be a new empty List. + ... + 8. Append p to asyncContextMapping. + 9. AsyncContextSwap(asyncContextMapping.) + 10. Let result be Completion(Call(func, undefined, args)). + ... + + AsyncContextSwap ( snapshotMapping ) + + 1. Let agentRecord be the surrounding agent's Agent Record. + ... + 3. Set agentRecord.[[AsyncContextMapping]] to snapshotMapping. + +features: [AsyncContext] +---*/ + +const asyncVar = new AsyncContext.Variable(); + +assert.sameValue( + asyncVar.get(), + undefined, + 'The value of `asyncVar.get()` before calling `run` is `undefined`' +); + +asyncVar.run(42, () => { + assert.sameValue( + asyncVar.get(), + 42, + 'The value of `asyncVar.get()` inside the callback passed to `run` is `42`' + ); +}); diff --git a/test/built-ins/AsyncContext/Variable/prototype/run/throws-if-callback-is-not-callable.js b/test/built-ins/AsyncContext/Variable/prototype/run/throws-if-callback-is-not-callable.js new file mode 100644 index 00000000000..35ebdbacc18 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/run/throws-if-callback-is-not-callable.js @@ -0,0 +1,53 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.run +description: > + Throws if the second parameter is not callable. +info: | + AsyncContext.Variable.prototype.run ( value, func, ...args ) + + 10. Let result be Completion(Call(func, undefined, args)). + ... + 12. Return result. + + Call ( F, V [ , argumentsList ] ) + + 2. If IsCallable(F) is false, throw a TypeError exception. +features: [AsyncContext] +---*/ + +const asyncVar = new AsyncContext.Variable(); + +assert.throws(TypeError, () => { + asyncVar.run("foo"); +}); + +assert.throws(TypeError, () => { + asyncVar.run("foo", 1); +}); + +assert.throws(TypeError, () => { + asyncVar.run("foo", true); +}); + +assert.throws(TypeError, () => { + asyncVar.run("foo", ''); +}); + +assert.throws(TypeError, () => { + asyncVar.run("foo", null); +}); + +assert.throws(TypeError, () => { + asyncVar.run("foo", undefined); +}); + +assert.throws(TypeError, () => { + asyncVar.run("foo", Symbol()); +}); + +assert.throws(TypeError, () => { + asyncVar.run("foo", {}); +}); diff --git a/test/built-ins/AsyncContext/Variable/prototype/run/throws-if-callback-throws.js b/test/built-ins/AsyncContext/Variable/prototype/run/throws-if-callback-throws.js new file mode 100644 index 00000000000..fcc3923b68b --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/run/throws-if-callback-throws.js @@ -0,0 +1,25 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.run +description: > + Throws if calling the second parameter throws. +info: | + AsyncContext.Variable.prototype.run ( value, func, ...args ) + + 10. Let result be Completion(Call(func, undefined, args)). + ... + 12. Return result. +features: [AsyncContext] +---*/ + +function CustomError() { } + +const asyncVar = new AsyncContext.Variable(); + +assert.throws(CustomError, () => { + asyncVar.run("foo", () => { + throw new CustomError(); + }); +}); diff --git a/test/built-ins/AsyncContext/Variable/prototype/run/unrelated-snapshot-entries-keep-their-value.js b/test/built-ins/AsyncContext/Variable/prototype/run/unrelated-snapshot-entries-keep-their-value.js new file mode 100644 index 00000000000..11b94605506 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/prototype/run/unrelated-snapshot-entries-keep-their-value.js @@ -0,0 +1,72 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable.prototype.run +description: > + Switches into a context snapshot which keeps the values of all entries not + corresponding to the current AsyncContext.Variable from the previous context + snapshot. +info: | + AsyncContext.Variable.prototype.run ( value, func, ...args ) + + 3. Let previousContextMapping be AsyncContextSnapshot(). + 4. Let asyncContextMapping be a new empty List. + 5. For each Async Context Mapping Record p of previousContextMapping, do + a. If SameValueZero(p.[[AsyncContextKey]], asyncVariable) is false, then + i. Let q be the Async Context Mapping Record + { [[AsyncContextKey]]: p.[[AsyncContextKey]], + [[AsyncContextValue]]: p.[[AsyncContextValue]] } + ii. Append q to asyncContextMapping + 6. Assert: asyncContextMapping does not contain an Async Context Mapping + Record whose [[AsyncContextKey]] is asyncVariable. + 7. Let p be the Async Context Mapping Record + { [[AsyncContextKey]]: asyncVariable, [[AsyncContextValue]]: value }. + 8. Append p to asyncContextMapping. + 9. AsyncContextSwap(asyncContextMapping) + ... + + AsyncContextSwap ( snapshotMapping ) + + 1. Let agentRecord be the surrounding agent's Agent Record. + ... + 3. Set agentRecord.[[AsyncContextMapping]] to snapshotMapping. + + AsyncContextSnapshot ( ) + + 1. Let agentRecord be the surrounding agent's Agent Record. + 2. Return agentRecord.[[AsyncContextMapping]]. +features: [AsyncContext] +---*/ + +const asyncVar1 = new AsyncContext.Variable(); +const asyncVar2 = new AsyncContext.Variable(); +const asyncVar3 = new AsyncContext.Variable(); + +const symbol1 = Symbol("symbol1"); +const symbol2 = Symbol("symbol2"); +const symbol3 = Symbol("symbol3"); +const symbol4 = Symbol("symbol4"); +const symbol5 = Symbol("symbol5"); + +asyncVar1.run(symbol1, () => { + asyncVar2.run(symbol2, () => { + asyncVar3.run(symbol3, () => { + + assert.sameValue(asyncVar1.get(), symbol1, 'asyncVar1.get() === symbol1'); + assert.sameValue(asyncVar2.get(), symbol2, 'asyncVar2.get() === symbol2'); + assert.sameValue(asyncVar3.get(), symbol3, 'asyncVar3.get() === symbol3'); + + asyncVar2.run(symbol4, () => { + asyncVar1.run(symbol5, () => { + + assert.sameValue(asyncVar1.get(), symbol5, 'asyncVar1.get() === symbol5'); + assert.sameValue(asyncVar2.get(), symbol4, 'asyncVar2.get() === symbol4'); + assert.sameValue(asyncVar3.get(), symbol3, 'asyncVar3.get() === symbol3'); + + }); + }); + + }); + }); +}); diff --git a/test/built-ins/AsyncContext/Variable/returns-new-object-from-constructor.js b/test/built-ins/AsyncContext/Variable/returns-new-object-from-constructor.js new file mode 100644 index 00000000000..bf730de8a39 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/returns-new-object-from-constructor.js @@ -0,0 +1,39 @@ +// Copyright (C) 2024 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable +description: > + Returns a new ordinary object from the FinalizationRegistry constructor +info: | + AsyncContext.Variable ( options ) + + ... + 5. Let asyncVariable be ? OrdinaryCreateFromConstructor(NewTarget, "%AsyncContext.Variable.prototype%", « [[AsyncVariableName]], [[AsyncVariableDefaultValue]] »). + ... + 8. Return asyncVariable. + + OrdinaryCreateFromConstructor ( constructor, intrinsicDefaultProto [ , internalSlotsList ] ) + + ... + 2. Let proto be ? GetPrototypeFromConstructor(constructor, intrinsicDefaultProto). + 3. Return ObjectCreate(proto, internalSlotsList). +features: [AsyncContext, for-of] +---*/ + +var optionsBag = {}; +var asyncVariable = new AsyncContext.Variable(optionsBag); + +assert.sameValue(Object.getPrototypeOf(asyncVariable), AsyncContext.Variable.prototype); +assert.notSameValue(asyncVariable, optionsBag, 'does not return the same object'); +assert.sameValue(asyncVariable instanceof AsyncContext.Variable, true, 'instanceof'); + +for (let key of Object.getOwnPropertyNames(asyncVariable)) { + assert(false, `should not set any own named properties: ${key}`); +} + +for (let key of Object.getOwnPropertySymbols(asyncVariable)) { + assert(false, `should not set any own symbol properties: ${String(key)}`); +} + +assert.sameValue(Object.getPrototypeOf(asyncVariable), AsyncContext.Variable.prototype); diff --git a/test/built-ins/AsyncContext/Variable/undefined-newtarget-throws.js b/test/built-ins/AsyncContext/Variable/undefined-newtarget-throws.js new file mode 100644 index 00000000000..d18f44a0866 --- /dev/null +++ b/test/built-ins/AsyncContext/Variable/undefined-newtarget-throws.js @@ -0,0 +1,21 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-variable +description: Throws a TypeError if NewTarget is undefined. +features: [AsyncContext] +---*/ + +assert.sameValue( + typeof AsyncContext.Variable, 'function', + 'typeof AsyncContext.Variable is function' +); + +assert.throws(TypeError, function() { + AsyncContext.Variable(); +}); + +assert.throws(TypeError, function() { + AsyncContext.Variable({}); +}); diff --git a/test/built-ins/AsyncContext/context-propagation/CreateIteratorFromClosure.js b/test/built-ins/AsyncContext/context-propagation/CreateIteratorFromClosure.js new file mode 100644 index 00000000000..58128989d97 --- /dev/null +++ b/test/built-ins/AsyncContext/context-propagation/CreateIteratorFromClosure.js @@ -0,0 +1,22 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: TODO +features: [AsyncContext] +---*/ + +const asyncVar = new AsyncContext.Variable(); + +const array = []; +Object.defineProperty(array, 0, { + get() { + assert.sameValue(asyncVar.get(), "bar"); + } +}); + +const iter = asyncVar.run("foo", () => array.values()); + +asyncVar.run("bar", () => { + iter.next(); +}); diff --git a/test/built-ins/AsyncContext/context-propagation/FinalizationRegistry.js b/test/built-ins/AsyncContext/context-propagation/FinalizationRegistry.js new file mode 100644 index 00000000000..7a13f720a09 --- /dev/null +++ b/test/built-ins/AsyncContext/context-propagation/FinalizationRegistry.js @@ -0,0 +1,35 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-finalization-registry-objects +description: > + The FinalizationRegistry cleanup callback uses HostMakeJobCallback and + HostCallJobCallback, which propagate an async context snapshot. +info: | + TODO +flags: [async, non-deterministic] +features: [AsyncContext, FinalizationRegistry, host-gc-required] +---*/ + +const asyncVar = new AsyncContext.Variable(); + +function cleanupCallback() { + try { + assert.sameValue(asyncVar.get(), "foo"); + $DONE(); + } catch (err) { + $DONE(err); + } +} + +const registry = asyncVar.run("foo", () => { + return new FinalizationRegistry(cleanupCallback); +}); + +asyncVar.run("bar", () => { + let target = {}; + registry.register(target); +}); + +asyncVar.run("baz", () => $262.gc()); diff --git a/test/built-ins/AsyncContext/context-propagation/async-await.js b/test/built-ins/AsyncContext/context-propagation/async-await.js new file mode 100644 index 00000000000..19fecbc5f69 --- /dev/null +++ b/test/built-ins/AsyncContext/context-propagation/async-await.js @@ -0,0 +1,35 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: TODO +flags: [async] +features: [AsyncContext] +includes: [asyncHelpers.js] +---*/ + +const context = new AsyncContext.Variable(); + +let resolveFn; + +async function asyncCb() { + assert.sameValue(context.get(), 42); + + await new Promise(resolve => { + resolveFn = resolve; + }); + + assert.sameValue(context.get(), 42); +} + +asyncTest(() => { + assert.sameValue(context.get(), undefined); + + const promise = context.run(42, asyncCb); + + return promise.then(() => { + assert.sameValue(context.get(), undefined); + }); +}); + +resolveFn(); diff --git a/test/built-ins/AsyncContext/context-propagation/async-generators-yield-inside-run.js b/test/built-ins/AsyncContext/context-propagation/async-generators-yield-inside-run.js new file mode 100644 index 00000000000..68a030b780b --- /dev/null +++ b/test/built-ins/AsyncContext/context-propagation/async-generators-yield-inside-run.js @@ -0,0 +1,40 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: TODO +flags: [async] +features: [AsyncContext] +includes: [asyncHelpers.js] +---*/ + +const asyncVar = new AsyncContext.Variable(); + +async function* gen() { + await Promise.resolve(); + assert.sameValue(asyncVar.get(), "init"); + yield* asyncVar.run("nested-gen", async function* () { + assert.sameValue(asyncVar.get(), "nested-gen"); + await Promise.resolve(); + assert.sameValue(asyncVar.get(), "nested-gen"); + yield; + assert.sameValue(asyncVar.get(), "nested-gen"); + await Promise.resolve(); + assert.sameValue(asyncVar.get(), "nested-gen"); + }); + await Promise.resolve(); + assert.sameValue(asyncVar.get(), "init"); +} + +asyncTest(async () => { + let g; + asyncVar.run('init', () => { + g = gen(); + }); + await asyncVar.run('first iter', async () => { + await g.next(); + }); + await asyncVar.run('second iter', async () => { + await g.next(); + }); +}); diff --git a/test/built-ins/AsyncContext/context-propagation/async-generators.js b/test/built-ins/AsyncContext/context-propagation/async-generators.js new file mode 100644 index 00000000000..8f1d40e623b --- /dev/null +++ b/test/built-ins/AsyncContext/context-propagation/async-generators.js @@ -0,0 +1,34 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: TODO +flags: [async] +features: [AsyncContext] +includes: [asyncHelpers.js] +---*/ + +const asyncVar = new AsyncContext.Variable(); + +async function* gen() { + assert.sameValue(asyncVar.get(), "init"); + await Promise.resolve(); + assert.sameValue(asyncVar.get(), "init"); + yield; + assert.sameValue(asyncVar.get(), "init"); + await Promise.resolve(); + assert.sameValue(asyncVar.get(), "init"); +} + +asyncTest(async () => { + let g; + asyncVar.run('init', () => { + g = gen(); + }); + await asyncVar.run('first iter', async () => { + await g.next(); + }); + await asyncVar.run('second iter', async () => { + await g.next(); + }); +}); diff --git a/test/built-ins/AsyncContext/context-propagation/continuation-propagation-thenable.js b/test/built-ins/AsyncContext/context-propagation/continuation-propagation-thenable.js new file mode 100644 index 00000000000..f681e6632af --- /dev/null +++ b/test/built-ins/AsyncContext/context-propagation/continuation-propagation-thenable.js @@ -0,0 +1,23 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: TODO +flags: [async] +features: [AsyncContext] +---*/ + +const asyncVar = new AsyncContext.Variable(); + +const expectedSymbol = Symbol("expected"); + +const thenable = { + then(cb) { + assert.sameValue(asyncVar.get(), expectedSymbol); + cb(); + } +}; + +asyncVar.run(expectedSymbol, () => { + Promise.resolve(thenable).then(() => $DONE()); +}); diff --git a/test/built-ins/AsyncContext/context-propagation/cross-realm.js b/test/built-ins/AsyncContext/context-propagation/cross-realm.js new file mode 100644 index 00000000000..3abb1c06426 --- /dev/null +++ b/test/built-ins/AsyncContext/context-propagation/cross-realm.js @@ -0,0 +1,49 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-agents +description: > + The context snapshot is stored in a field of the agent record, so it is + preserved across realms. +info: | + TODO +features: [AsyncContext, cross-realm] +---*/ + +const realm = $262.createRealm().global; + +realm.eval(` + var asyncSnapshot; + + function wrap(cb) { + asyncSnapshot.run(cb); + } +`); + +const asyncVar1 = new AsyncContext.Variable(); +const asyncVar2 = new AsyncContext.Variable(); +const asyncVar3 = new AsyncContext.Variable(); + +asyncVar1.run(42, () => { + + realm.eval(` + asyncSnapshot = new AsyncContext.Snapshot(); + `); + + asyncVar2.run("foo", () => { + asyncVar3.run("bar", () => { + asyncVar1.run("baz", () => { + + realm.wrap(() => { + + assert.sameValue(asyncVar1.get(), 42); + assert.sameValue(asyncVar2.get(), undefined); + assert.sameValue(asyncVar3.get(), undefined); + + }); + + }); + }); + }); +}); diff --git a/test/built-ins/AsyncContext/context-propagation/generators-yield-inside-run.js b/test/built-ins/AsyncContext/context-propagation/generators-yield-inside-run.js new file mode 100644 index 00000000000..5551bc1454d --- /dev/null +++ b/test/built-ins/AsyncContext/context-propagation/generators-yield-inside-run.js @@ -0,0 +1,30 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: TODO +features: [AsyncContext] +---*/ + +const asyncVar = new AsyncContext.Variable(); + +function* gen() { + assert.sameValue(asyncVar.get(), "init"); + yield* asyncVar.run("nested-gen", function* () { + assert.sameValue(asyncVar.get(), "nested-gen"); + yield; + assert.sameValue(asyncVar.get(), "nested-gen"); + }); + assert.sameValue(asyncVar.get(), "init"); +} + +let g; +asyncVar.run('init', () => { + g = gen(); +}); +asyncVar.run('first iter', () => { + g.next(); +}); +asyncVar.run('second iter', () => { + g.next(); +}); diff --git a/test/built-ins/AsyncContext/context-propagation/generators.js b/test/built-ins/AsyncContext/context-propagation/generators.js new file mode 100644 index 00000000000..3e5fa9d90b6 --- /dev/null +++ b/test/built-ins/AsyncContext/context-propagation/generators.js @@ -0,0 +1,26 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: TODO +features: [AsyncContext] +---*/ + +const asyncVar = new AsyncContext.Variable(); + +function* gen() { + assert.sameValue(asyncVar.get(), "init"); + yield; + assert.sameValue(asyncVar.get(), "init"); +} + +let g; +asyncVar.run('init', () => { + g = gen(); +}); +asyncVar.run('first iter', () => { + g.next(); +}); +asyncVar.run('second iter', () => { + g.next(); +}); diff --git a/test/built-ins/AsyncContext/context-propagation/promise-then.js b/test/built-ins/AsyncContext/context-propagation/promise-then.js new file mode 100644 index 00000000000..d850253b148 --- /dev/null +++ b/test/built-ins/AsyncContext/context-propagation/promise-then.js @@ -0,0 +1,29 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: TODO +features: [AsyncContext] +flags: [async] +---*/ + +const asyncVar = new AsyncContext.Variable(); + +function thenCallback() { + assert.sameValue(asyncVar.get(), "then registration time"); +} + +let resolveFn; +const promise = asyncVar.run("promise creation time", () => { + return new Promise(resolve => { + resolveFn = resolve; + }); +}); + +asyncVar.run("then registration time", () => { + return promise.then(thenCallback); +}).then($DONE, $DONE); + +asyncVar.run("resolution time", () => { + resolveFn(); +}); diff --git a/test/built-ins/AsyncContext/proc-desc.js b/test/built-ins/AsyncContext/proc-desc.js new file mode 100644 index 00000000000..1b98ed47f35 --- /dev/null +++ b/test/built-ins/AsyncContext/proc-desc.js @@ -0,0 +1,40 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-object +description: > + Property descriptor of AsyncContext +info: | + The AsyncContext Object + + The AsyncContext object does not have a [[Construct]] internal method; it + cannot be used as a constructor with the new operator. + + The AsyncContext object does not have a [[Call]] internal method; it cannot + be invoked as a function. + + 17 ECMAScript Standard Built-in Objects: + + Every other data property described in clauses 18 through 26 and in Annex + B.2 has the attributes { [[Writable]]: true, [[Enumerable]]: false, + [[Configurable]]: true } unless otherwise specified. +includes: [propertyHelper.js] +features: [AsyncContext] +---*/ + +assert.sameValue(typeof AsyncContext, "object"); + +assert.throws(TypeError, function() { + AsyncContext(); +}, "no [[Call]]"); + +assert.throws(TypeError, function() { + new AsyncContext(); +}, "no [[Construct]]"); + +verifyProperty(this, "AsyncContext", { + enumerable: false, + writable: true, + configurable: true +}); diff --git a/test/built-ins/AsyncContext/proto.js b/test/built-ins/AsyncContext/proto.js new file mode 100644 index 00000000000..134c25a8596 --- /dev/null +++ b/test/built-ins/AsyncContext/proto.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Igalia, S. L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asynccontext-object +description: > + The prototype of AsyncContext is Object.prototype +info: | + The AsyncContext Object has a [[Prototype]] internal slot whose value is + %Object.prototype%. +features: [AsyncContext] +---*/ + +const proto = Object.getPrototypeOf(AsyncContext); + +assert.sameValue(proto, Object.prototype);