diff --git a/Jint.Tests.Test262/Language/Expressions/LogicalTests.cs b/Jint.Tests.Test262/Language/Expressions/LogicalTests.cs new file mode 100644 index 0000000000..ac9eef301b --- /dev/null +++ b/Jint.Tests.Test262/Language/Expressions/LogicalTests.cs @@ -0,0 +1,39 @@ +using Xunit; + +namespace Jint.Tests.Test262.Language.Expressions +{ + public class LogicalTests : Test262Test + { + [Theory(DisplayName = "language\\expressions\\logical-and")] + [MemberData(nameof(SourceFiles), "language\\expressions\\logical-and", false)] + [MemberData(nameof(SourceFiles), "language\\expressions\\logical-and", true, Skip = "Skipped")] + protected void LogicalAnd(SourceFile sourceFile) + { + RunTestInternal(sourceFile); + } + + [Theory(DisplayName = "language\\expressions\\logical-assignment")] + [MemberData(nameof(SourceFiles), "language\\expressions\\logical-assignment", false)] + [MemberData(nameof(SourceFiles), "language\\expressions\\logical-assignment", true, Skip = "Skipped")] + protected void LogicalAssignment(SourceFile sourceFile) + { + RunTestInternal(sourceFile); + } + + [Theory(DisplayName = "language\\expressions\\logical-not")] + [MemberData(nameof(SourceFiles), "language\\expressions\\logical-not", false)] + [MemberData(nameof(SourceFiles), "language\\expressions\\logical-not", true, Skip = "Skipped")] + protected void LogicalNot(SourceFile sourceFile) + { + RunTestInternal(sourceFile); + } + + [Theory(DisplayName = "language\\expressions\\logical-or")] + [MemberData(nameof(SourceFiles), "language\\expressions\\logical-or", false)] + [MemberData(nameof(SourceFiles), "language\\expressions\\logical-or", true, Skip = "Skipped")] + protected void LogicalOr(SourceFile sourceFile) + { + RunTestInternal(sourceFile); + } + } +} \ No newline at end of file diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-arguments-strict.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-arguments-strict.js new file mode 100644 index 0000000000..c23e4a88f9 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-arguments-strict.js @@ -0,0 +1,18 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators +description: > + Strict Mode - SyntaxError is thrown if the identifier arguments + appear as the LeftHandSideExpression of a Logical Assignment + operator(&&=) +flags: [onlyStrict] +negative: + phase: parse + type: SyntaxError +features: [logical-assignment-operators] +---*/ +$DONOTEVALUATE(); + +arguments &&= 20; diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-bigint.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-bigint.js new file mode 100644 index 0000000000..784dcfc8b9 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-bigint.js @@ -0,0 +1,27 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: Logical And Assignment Operator +features: [BigInt, logical-assignment-operators] +info: | + AssignmentExpression: + LeftHandSideExpression &&= AssignmentExpression + + 1. Let lref be the result of evaluating LeftHandSideExpression. + 2. Let lval be ? GetValue(lref). + 3. Let lbool be ! ToBoolean(lval). + 4. If lbool is false, return lval. + 5. Let rref be the result of evaluating AssignmentExpression. + 6. Let rval be ? GetValue(rref). + 7. Perform ? PutValue(lref, rval). + 8. Return rval. + +---*/ + +var value = 0n; +assert.sameValue(value &&= 1n, 0n, "(value &&= 1n) === 0n; where value = 0n"); + +value = 2n; +assert.sameValue(value &&= 1n, 1n, "(value &&= 1n) === 1n; where value = 2n"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-lhs-before-rhs.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-lhs-before-rhs.js new file mode 100644 index 0000000000..af6784bb58 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-lhs-before-rhs.js @@ -0,0 +1,53 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + The LeftHandSideExpression is evaluated before the AssignmentExpression. +features: [logical-assignment-operators] + +---*/ + +function DummyError() { } + +assert.throws(DummyError, function() { + var base = null; + var prop = function() { + throw new DummyError(); + }; + var expr = function() { + throw new Test262Error("right-hand side expression evaluated"); + }; + + base[prop()] &&= expr(); +}); + +assert.throws(TypeError, function() { + var base = null; + var prop = { + toString: function() { + throw new Test262Error("property key evaluated"); + } + }; + var expr = function() { + throw new Test262Error("right-hand side expression evaluated"); + }; + + base[prop] &&= expr(); +}); + +var count = 0; +var obj = {}; +function incr() { + return ++count; +} + +assert.sameValue(obj[incr()] &&= incr(), undefined, "obj[incr()] &&= incr()"); +assert.sameValue(obj[1], undefined, "obj[1]"); +assert.sameValue(count, 1, "count"); + +obj[2] = 1; +assert.sameValue(obj[incr()] &&= incr(), 3, "obj[incr()] &&= incr()"); +assert.sameValue(obj[2], 3, "obj[2]"); +assert.sameValue(count, 3, "count"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-namedevaluation-arrow-function.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-namedevaluation-arrow-function.js new file mode 100644 index 0000000000..283c3a50d9 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-namedevaluation-arrow-function.js @@ -0,0 +1,20 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: NamedEvaluation of Logical And Assignment +info: | + AssignmentExpression: + LeftHandSideExpression &&= AssignmentExpression + + 5. If IsAnonymousFunctionDefinition(AssignmentExpression) and IsIdentifierRef of LeftHandSideExpression are both true, then + a. Let rval be NamedEvaluation of AssignmentExpression with argument GetReferencedName(lref). +features: [logical-assignment-operators] + +---*/ + +var value = 1; +value &&= () => {}; + +assert.sameValue(value.name, "value", "value"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-namedevaluation-class-expression.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-namedevaluation-class-expression.js new file mode 100644 index 0000000000..af96ddaaa9 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-namedevaluation-class-expression.js @@ -0,0 +1,20 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: NamedEvaluation of Logical And Assignment +info: | + AssignmentExpression: + LeftHandSideExpression &&= AssignmentExpression + + 5. If IsAnonymousFunctionDefinition(AssignmentExpression) and IsIdentifierRef of LeftHandSideExpression are both true, then + a. Let rval be NamedEvaluation of AssignmentExpression with argument GetReferencedName(lref). +features: [logical-assignment-operators] + +---*/ + +var value = 1; +value &&= class {}; + +assert.sameValue(value.name, "value", "value"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-namedevaluation-function.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-namedevaluation-function.js new file mode 100644 index 0000000000..06948e3eef --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-namedevaluation-function.js @@ -0,0 +1,20 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: NamedEvaluation of Logical And Assignment +info: | + AssignmentExpression: + LeftHandSideExpression &&= AssignmentExpression + + 5. If IsAnonymousFunctionDefinition(AssignmentExpression) and IsIdentifierRef of LeftHandSideExpression are both true, then + a. Let rval be NamedEvaluation of AssignmentExpression with argument GetReferencedName(lref). +features: [logical-assignment-operators] + +---*/ + +var value = 1; +value &&= function() {}; + +assert.sameValue(value.name, "value", "value"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-no-set-put.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-no-set-put.js new file mode 100644 index 0000000000..08d42d214f --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-no-set-put.js @@ -0,0 +1,28 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + Strict Mode - TypeError is thrown if the LeftHandSide of a Logical + Assignment operator(&&=) is a reference to a data property with the + attribute value {[[Set]]:undefined} and PutValue step is reached. +flags: [onlyStrict] +features: [logical-assignment-operators] + +---*/ + +var obj = {}; +Object.defineProperty(obj, "prop", { + get: function() { + return 2; + }, + set: undefined, + enumerable: true, + configurable: true +}); + +assert.throws(TypeError, function() { + obj.prop &&= 1; +}); +assert.sameValue(obj.prop, 2, "obj.prop"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-no-set.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-no-set.js new file mode 100644 index 0000000000..67da0a5614 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-no-set.js @@ -0,0 +1,25 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + Strict Mode - TypeError is not thrown if the LeftHandSide of a Logical + Assignment operator(&&=) is a reference to a data property with the + attribute value {[[Set]]:undefined} and PutValue step is not reached. +flags: [onlyStrict] +features: [logical-assignment-operators] + +---*/ + +var obj = {}; +Object.defineProperty(obj, "prop", { + get: function() { + return 0; + }, + set: undefined, + enumerable: true, + configurable: true +}); + +assert.sameValue(obj.prop &&= 1, 0, "obj.prop"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-non-extensible.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-non-extensible.js new file mode 100644 index 0000000000..b1e3cf05ea --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-non-extensible.js @@ -0,0 +1,19 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + Strict Mode - TypeError is not thrown if The LeftHandSide of a Logical + Assignment operator(&&=) is a reference to a non-existent property of an + object whose [[Extensible]] internal property is false. +flags: [onlyStrict] +features: [logical-assignment-operators] + +---*/ + +var obj = {}; +Object.preventExtensions(obj); + +obj.prop &&= 1; +assert.sameValue(obj.prop, undefined, "obj.prop"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-non-simple-lhs.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-non-simple-lhs.js new file mode 100644 index 0000000000..de37734ee2 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-non-simple-lhs.js @@ -0,0 +1,19 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-static-semantics-early-errors +description: > + It is a Syntax Error if AssignmentTargetType of LeftHandSideExpression is + not simple. +negative: + phase: parse + type: SyntaxError +features: [logical-assignment-operators] + +---*/ + +$DONOTEVALUATE(); + +function test() {} +test() &&= 1; diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-non-writeable-put.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-non-writeable-put.js new file mode 100644 index 0000000000..2dff8b5726 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-non-writeable-put.js @@ -0,0 +1,26 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + Strict Mode - TypeError is thrown if the LeftHandSide of a Logical + Assignment operator(&&=) is a reference to a data property with the + attribute value {[[Writable]]:false} and PutValue step is reached. +flags: [onlyStrict] +features: [logical-assignment-operators] + +---*/ + +var obj = {}; +Object.defineProperty(obj, "prop", { + value: 2, + writable: false, + enumerable: true, + configurable: true +}); + +assert.throws(TypeError, function() { + obj.prop &&= 1; +}); +assert.sameValue(obj.prop, 2, "obj.prop"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-non-writeable.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-non-writeable.js new file mode 100644 index 0000000000..d9c67defaa --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-non-writeable.js @@ -0,0 +1,23 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + Strict Mode - TypeError is not thrown if the LeftHandSide of a Logical + Assignment operator(&&=) is a reference to a data property with the + attribute value {[[Writable]]:false} and PutValue step is not reached. +flags: [onlyStrict] +features: [logical-assignment-operators] + +---*/ + +var obj = {}; +Object.defineProperty(obj, "prop", { + value: 0, + writable: false, + enumerable: true, + configurable: true +}); + +assert.sameValue(obj.prop &&= 1, 0, "obj.prop"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-unresolved-lhs.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-unresolved-lhs.js new file mode 100644 index 0000000000..984df2abcf --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-unresolved-lhs.js @@ -0,0 +1,16 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + ReferenceError is thrown if the LeftHandSideExpression of a Logical + Assignment operator(&&=) evaluates to an unresolvable reference +flags: [onlyStrict] +features: [logical-assignment-operators] + +---*/ + +assert.throws(ReferenceError, function() { + unresolved &&= 1; +}); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-unresolved-rhs-put.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-unresolved-rhs-put.js new file mode 100644 index 0000000000..f59a32eaa7 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-unresolved-rhs-put.js @@ -0,0 +1,19 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + ReferenceError is thrown if the AssignmentExpression of a Logical + Assignment operator(&&=) evaluates to an unresolvable reference and the + AssignmentExpression is evaluated. +features: [logical-assignment-operators] + +---*/ + +var value = 2; + +assert.throws(ReferenceError, function() { + value &&= unresolved; +}); +assert.sameValue(value, 2, "value"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-unresolved-rhs.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-unresolved-rhs.js new file mode 100644 index 0000000000..b86c6c47f1 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator-unresolved-rhs.js @@ -0,0 +1,16 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + ReferenceError is not thrown if the AssignmentExpression of a Logical + Assignment operator(&&=) evaluates to an unresolvable reference and the + AssignmentExpression is not evaluated. +features: [logical-assignment-operators] + +---*/ + +var value = 0; + +assert.sameValue(value &&= unresolved, 0, "value"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator.js new file mode 100644 index 0000000000..71765a0415 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-assignment-operator.js @@ -0,0 +1,61 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: Logical And Assignment Operator +info: | + AssignmentExpression: + LeftHandSideExpression &&= AssignmentExpression + + 1. Let lref be the result of evaluating LeftHandSideExpression. + 2. Let lval be ? GetValue(lref). + 3. Let lbool be ! ToBoolean(lval). + 4. If lbool is false, return lval. + 5. Let rref be the result of evaluating AssignmentExpression. + 6. Let rval be ? GetValue(rref). + 7. Perform ? PutValue(lref, rval). + 8. Return rval. +features: [logical-assignment-operators] + +---*/ + +var value = undefined; +assert.sameValue(value &&= 1, undefined, "(value &&= 1) === undefined; where value = undefined"); + +value = null; +assert.sameValue(value &&= 1, null, "(value &&= 1) === null where value = null"); + +value = false; +assert.sameValue(value &&= 1, false, "(value &&= 1) === false; where value = false"); + +value = 0; +assert.sameValue(value &&= 1, 0, "(value &&= 1) === 0; where value = 0"); + +value = -0; +assert.sameValue(value &&= 1, -0, "(value &&= 1) === -0; where value = -0"); + +value = NaN; +assert.sameValue(value &&= 1, NaN, "(value &&= 1) === NaN; where value = NaN"); + +value = ""; +assert.sameValue(value &&= 1, "", '(value &&= 1) === "" where value = ""'); + + + +value = true; +assert.sameValue(value &&= 1, 1, "(value &&= 1) === 1; where value = true"); + +value = 2; +assert.sameValue(value &&= 1, 1, "(value &&= 1) === 1; where value = 2"); + +value = "test"; +assert.sameValue(value &&= 1, 1, '(value &&= 1) === 1; where value = "test"'); + +var sym = Symbol(""); +value = sym; +assert.sameValue(value &&= 1, 1, "(value &&= 1) === 1; where value = Symbol()"); + +var obj = {}; +value = obj; +assert.sameValue(value &&= 1, 1, "(value &&= 1) === 1; where value = {}"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-eval-strict.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-eval-strict.js new file mode 100644 index 0000000000..345ea960e1 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-eval-strict.js @@ -0,0 +1,17 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators +description: > + Strict Mode - SyntaxError is thrown if the identifier eval appear + as the LeftHandSideExpression of a Logical Assignment operator(&&=) +flags: [onlyStrict] +negative: + phase: parse + type: SyntaxError +features: [logical-assignment-operators] +---*/ +$DONOTEVALUATE(); + +eval &&= 20; diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-non-simple.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-non-simple.js new file mode 100644 index 0000000000..977ace323a --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-non-simple.js @@ -0,0 +1,17 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-static-semantics-early-errors +info: | + It is an early Syntax Error if AssignmentTargetType of + LeftHandSideExpression is invalid or strict. +description: Logical "&&=" assignment with non-simple target +negative: + phase: parse + type: SyntaxError +features: [logical-assignment-operators] +---*/ +$DONOTEVALUATE(); + +1 &&= 1; diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-whitespace.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-whitespace.js new file mode 100644 index 0000000000..35b8157135 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-and-whitespace.js @@ -0,0 +1,58 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: | + White Space and Line Terminator between LeftHandSideExpression and "@=" + or between "@=" and AssignmentExpression are allowed +esid: sec-assignment-operators +description: Checking by evaluating expression "x[...]&&=[...]y" +features: [logical-assignment-operators] +---*/ +var x; + +x = 1; +assert.sameValue(x &&= 2, 2, 'U+0009 (expression)'); +assert.sameValue(x, 2, 'U+0009 (side effect)'); + +x = 1; +assert.sameValue(x &&= 2, 2, 'U+000B (expression)'); +assert.sameValue(x, 2, 'U+000B (side effect)'); + +x = 1; +assert.sameValue(x &&= 2, 2, 'U+000C (expression)'); +assert.sameValue(x, 2, 'U+000C (side effect)'); + +x = 1; +assert.sameValue(x &&= 2, 2, 'U+0020 (expression)'); +assert.sameValue(x, 2, 'U+0020 (side effect)'); + +x = 1; +assert.sameValue(x &&= 2, 2, 'U+00A0 (expression)'); +assert.sameValue(x, 2, 'U+00A0 (side effect)'); + +x = 1; +assert.sameValue(x +&&= +2, 2, 'U+000A (expression)'); +assert.sameValue(x, 2, 'U+000A (side effect)'); + +x = 1; +assert.sameValue(x +&&= +2, 2, 'U+000D (expression)'); +assert.sameValue(x, 2, 'U+000D (side effect)'); + +x = 1; +assert.sameValue(x
&&=
2, 2, 'U+2028 (expression)'); +assert.sameValue(x, 2, 'U+2028 (side effect)'); + +x = 1; +assert.sameValue(x
&&=
2, 2, 'U+2029 (expression)'); +assert.sameValue(x, 2, 'U+2029 (side effect)'); + +x = 1; +assert.sameValue(x   +

&&=   +

2, 2, 'U+0009U+000BU+000CU+0020U+00A0U+000AU+000DU+2028U+2029 (expression)'); +assert.sameValue(x, 2, 'U+0009U+000BU+000CU+0020U+00A0U+000AU+000DU+2028U+2029 (side effect)'); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-arguments-strict.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-arguments-strict.js new file mode 100644 index 0000000000..499f9ea045 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-arguments-strict.js @@ -0,0 +1,18 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators +description: > + Strict Mode - SyntaxError is thrown if the identifier arguments + appear as the LeftHandSideExpression of a Logical Assignment + operator(??=) +flags: [onlyStrict] +negative: + phase: parse + type: SyntaxError +features: [logical-assignment-operators] +---*/ +$DONOTEVALUATE(); + +arguments ??= 20; diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-bigint.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-bigint.js new file mode 100644 index 0000000000..4626ce7307 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-bigint.js @@ -0,0 +1,26 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: Logical Nullish Assignment Operator +features: [BigInt, logical-assignment-operators] +info: | + AssignmentExpression: + LeftHandSideExpression ??= AssignmentExpression + + 1. Let lref be the result of evaluating LeftHandSideExpression. + 2. Let lval be ? GetValue(lref). + 3. If lval is neither undefined nor null, return lval. + 4. Let rref be the result of evaluating AssignmentExpression. + 5. Let rval be ? GetValue(rref). + 6. Perform ? PutValue(lref, rval). + 7. Return rval. + +---*/ + +var value = 0n; +assert.sameValue(value ??= 1n, 0n, "(value ??= 1n) === 0n; where value = 0n"); + +value = 2n; +assert.sameValue(value ??= 1n, 2n, "(value ??= 1n) === 2n; where value = 2n"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-lhs-before-rhs.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-lhs-before-rhs.js new file mode 100644 index 0000000000..32d5d1bd55 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-lhs-before-rhs.js @@ -0,0 +1,53 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + The LeftHandSideExpression is evaluated before the AssignmentExpression. +features: [logical-assignment-operators] + +---*/ + +function DummyError() { } + +assert.throws(DummyError, function() { + var base = null; + var prop = function() { + throw new DummyError(); + }; + var expr = function() { + throw new Test262Error("right-hand side expression evaluated"); + }; + + base[prop()] ??= expr(); +}); + +assert.throws(TypeError, function() { + var base = null; + var prop = { + toString: function() { + throw new Test262Error("property key evaluated"); + } + }; + var expr = function() { + throw new Test262Error("right-hand side expression evaluated"); + }; + + base[prop] ??= expr(); +}); + +var count = 0; +var obj = {}; +function incr() { + return ++count; +} + +assert.sameValue(obj[incr()] ??= incr(), 2, "obj[incr()] ??= incr()"); +assert.sameValue(obj[1], 2, "obj[1]"); +assert.sameValue(count, 2, "count"); + +obj[3] = 1; +assert.sameValue(obj[incr()] ??= incr(), 1, "obj[incr()] ??= incr()"); +assert.sameValue(obj[3], 1, "obj[3]"); +assert.sameValue(count, 3, "count"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-namedevaluation-arrow-function.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-namedevaluation-arrow-function.js new file mode 100644 index 0000000000..04f45651d9 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-namedevaluation-arrow-function.js @@ -0,0 +1,20 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: NamedEvaluation of Logical Nullish Assignment +info: | + AssignmentExpression: + LeftHandSideExpression ??= AssignmentExpression + + 4. If IsAnonymousFunctionDefinition(AssignmentExpression) and IsIdentifierRef of LeftHandSideExpression are both true, then + a. Let rval be NamedEvaluation of AssignmentExpression with argument GetReferencedName(lref). +features: [logical-assignment-operators] + +---*/ + +var value = undefined; +value ??= () => {}; + +assert.sameValue(value.name, "value", "value"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-namedevaluation-class-expression.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-namedevaluation-class-expression.js new file mode 100644 index 0000000000..988ca4355d --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-namedevaluation-class-expression.js @@ -0,0 +1,20 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: NamedEvaluation of Logical Nullish Assignment +info: | + AssignmentExpression: + LeftHandSideExpression ??= AssignmentExpression + + 4. If IsAnonymousFunctionDefinition(AssignmentExpression) and IsIdentifierRef of LeftHandSideExpression are both true, then + a. Let rval be NamedEvaluation of AssignmentExpression with argument GetReferencedName(lref). +features: [logical-assignment-operators] + +---*/ + +var value = undefined; +value ??= class {}; + +assert.sameValue(value.name, "value", "value"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-namedevaluation-function.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-namedevaluation-function.js new file mode 100644 index 0000000000..6f41d29a2c --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-namedevaluation-function.js @@ -0,0 +1,20 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: NamedEvaluation of Logical Nullish Assignment +info: | + AssignmentExpression: + LeftHandSideExpression ??= AssignmentExpression + + 4. If IsAnonymousFunctionDefinition(AssignmentExpression) and IsIdentifierRef of LeftHandSideExpression are both true, then + a. Let rval be NamedEvaluation of AssignmentExpression with argument GetReferencedName(lref). +features: [logical-assignment-operators] + +---*/ + +var value = undefined; +value ??= function() {}; + +assert.sameValue(value.name, "value", "value"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-no-set-put.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-no-set-put.js new file mode 100644 index 0000000000..1d4441bfad --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-no-set-put.js @@ -0,0 +1,28 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + Strict Mode - TypeError is thrown if the LeftHandSide of a Logical + Assignment operator(??=) is a reference to a data property with the + attribute value {[[Set]]:undefined} and PutValue step is reached. +flags: [onlyStrict] +features: [logical-assignment-operators] + +---*/ + +var obj = {}; +Object.defineProperty(obj, "prop", { + get: function() { + return undefined; + }, + set: undefined, + enumerable: true, + configurable: true +}); + +assert.throws(TypeError, function() { + obj.prop ??= 1; +}); +assert.sameValue(obj.prop, undefined, "obj.prop"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-no-set.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-no-set.js new file mode 100644 index 0000000000..788f276a75 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-no-set.js @@ -0,0 +1,25 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + Strict Mode - TypeError is not thrown if the LeftHandSide of a Logical + Assignment operator(??=) is a reference to a data property with the + attribute value {[[Set]]:undefined} and PutValue step is not reached. +flags: [onlyStrict] +features: [logical-assignment-operators] + +---*/ + +var obj = {}; +Object.defineProperty(obj, "prop", { + get: function() { + return 0; + }, + set: undefined, + enumerable: true, + configurable: true +}); + +assert.sameValue(obj.prop ??= 1, 0, "obj.prop"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-non-extensible.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-non-extensible.js new file mode 100644 index 0000000000..8556519956 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-non-extensible.js @@ -0,0 +1,21 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + Strict Mode - TypeError is thrown if The LeftHandSide of a Logical + Assignment operator(??=) is a reference to a non-existent property + of an object whose [[Extensible]] internal property is false. +flags: [onlyStrict] +features: [logical-assignment-operators] + +---*/ + +var obj = {}; +Object.preventExtensions(obj); + +assert.throws(TypeError, function() { + obj.prop ??= 1; +}); +assert.sameValue(obj.prop, undefined, "obj.prop"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-non-simple-lhs.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-non-simple-lhs.js new file mode 100644 index 0000000000..ff2dd530ad --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-non-simple-lhs.js @@ -0,0 +1,19 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-static-semantics-early-errors +description: > + It is a Syntax Error if AssignmentTargetType of LeftHandSideExpression is + not simple. +negative: + phase: parse + type: SyntaxError +features: [logical-assignment-operators] + +---*/ + +$DONOTEVALUATE(); + +function test() {} +test() ??= 1; diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-non-writeable-put.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-non-writeable-put.js new file mode 100644 index 0000000000..67d219d618 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-non-writeable-put.js @@ -0,0 +1,26 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + Strict Mode - TypeError is thrown if the LeftHandSide of a Logical + Assignment operator(??=) is a reference to a data property with the + attribute value {[[Writable]]:false} and PutValue step is reached. +flags: [onlyStrict] +features: [logical-assignment-operators] + +---*/ + +var obj = {}; +Object.defineProperty(obj, "prop", { + value: undefined, + writable: false, + enumerable: true, + configurable: true +}); + +assert.throws(TypeError, function() { + obj.prop ??= 1; +}); +assert.sameValue(obj.prop, undefined, "obj.prop"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-non-writeable.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-non-writeable.js new file mode 100644 index 0000000000..6589a43474 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-non-writeable.js @@ -0,0 +1,23 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + Strict Mode - TypeError is not thrown if the LeftHandSide of a Logical + Assignment operator(??=) is a reference to a data property with the + attribute value {[[Writable]]:false} and PutValue step is not reached. +flags: [onlyStrict] +features: [logical-assignment-operators] + +---*/ + +var obj = {}; +Object.defineProperty(obj, "prop", { + value: 0, + writable: false, + enumerable: true, + configurable: true +}); + +assert.sameValue(obj.prop ??= 1, 0, "obj.prop"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-unresolved-lhs.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-unresolved-lhs.js new file mode 100644 index 0000000000..f0663234d2 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-unresolved-lhs.js @@ -0,0 +1,16 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + ReferenceError is thrown if the LeftHandSideExpression of a Logical + Assignment operator(??=) evaluates to an unresolvable reference +flags: [onlyStrict] +features: [logical-assignment-operators] + +---*/ + +assert.throws(ReferenceError, function() { + unresolved ??= 1; +}); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-unresolved-rhs-put.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-unresolved-rhs-put.js new file mode 100644 index 0000000000..e91c973f04 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-unresolved-rhs-put.js @@ -0,0 +1,19 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + ReferenceError is thrown if the AssignmentExpression of a Logical + Assignment operator(??=) evaluates to an unresolvable reference and the + AssignmentExpression is evaluated. +features: [logical-assignment-operators] + +---*/ + +var value = undefined; + +assert.throws(ReferenceError, function() { + value ??= unresolved; +}); +assert.sameValue(value, undefined, "value"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-unresolved-rhs.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-unresolved-rhs.js new file mode 100644 index 0000000000..b330413856 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator-unresolved-rhs.js @@ -0,0 +1,16 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + ReferenceError is not thrown if the AssignmentExpression of a Logical + Assignment operator(??=) evaluates to an unresolvable reference and the + AssignmentExpression is not evaluated. +features: [logical-assignment-operators] + +---*/ + +var value = 0; + +assert.sameValue(value ??= unresolved, 0, "value"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator.js new file mode 100644 index 0000000000..8d94ecc3ae --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-assignment-operator.js @@ -0,0 +1,60 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: Logical Nullish Assignment Operator +info: | + AssignmentExpression: + LeftHandSideExpression ??= AssignmentExpression + + 1. Let lref be the result of evaluating LeftHandSideExpression. + 2. Let lval be ? GetValue(lref). + 3. If lval is neither undefined nor null, return lval. + 4. Let rref be the result of evaluating AssignmentExpression. + 5. Let rval be ? GetValue(rref). + 6. Perform ? PutValue(lref, rval). + 7. Return rval. +features: [logical-assignment-operators] + +---*/ + +var value = undefined; +assert.sameValue(value ??= 1, 1, "(value ??= 1) === 1; where value = undefined"); + +value = null; +assert.sameValue(value ??= 1, 1, "(value ??= 1) === 1; where value = null"); + +value = false; +assert.sameValue(value ??= 1, false, "(value ??= 1) === false; where value = false"); + +value = 0; +assert.sameValue(value ??= 1, 0, "(value ??= 1) === 0; where value = 0"); + +value = -0; +assert.sameValue(value ??= 1, -0, "(value ??= 1) === -0; where value = -0"); + +value = NaN; +assert.sameValue(value ??= 1, NaN, "(value ??= 1) === NaN; where value = NaN"); + +value = ""; +assert.sameValue(value ??= 1, "", '(value ??= 1) === "" where value = ""'); + + + +value = true; +assert.sameValue(value ??= 1, true, "(value ??= 1) === true; where value = true"); + +value = 2; +assert.sameValue(value ??= 1, 2, "(value ??= 1) === 2; where value = 2"); + +value = "test"; +assert.sameValue(value ??= 1, "test", '(value ??= 1) === "test"; where value = "test"'); + +var sym = Symbol(""); +value = sym; +assert.sameValue(value ??= 1, sym, "(value ??= 1) === Symbol(); where value = Symbol()"); + +var obj = {}; +value = obj; +assert.sameValue(value ??= 1, obj, "(value ??= 1) === {}; where value = {}"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-eval-strict.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-eval-strict.js new file mode 100644 index 0000000000..70cd3c9bca --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-eval-strict.js @@ -0,0 +1,17 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators +description: > + Strict Mode - SyntaxError is thrown if the identifier eval appear + as the LeftHandSideExpression of a Logical Assignment operator(??=) +flags: [onlyStrict] +negative: + phase: parse + type: SyntaxError +features: [logical-assignment-operators] +---*/ +$DONOTEVALUATE(); + +eval ??= 20; diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-non-simple.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-non-simple.js new file mode 100644 index 0000000000..fe31217166 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-non-simple.js @@ -0,0 +1,17 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-static-semantics-early-errors +info: | + It is an early Syntax Error if AssignmentTargetType of + LeftHandSideExpression is invalid or strict. +description: Logical "??=" assignment with non-simple target +negative: + phase: parse + type: SyntaxError +features: [logical-assignment-operators] +---*/ +$DONOTEVALUATE(); + +1 ??= 1; diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-whitespace.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-whitespace.js new file mode 100644 index 0000000000..7fc6484207 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-nullish-whitespace.js @@ -0,0 +1,58 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: | + White Space and Line Terminator between LeftHandSideExpression and "@=" + or between "@=" and AssignmentExpression are allowed +esid: sec-assignment-operators +description: Checking by evaluating expression "x[...]??=[...]y" +features: [logical-assignment-operators] +---*/ +var x; + +x = null; +assert.sameValue(x ??= 1, 1, 'U+0009 (expression)'); +assert.sameValue(x, 1, 'U+0009 (side effect)'); + +x = null; +assert.sameValue(x ??= 1, 1, 'U+000B (expression)'); +assert.sameValue(x, 1, 'U+000B (side effect)'); + +x = null; +assert.sameValue(x ??= 1, 1, 'U+000C (expression)'); +assert.sameValue(x, 1, 'U+000C (side effect)'); + +x = null; +assert.sameValue(x ??= 1, 1, 'U+0020 (expression)'); +assert.sameValue(x, 1, 'U+0020 (side effect)'); + +x = null; +assert.sameValue(x ??= 1, 1, 'U+00A0 (expression)'); +assert.sameValue(x, 1, 'U+00A0 (side effect)'); + +x = null; +assert.sameValue(x +??= +1, 1, 'U+000A (expression)'); +assert.sameValue(x, 1, 'U+000A (side effect)'); + +x = null; +assert.sameValue(x +??= +1, 1, 'U+000D (expression)'); +assert.sameValue(x, 1, 'U+000D (side effect)'); + +x = null; +assert.sameValue(x
??=
1, 1, 'U+2028 (expression)'); +assert.sameValue(x, 1, 'U+2028 (side effect)'); + +x = null; +assert.sameValue(x
??=
1, 1, 'U+2029 (expression)'); +assert.sameValue(x, 1, 'U+2029 (side effect)'); + +x = null; +assert.sameValue(x   +

??=   +

1, 1, 'U+0009U+000BU+000CU+0020U+00A0U+000AU+000DU+2028U+2029 (expression)'); +assert.sameValue(x, 1, 'U+0009U+000BU+000CU+0020U+00A0U+000AU+000DU+2028U+2029 (side effect)'); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-arguments-strict.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-arguments-strict.js new file mode 100644 index 0000000000..c67b535501 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-arguments-strict.js @@ -0,0 +1,18 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators +description: > + Strict Mode - SyntaxError is thrown if the identifier arguments + appear as the LeftHandSideExpression of a Logical Assignment + operator(||=) +flags: [onlyStrict] +negative: + phase: parse + type: SyntaxError +features: [logical-assignment-operators] +---*/ +$DONOTEVALUATE(); + +arguments ||= 20; diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-bigint.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-bigint.js new file mode 100644 index 0000000000..ccd4db2f10 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-bigint.js @@ -0,0 +1,27 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: Logical Or Assignment Operator +features: [BigInt, logical-assignment-operators] +info: | + AssignmentExpression: + LeftHandSideExpression ||= AssignmentExpression + + 1. Let lref be the result of evaluating LeftHandSideExpression. + 2. Let lval be ? GetValue(lref). + 3. Let lbool be ! ToBoolean(lval). + 4. If lbool is true, return lval. + 5. Let rref be the result of evaluating AssignmentExpression. + 6. Let rval be ? GetValue(rref). + 7. Perform ? PutValue(lref, rval). + 8. Return rval. + +---*/ + +var value = 0n; +assert.sameValue(value ||= 1n, 1n, "(value ||= 1n) === 1n; where value = 0n"); + +value = 2n; +assert.sameValue(value ||= 1n, 2n, "(value ||= 1n) === 2n; where value = 2n"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-lhs-before-rhs.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-lhs-before-rhs.js new file mode 100644 index 0000000000..7cbe2ca43e --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-lhs-before-rhs.js @@ -0,0 +1,53 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + The LeftHandSideExpression is evaluated before the AssignmentExpression. +features: [logical-assignment-operators] + +---*/ + +function DummyError() { } + +assert.throws(DummyError, function() { + var base = null; + var prop = function() { + throw new DummyError(); + }; + var expr = function() { + throw new Test262Error("right-hand side expression evaluated"); + }; + + base[prop()] ||= expr(); +}); + +assert.throws(TypeError, function() { + var base = null; + var prop = { + toString: function() { + throw new Test262Error("property key evaluated"); + } + }; + var expr = function() { + throw new Test262Error("right-hand side expression evaluated"); + }; + + base[prop] ||= expr(); +}); + +var count = 0; +var obj = {}; +function incr() { + return ++count; +} + +assert.sameValue(obj[incr()] ||= incr(), 2, "obj[incr()] ||= incr()"); +assert.sameValue(obj[1], 2, "obj[1]"); +assert.sameValue(count, 2, "count"); + +obj[3] = 1; +assert.sameValue(obj[incr()] ||= incr(), 1, "obj[incr()] ||= incr()"); +assert.sameValue(obj[3], 1, "obj[3]"); +assert.sameValue(count, 3, "count"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-namedevaluation-arrow-function.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-namedevaluation-arrow-function.js new file mode 100644 index 0000000000..39e192ce4e --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-namedevaluation-arrow-function.js @@ -0,0 +1,20 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: NamedEvaluation of Logical Or Assignment +info: | + AssignmentExpression: + LeftHandSideExpression ||= AssignmentExpression + + 5. If IsAnonymousFunctionDefinition(AssignmentExpression) and IsIdentifierRef of LeftHandSideExpression are both true, then + a. Let rval be NamedEvaluation of AssignmentExpression with argument GetReferencedName(lref). +features: [logical-assignment-operators] + +---*/ + +var value = 0; +value ||= () => {}; + +assert.sameValue(value.name, "value", "value"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-namedevaluation-class-expression.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-namedevaluation-class-expression.js new file mode 100644 index 0000000000..bff369098a --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-namedevaluation-class-expression.js @@ -0,0 +1,20 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: NamedEvaluation of Logical Or Assignment +info: | + AssignmentExpression: + LeftHandSideExpression ||= AssignmentExpression + + 5. If IsAnonymousFunctionDefinition(AssignmentExpression) and IsIdentifierRef of LeftHandSideExpression are both true, then + a. Let rval be NamedEvaluation of AssignmentExpression with argument GetReferencedName(lref). +features: [logical-assignment-operators] + +---*/ + +var value = 0; +value ||= class {}; + +assert.sameValue(value.name, "value", "value"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-namedevaluation-function.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-namedevaluation-function.js new file mode 100644 index 0000000000..3a8acd112a --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-namedevaluation-function.js @@ -0,0 +1,20 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: NamedEvaluation of Logical Or Assignment +info: | + AssignmentExpression: + LeftHandSideExpression ||= AssignmentExpression + + 5. If IsAnonymousFunctionDefinition(AssignmentExpression) and IsIdentifierRef of LeftHandSideExpression are both true, then + a. Let rval be NamedEvaluation of AssignmentExpression with argument GetReferencedName(lref). +features: [logical-assignment-operators] + +---*/ + +var value = 0; +value ||= function() {}; + +assert.sameValue(value.name, "value", "value"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-no-set-put.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-no-set-put.js new file mode 100644 index 0000000000..c0856eea6e --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-no-set-put.js @@ -0,0 +1,28 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + Strict Mode - TypeError is thrown if the LeftHandSide of a Logical + Assignment operator(||=) is a reference to a data property with the + attribute value {[[Set]]:undefined} and PutValue step is reached. +flags: [onlyStrict] +features: [logical-assignment-operators] + +---*/ + +var obj = {}; +Object.defineProperty(obj, "prop", { + get: function() { + return 0; + }, + set: undefined, + enumerable: true, + configurable: true +}); + +assert.throws(TypeError, function() { + obj.prop ||= 1; +}); +assert.sameValue(obj.prop, 0, "obj.prop"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-no-set.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-no-set.js new file mode 100644 index 0000000000..dcab72277f --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-no-set.js @@ -0,0 +1,25 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + Strict Mode - TypeError is not thrown if the LeftHandSide of a Logical + Assignment operator(||=) is a reference to a data property with the + attribute value {[[Set]]:undefined} and PutValue step is not reached. +flags: [onlyStrict] +features: [logical-assignment-operators] + +---*/ + +var obj = {}; +Object.defineProperty(obj, "prop", { + get: function() { + return 2; + }, + set: undefined, + enumerable: true, + configurable: true +}); + +assert.sameValue(obj.prop ||= 1, 2, "obj.prop"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-non-extensible.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-non-extensible.js new file mode 100644 index 0000000000..16275bf717 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-non-extensible.js @@ -0,0 +1,21 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + Strict Mode - TypeError is thrown if The LeftHandSide of a Logical + Assignment operator(||=) is a reference to a non-existent property + of an object whose [[Extensible]] internal property is false. +flags: [onlyStrict] +features: [logical-assignment-operators] + +---*/ + +var obj = {}; +Object.preventExtensions(obj); + +assert.throws(TypeError, function() { + obj.prop ||= 1; +}); +assert.sameValue(obj.prop, undefined, "obj.prop"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-non-simple-lhs.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-non-simple-lhs.js new file mode 100644 index 0000000000..ba35c8964b --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-non-simple-lhs.js @@ -0,0 +1,19 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-static-semantics-early-errors +description: > + It is a Syntax Error if AssignmentTargetType of LeftHandSideExpression is + not simple. +negative: + phase: parse + type: SyntaxError +features: [logical-assignment-operators] + +---*/ + +$DONOTEVALUATE(); + +function test() {} +test() ||= 1; diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-non-writeable-put.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-non-writeable-put.js new file mode 100644 index 0000000000..2f57dd7089 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-non-writeable-put.js @@ -0,0 +1,26 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + Strict Mode - TypeError is thrown if the LeftHandSide of a Logical + Assignment operator(||=) is a reference to a data property with the + attribute value {[[Writable]]:false} and PutValue step is reached. +flags: [onlyStrict] +features: [logical-assignment-operators] + +---*/ + +var obj = {}; +Object.defineProperty(obj, "prop", { + value: 0, + writable: false, + enumerable: true, + configurable: true +}); + +assert.throws(TypeError, function() { + obj.prop ||= 1; +}); +assert.sameValue(obj.prop, 0, "obj.prop"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-non-writeable.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-non-writeable.js new file mode 100644 index 0000000000..ca43cd6b1b --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-non-writeable.js @@ -0,0 +1,23 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + Strict Mode - TypeError is not thrown if the LeftHandSide of a Logical + Assignment operator(||=) is a reference to a data property with the + attribute value {[[Writable]]:false} and PutValue step is not reached. +flags: [onlyStrict] +features: [logical-assignment-operators] + +---*/ + +var obj = {}; +Object.defineProperty(obj, "prop", { + value: 2, + writable: false, + enumerable: true, + configurable: true +}); + +assert.sameValue(obj.prop ||= 1, 2, "obj.prop"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-unresolved-lhs.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-unresolved-lhs.js new file mode 100644 index 0000000000..5c66a54d37 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-unresolved-lhs.js @@ -0,0 +1,16 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + ReferenceError is thrown if the LeftHandSideExpression of a Logical + Assignment operator(||=) evaluates to an unresolvable reference +flags: [onlyStrict] +features: [logical-assignment-operators] + +---*/ + +assert.throws(ReferenceError, function() { + unresolved ||= 1; +}); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-unresolved-rhs-put.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-unresolved-rhs-put.js new file mode 100644 index 0000000000..ee291919b2 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-unresolved-rhs-put.js @@ -0,0 +1,19 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + ReferenceError is thrown if the AssignmentExpression of a Logical + Assignment operator(||=) evaluates to an unresolvable reference and the + AssignmentExpression is evaluated. +features: [logical-assignment-operators] + +---*/ + +var value = 0; + +assert.throws(ReferenceError, function() { + value ||= unresolved; +}); +assert.sameValue(value, 0, "value"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-unresolved-rhs.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-unresolved-rhs.js new file mode 100644 index 0000000000..653ac81d9b --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator-unresolved-rhs.js @@ -0,0 +1,16 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: > + ReferenceError is not thrown if the AssignmentExpression of a Logical + Assignment operator(||=) evaluates to an unresolvable reference and the + AssignmentExpression is not evaluated. +features: [logical-assignment-operators] + +---*/ + +var value = 2; + +assert.sameValue(value ||= unresolved, 2, "value"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator.js new file mode 100644 index 0000000000..3a0648f8e9 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-assignment-operator.js @@ -0,0 +1,61 @@ +// Copyright (c) 2020 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-runtime-semantics-evaluation +description: Logical Or Assignment Operator +info: | + AssignmentExpression: + LeftHandSideExpression ||= AssignmentExpression + + 1. Let lref be the result of evaluating LeftHandSideExpression. + 2. Let lval be ? GetValue(lref). + 3. Let lbool be ! ToBoolean(lval). + 4. If lbool is true, return lval. + 5. Let rref be the result of evaluating AssignmentExpression. + 6. Let rval be ? GetValue(rref). + 7. Perform ? PutValue(lref, rval). + 8. Return rval. +features: [logical-assignment-operators] + +---*/ + +var value = undefined; +assert.sameValue(value ||= 1, 1, "(value ||= 1) === 1; where value = undefined"); + +value = null; +assert.sameValue(value ||= 1, 1, "(value ||= 1) === 1; where value = null"); + +value = false; +assert.sameValue(value ||= 1, 1, "(value ||= 1) === 1; where value = false"); + +value = 0; +assert.sameValue(value ||= 1, 1, "(value ||= 1) === 1; where value = 0"); + +value = -0; +assert.sameValue(value ||= 1, 1, "(value ||= 1) === 1; where value = -0"); + +value = NaN; +assert.sameValue(value ||= 1, 1, "(value ||= 1) === 1; where value = NaN"); + +value = ""; +assert.sameValue(value ||= 1, 1, '(value ||= 1) === 1; where value = ""'); + + + +value = true; +assert.sameValue(value ||= 1, true, "(value ||= 1) === true; where value = true"); + +value = 2; +assert.sameValue(value ||= 1, 2, "(value ||= 1) === 2; where value = 2"); + +value = "test"; +assert.sameValue(value ||= 1, "test", '(value ||= 1) === "test"; where value = "test"'); + +var sym = Symbol(""); +value = sym; +assert.sameValue(value ||= 1, sym, "(value ||= 1) === Symbol(); where value = Symbol()"); + +var obj = {}; +value = obj; +assert.sameValue(value ||= 1, obj, "(value ||= 1) === {}; where value = {}"); diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-eval-strict.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-eval-strict.js new file mode 100644 index 0000000000..1d3a9e2347 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-eval-strict.js @@ -0,0 +1,17 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators +description: > + Strict Mode - SyntaxError is thrown if the identifier eval appear + as the LeftHandSideExpression of a Logical Assignment operator(||=) +flags: [onlyStrict] +negative: + phase: parse + type: SyntaxError +features: [logical-assignment-operators] +---*/ +$DONOTEVALUATE(); + +eval ||= 20; diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-non-simple.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-non-simple.js new file mode 100644 index 0000000000..90639520ec --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-non-simple.js @@ -0,0 +1,17 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-assignment-operators-static-semantics-early-errors +info: | + It is an early Syntax Error if AssignmentTargetType of + LeftHandSideExpression is invalid or strict. +description: Logical "||=" assignment with non-simple target +negative: + phase: parse + type: SyntaxError +features: [logical-assignment-operators] +---*/ +$DONOTEVALUATE(); + +1 ||= 1; diff --git a/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-whitespace.js b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-whitespace.js new file mode 100644 index 0000000000..bae1526633 --- /dev/null +++ b/Jint.Tests.Test262/test/language/expressions/logical-assignment/lgcl-or-whitespace.js @@ -0,0 +1,58 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +info: | + White Space and Line Terminator between LeftHandSideExpression and "@=" + or between "@=" and AssignmentExpression are allowed +esid: sec-assignment-operators +description: Checking by evaluating expression "x[...]||=[...]y" +features: [logical-assignment-operators] +---*/ +var x; + +x = 0; +assert.sameValue(x ||= 1, 1, 'U+0009 (expression)'); +assert.sameValue(x, 1, 'U+0009 (side effect)'); + +x = 0; +assert.sameValue(x ||= 1, 1, 'U+000B (expression)'); +assert.sameValue(x, 1, 'U+000B (side effect)'); + +x = 0; +assert.sameValue(x ||= 1, 1, 'U+000C (expression)'); +assert.sameValue(x, 1, 'U+000C (side effect)'); + +x = 0; +assert.sameValue(x ||= 1, 1, 'U+0020 (expression)'); +assert.sameValue(x, 1, 'U+0020 (side effect)'); + +x = 0; +assert.sameValue(x ||= 1, 1, 'U+00A0 (expression)'); +assert.sameValue(x, 1, 'U+00A0 (side effect)'); + +x = 0; +assert.sameValue(x +||= +1, 1, 'U+000A (expression)'); +assert.sameValue(x, 1, 'U+000A (side effect)'); + +x = 0; +assert.sameValue(x +||= +1, 1, 'U+000D (expression)'); +assert.sameValue(x, 1, 'U+000D (side effect)'); + +x = 0; +assert.sameValue(x
||=
1, 1, 'U+2028 (expression)'); +assert.sameValue(x, 1, 'U+2028 (side effect)'); + +x = 0; +assert.sameValue(x
||=
1, 1, 'U+2029 (expression)'); +assert.sameValue(x, 1, 'U+2029 (side effect)'); + +x = 0; +assert.sameValue(x   +

||=   +

1, 1, 'U+0009U+000BU+000CU+0020U+00A0U+000AU+000DU+2028U+2029 (expression)'); +assert.sameValue(x, 1, 'U+0009U+000BU+000CU+0020U+00A0U+000AU+000DU+2028U+2029 (side effect)'); diff --git a/Jint.Tests/Runtime/EngineTests.cs b/Jint.Tests/Runtime/EngineTests.cs index 78fa54f258..b36c055af4 100644 --- a/Jint.Tests/Runtime/EngineTests.cs +++ b/Jint.Tests/Runtime/EngineTests.cs @@ -1003,7 +1003,7 @@ public void ShouldLimitRecursionWithGetters() { const string code = @"var obj = { get test() { return this.test + '2'; } }; obj.test;"; var engine = new Engine(cfg => cfg.LimitRecursion(10)); - + Assert.Throws(() => engine.Evaluate(code)); } @@ -1513,15 +1513,6 @@ public void ShouldExecuteDromaeoBase64() RunTest(content); } - [Fact] - public void ShouldExecuteKnockoutWithErrorWhenIntolerant() - { - var content = GetEmbeddedFile("knockout-3.4.0.js"); - - var ex = Assert.Throws(() => _engine.Execute(content, new ParserOptions { Tolerant = false })); - Assert.Contains("Duplicate __proto__ fields are not allowed in object literals", ex.Message); - } - [Fact] public void ShouldExecuteKnockoutWithoutErrorWhenTolerant() { @@ -2101,7 +2092,7 @@ public void LocaleNumberShouldUseLocalCulture() engine.Evaluate("var d = new Number(-1.23);"); engine.Evaluate("equal('-1.23', d.toString());"); - + // NET 5 globalization APIs use ICU libraries on newer Windows 10 giving different result // build server is older Windows... engine.Evaluate("assert('-1,230' === d.toLocaleString() || '-1,23' === d.toLocaleString());"); diff --git a/Jint/EsprimaExtensions.cs b/Jint/EsprimaExtensions.cs index ec90c12818..ebe0ffbde6 100644 --- a/Jint/EsprimaExtensions.cs +++ b/Jint/EsprimaExtensions.cs @@ -59,13 +59,51 @@ or Nodes.UnaryExpression } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool IsFunctionWithName(this T node) where T : Node + internal static bool IsFunctionDefinition(this T node) where T : Node { var type = node.Type; - return type == Nodes.FunctionExpression - || type == Nodes.ArrowFunctionExpression - || type == Nodes.ArrowParameterPlaceHolder - || type == Nodes.ClassExpression; + return type + is Nodes.FunctionExpression + or Nodes.ArrowFunctionExpression + or Nodes.ArrowParameterPlaceHolder + or Nodes.ClassExpression; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool HasName(this T node) where T : Node + { + if (!node.IsFunctionDefinition()) + { + return false; + } + + if ((node as IFunction)?.Id is not null) + { + return true; + } + + if ((node as ClassExpression)?.Id is not null) + { + return true; + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool IsAnonymousFunctionDefinition(this T node) where T : Node + { + if (!node.IsFunctionDefinition()) + { + return false; + } + + if (node.HasName()) + { + return false; + } + + return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Jint/Jint.csproj b/Jint/Jint.csproj index 2b79227a79..fcab917006 100644 --- a/Jint/Jint.csproj +++ b/Jint/Jint.csproj @@ -8,7 +8,7 @@ true - + diff --git a/Jint/Runtime/Environments/FunctionEnvironmentRecord.cs b/Jint/Runtime/Environments/FunctionEnvironmentRecord.cs index 7410e0e7a3..8826881f06 100644 --- a/Jint/Runtime/Environments/FunctionEnvironmentRecord.cs +++ b/Jint/Runtime/Environments/FunctionEnvironmentRecord.cs @@ -344,7 +344,7 @@ private void HandleAssignmentPatternOrExpression( _engine.LeaveExecutionContext(); } - if (idLeft != null && right.IsFunctionWithName()) + if (idLeft != null && right.IsFunctionDefinition()) { ((FunctionInstance) argument).SetFunctionName(idLeft.Name); } diff --git a/Jint/Runtime/Interpreter/Expressions/BindingPatternAssignmentExpression.cs b/Jint/Runtime/Interpreter/Expressions/BindingPatternAssignmentExpression.cs index 1925e48197..e9c48fc836 100644 --- a/Jint/Runtime/Interpreter/Expressions/BindingPatternAssignmentExpression.cs +++ b/Jint/Runtime/Interpreter/Expressions/BindingPatternAssignmentExpression.cs @@ -235,7 +235,7 @@ private static void HandleArrayPattern(Engine engine, ArrayPattern pattern, JsVa if (assignmentPattern.Left is Identifier leftIdentifier) { - if (assignmentPattern.Right.IsFunctionWithName()) + if (assignmentPattern.Right.IsFunctionDefinition()) { ((FunctionInstance) value).SetFunctionName(new JsString(leftIdentifier.Name)); } @@ -312,7 +312,7 @@ private static void HandleObjectPattern(Engine engine, ObjectPattern pattern, Js var target = assignmentPattern.Left as Identifier ?? identifier; - if (assignmentPattern.Right.IsFunctionWithName()) + if (assignmentPattern.Right.IsFunctionDefinition()) { ((FunctionInstance) value).SetFunctionName(target.Name); } diff --git a/Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs b/Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs index d42f4070d8..c5535da00d 100644 --- a/Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs +++ b/Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs @@ -42,7 +42,6 @@ protected override object EvaluateInternal() ExceptionHelper.ThrowReferenceError(_engine.Realm, "not a valid reference"); } - var rval = _right.GetValue(); var lval = _engine.GetValue(lref, false); var handledByOverload = false; @@ -90,11 +89,14 @@ protected override object EvaluateInternal() break; } - if (operatorClrName != null && - JintBinaryExpression.TryOperatorOverloading(_engine, lval, rval, operatorClrName, out var result)) + if (operatorClrName != null) { - lval = JsValue.FromObject(_engine, result); - handledByOverload = true; + var rval = _right.GetValue(); + if (JintBinaryExpression.TryOperatorOverloading(_engine, lval, rval, operatorClrName, out var result)) + { + lval = JsValue.FromObject(_engine, result); + handledByOverload = true; + } } } @@ -103,6 +105,8 @@ protected override object EvaluateInternal() switch (_operator) { case AssignmentOperator.PlusAssign: + { + var rval = _right.GetValue(); if (AreIntegerOperands(lval, rval)) { lval = (long) lval.AsInteger() + rval.AsInteger(); @@ -128,14 +132,20 @@ protected override object EvaluateInternal() } break; + } case AssignmentOperator.MinusAssign: + { + var rval = _right.GetValue(); lval = AreIntegerOperands(lval, rval) ? JsNumber.Create(lval.AsInteger() - rval.AsInteger()) : JsNumber.Create(TypeConverter.ToNumber(lval) - TypeConverter.ToNumber(rval)); break; + } case AssignmentOperator.TimesAssign: + { + var rval = _right.GetValue(); if (AreIntegerOperands(lval, rval)) { lval = (long) lval.AsInteger() * rval.AsInteger(); @@ -150,12 +160,18 @@ protected override object EvaluateInternal() } break; + } case AssignmentOperator.DivideAssign: + { + var rval = _right.GetValue(); lval = Divide(lval, rval); break; + } case AssignmentOperator.ModuloAssign: + { + var rval = _right.GetValue(); if (lval.IsUndefined() || rval.IsUndefined()) { lval = Undefined.Instance; @@ -166,30 +182,85 @@ protected override object EvaluateInternal() } break; + } case AssignmentOperator.BitwiseAndAssign: + { + var rval = _right.GetValue(); lval = TypeConverter.ToInt32(lval) & TypeConverter.ToInt32(rval); break; + } case AssignmentOperator.BitwiseOrAssign: + { + var rval = _right.GetValue(); lval = TypeConverter.ToInt32(lval) | TypeConverter.ToInt32(rval); break; + } case AssignmentOperator.BitwiseXOrAssign: + { + var rval = _right.GetValue(); lval = TypeConverter.ToInt32(lval) ^ TypeConverter.ToInt32(rval); break; + } case AssignmentOperator.LeftShiftAssign: + { + var rval = _right.GetValue(); lval = TypeConverter.ToInt32(lval) << (int) (TypeConverter.ToUint32(rval) & 0x1F); break; + } case AssignmentOperator.RightShiftAssign: + { + var rval = _right.GetValue(); lval = TypeConverter.ToInt32(lval) >> (int) (TypeConverter.ToUint32(rval) & 0x1F); break; + } case AssignmentOperator.UnsignedRightShiftAssign: + { + var rval = _right.GetValue(); lval = (uint) TypeConverter.ToInt32(lval) >> (int) (TypeConverter.ToUint32(rval) & 0x1F); break; + } + + case AssignmentOperator.NullishAssign: + { + if (!lval.IsNullOrUndefined()) + { + return lval; + } + + var rval = NamedEvaluation(_right); + lval = rval; + break; + } + + case AssignmentOperator.AndAssign: + { + if (!TypeConverter.ToBoolean(lval)) + { + return lval; + } + + var rval = NamedEvaluation(_right); + lval = rval; + break; + } + + case AssignmentOperator.OrAssign: + { + if (TypeConverter.ToBoolean(lval)) + { + return lval; + } + + var rval = NamedEvaluation(_right); + lval = rval; + break; + } default: ExceptionHelper.ThrowNotImplementedException(); @@ -203,6 +274,17 @@ protected override object EvaluateInternal() return lval; } + private JsValue NamedEvaluation(JintExpression expression) + { + var rval = expression.GetValue(); + if (expression._expression.IsAnonymousFunctionDefinition() && _left._expression.Type == Nodes.Identifier) + { + ((FunctionInstance) rval).SetFunctionName(((Identifier) _left._expression).Name); + } + + return rval; + } + internal sealed class SimpleAssignmentExpression : JintExpression { private JintExpression _left; @@ -276,7 +358,7 @@ internal static JsValue AssignToIdentifier( var rval = right.GetValue().Clone(); - if (right._expression.IsFunctionWithName()) + if (right._expression.IsFunctionDefinition()) { ((FunctionInstance) rval).SetFunctionName(left._expressionName.StringValue); } diff --git a/Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs b/Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs index 6395139ad1..1a4cec7f4d 100644 --- a/Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs +++ b/Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs @@ -76,7 +76,7 @@ protected override void Initialize() { var propertyValue = p.Value; _valueExpressions[i] = Build(_engine, propertyValue); - _canBuildFast &= !propertyValue.IsFunctionWithName(); + _canBuildFast &= !propertyValue.IsFunctionDefinition(); } else { @@ -166,7 +166,7 @@ private object BuildObjectNormal() { var expr = _valueExpressions[i]; JsValue propValue = expr.GetValue().Clone(); - if (expr._expression.IsFunctionWithName()) + if (expr._expression.IsFunctionDefinition()) { var closure = (FunctionInstance) propValue; closure.SetFunctionName(propName); diff --git a/Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs b/Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs index b35ff32b8d..4ff2ba8981 100644 --- a/Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs +++ b/Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs @@ -45,12 +45,12 @@ protected override void Initialize() { left = JintExpression.Build(_engine, declaration.Id); } - + if (declaration.Init != null) { init = JintExpression.Build(_engine, declaration.Init); } - + var leftIdentifier = left as JintIdentifierExpression; _declarations[i] = new ResolvedDeclaration { @@ -70,7 +70,7 @@ protected override Completion ExecuteInternal() _initialized = true; Initialize(); } - + foreach (var declaration in _declarations) { if (_statement.Kind != VariableDeclarationKind.Var && declaration.Left != null) @@ -80,7 +80,7 @@ protected override Completion ExecuteInternal() if (declaration.Init != null) { value = declaration.Init.GetValue().Clone(); - if (declaration.Init._expression.IsFunctionWithName()) + if (declaration.Init._expression.IsFunctionDefinition()) { ((FunctionInstance) value).SetFunctionName(lhs.GetReferencedName()); } @@ -117,7 +117,7 @@ protected override Completion ExecuteInternal() var value = declaration.Init.GetValue().Clone(); - if (declaration.Init._expression.IsFunctionWithName()) + if (declaration.Init._expression.IsFunctionDefinition()) { ((FunctionInstance) value).SetFunctionName(lhs.GetReferencedName()); }