diff --git a/test/language/expressions/assignment/destructuring/default-expr-throws-iterator-return-get-throws.js b/test/language/expressions/assignment/destructuring/default-expr-throws-iterator-return-get-throws.js new file mode 100644 index 0000000000..01104ad0f7 --- /dev/null +++ b/test/language/expressions/assignment/destructuring/default-expr-throws-iterator-return-get-throws.js @@ -0,0 +1,57 @@ +// Copyright (C) 2025 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-destructuringassignmentevaluation +description: > + Input throw-completion forwarded when IteratorClose returns abruptly because GetV in GetMethod throws. +info: | + 13.15.5.2 Runtime Semantics: DestructuringAssignmentEvaluation + + ArrayAssignmentPattern : [ AssignmentElementList , Elisionopt AssignmentRestElementopt ] + ... + 2. Let status be Completion(IteratorDestructuringAssignmentEvaluation of AssignmentElementList with argument iteratorRecord). + 3. If status is an abrupt completion, then + a. If iteratorRecord.[[Done]] is false, return ? IteratorClose(iteratorRecord, status). + b. Return ? status. + ... + + 7.4.11 IteratorClose ( iteratorRecord, completion ) + ... + 3. Let innerResult be Completion(GetMethod(iterator, "return")). + ... + 5. If completion is a throw completion, return ? completion. + ... + + 7.3.10 GetMethod ( V, P ) + 1. Let func be ? GetV(V, P). + ... +---*/ + +function MyError() {} + +function thrower() { + throw new MyError(); +} + +var returnGetterCalled = 0; + +var iterator = { + [Symbol.iterator]() { + return this; + }, + next() { + return {done: false}; + }, + get return() { + returnGetterCalled += 1; + throw "bad"; + } +}; + +assert.throws(MyError, function() { + var a; + ([a = thrower()] = iterator); +}); + +assert.sameValue(returnGetterCalled, 1); diff --git a/test/language/expressions/assignment/destructuring/default-expr-throws-iterator-return-is-not-callable.js b/test/language/expressions/assignment/destructuring/default-expr-throws-iterator-return-is-not-callable.js new file mode 100644 index 0000000000..1830be8722 --- /dev/null +++ b/test/language/expressions/assignment/destructuring/default-expr-throws-iterator-return-is-not-callable.js @@ -0,0 +1,54 @@ +// Copyright (C) 2025 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-destructuringassignmentevaluation +description: > + Input throw-completion forwarded when IteratorClose returns abruptly because GetMethod throws. +info: | + 13.15.5.2 Runtime Semantics: DestructuringAssignmentEvaluation + + ArrayAssignmentPattern : [ AssignmentElementList , Elisionopt AssignmentRestElementopt ] + ... + 2. Let status be Completion(IteratorDestructuringAssignmentEvaluation of AssignmentElementList with argument iteratorRecord). + 3. If status is an abrupt completion, then + a. If iteratorRecord.[[Done]] is false, return ? IteratorClose(iteratorRecord, status). + b. Return ? status. + ... + + 7.4.11 IteratorClose ( iteratorRecord, completion ) + ... + 3. Let innerResult be Completion(GetMethod(iterator, "return")). + ... + 5. If completion is a throw completion, return ? completion. + ... + + 7.3.10 GetMethod ( V, P ) + 1. Let func be ? GetV(V, P). + 2. If func is either undefined or null, return undefined. + 3. If IsCallable(func) is false, throw a TypeError exception. + ... +---*/ + +function MyError() {} + +function thrower() { + throw new MyError(); +} + +for (var returnMethod of [0, 0n, true, "string", {}, Symbol()]) { + var iterable = { + [Symbol.iterator]() { + return this; + }, + next() { + return {done: false}; + }, + return: returnMethod, + }; + + assert.throws(MyError, function() { + var a; + ([a = thrower()] = iterable); + }); +} diff --git a/test/language/expressions/assignment/destructuring/target-assign-throws-iterator-return-get-throws.js b/test/language/expressions/assignment/destructuring/target-assign-throws-iterator-return-get-throws.js new file mode 100644 index 0000000000..ebb779a0fa --- /dev/null +++ b/test/language/expressions/assignment/destructuring/target-assign-throws-iterator-return-get-throws.js @@ -0,0 +1,58 @@ +// Copyright (C) 2025 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-destructuringassignmentevaluation +description: > + Input throw-completion forwarded when IteratorClose returns abruptly because GetV in GetMethod throws. +info: | + 13.15.5.2 Runtime Semantics: DestructuringAssignmentEvaluation + + ArrayAssignmentPattern : [ AssignmentElementList , Elisionopt AssignmentRestElementopt ] + ... + 2. Let status be Completion(IteratorDestructuringAssignmentEvaluation of AssignmentElementList with argument iteratorRecord). + 3. If status is an abrupt completion, then + a. If iteratorRecord.[[Done]] is false, return ? IteratorClose(iteratorRecord, status). + b. Return ? status. + ... + + 7.4.11 IteratorClose ( iteratorRecord, completion ) + ... + 3. Let innerResult be Completion(GetMethod(iterator, "return")). + ... + 5. If completion is a throw completion, return ? completion. + ... + + 7.3.10 GetMethod ( V, P ) + 1. Let func be ? GetV(V, P). + ... +---*/ + +function MyError() {} + +var target = { + set a(v) { + throw new MyError(); + } +}; + +var returnGetterCalled = 0; + +var iterator = { + [Symbol.iterator]() { + return this; + }, + next() { + return {done: false}; + }, + get return() { + returnGetterCalled += 1; + throw "bad"; + } +}; + +assert.throws(MyError, function() { + ([target.a] = iterator); +}); + +assert.sameValue(returnGetterCalled, 1); diff --git a/test/language/expressions/assignment/destructuring/target-assign-throws-iterator-return-is-not-callable.js b/test/language/expressions/assignment/destructuring/target-assign-throws-iterator-return-is-not-callable.js new file mode 100644 index 0000000000..8111ebaa3f --- /dev/null +++ b/test/language/expressions/assignment/destructuring/target-assign-throws-iterator-return-is-not-callable.js @@ -0,0 +1,55 @@ +// Copyright (C) 2025 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-destructuringassignmentevaluation +description: > + Input throw-completion forwarded when IteratorClose returns abruptly because GetMethod throws. +info: | + 13.15.5.2 Runtime Semantics: DestructuringAssignmentEvaluation + + ArrayAssignmentPattern : [ AssignmentElementList , Elisionopt AssignmentRestElementopt ] + ... + 2. Let status be Completion(IteratorDestructuringAssignmentEvaluation of AssignmentElementList with argument iteratorRecord). + 3. If status is an abrupt completion, then + a. If iteratorRecord.[[Done]] is false, return ? IteratorClose(iteratorRecord, status). + b. Return ? status. + ... + + 7.4.11 IteratorClose ( iteratorRecord, completion ) + ... + 3. Let innerResult be Completion(GetMethod(iterator, "return")). + ... + 5. If completion is a throw completion, return ? completion. + ... + + 7.3.10 GetMethod ( V, P ) + 1. Let func be ? GetV(V, P). + 2. If func is either undefined or null, return undefined. + 3. If IsCallable(func) is false, throw a TypeError exception. + ... +---*/ + +function MyError() {} + +var target = { + set a(v) { + throw new MyError(); + } +}; + +for (var returnMethod of [0, 0n, true, "string", {}, Symbol()]) { + var iterator = { + [Symbol.iterator]() { + return this; + }, + next() { + return {done: false}; + }, + return: returnMethod, + }; + + assert.throws(MyError, function() { + ([target.a] = iterator); + }); +}