Implement optional chaining (#5164)

The work is based on PR #4843, only fixed some conflicts and cppcheck errors.

Co-authored-by: Robert Fancsik robert.fancsik@h-lab.eu
JerryScript-DCO-1.0-Signed-off-by: Gergo Csizi gergocs@inf.u-szeged.hu
This commit is contained in:
Gergo Csizi
2024-11-20 11:57:58 +01:00
committed by GitHub
parent e9f08a7879
commit f54f2d3a7b
14 changed files with 1012 additions and 412 deletions
+167
View File
@@ -0,0 +1,167 @@
// Copyright JS Foundation and other contributors, http://js.foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
function expectSyntaxError(str) {
try {
eval(str);
assert(false);
} catch (e) {
assert(e instanceof SyntaxError);
}
}
function expectTypeError(cb) {
try {
cb();
assert(false);
} catch (e) {
assert(e instanceof TypeError);
}
}
expectSyntaxError("this?.a``");
expectSyntaxError("this?.['a']``");
expectSyntaxError("this?.``");
expectSyntaxError("this.a?.``");
expectSyntaxError("this['a']?.``");
expectSyntaxError("this?.a = 9");
expectSyntaxError("this.a.a.a.a.a?.a = 9");
expectSyntaxError("this?.a.a.a.a.a.a = 9");
expectSyntaxError("this?.a++");
expectSyntaxError("this?.a.a.a.a.a.a++");
expectSyntaxError("this.a.a.a.a.?a.a++");
expectSyntaxError("this?.a--");
expectSyntaxError("this?.a.a.a.a.a.a--");
expectSyntaxError("this.a.a.a.a.?a.a--");
var o = {
a: 4.1,
b() {
return 4.2;
},
c: {
a: 4.3,
b() {
return 4.4
}
},
e(...args) {
return args.reduce((p, c) => p + c);
}
}
assert(o?.a === 4.1);
assert(o?.a2 === undefined);
assert(this.o?.a === 4.1);
assert(this.o?.a2 === undefined);
assert(this.o?.['a'] === 4.1);
assert(this.o?.['a2'] === undefined);
assert(typeof o?.a === 'number');
assert(typeof o?.a2 === 'undefined');
assert(o?.c?.a === 4.3);
assert(o?.c?.a2 === undefined);
assert(this.o?.c?.a === 4.3);
assert(this.o?.c?.a2 === undefined);
assert(this.o?.c?.['a'] === 4.3);
assert(this.o?.c?.['a2'] === undefined);
assert(typeof o?.c?.a === 'number');
assert(typeof o?.c?.a2 === 'undefined');
assert(o?.d === undefined);
assert(o?.d?.d === undefined);
assert(o?.d?.d?.['d'] === undefined);
assert(o?.b?.() === 4.2);
assert(o?.b2?.() === undefined);
assert(o?.c?.b?.() === 4.4);
assert(o?.c?.b2?.() === undefined);
assert(o?.e(...[1.25, 2.25, 3.25]) === 6.75);
assert(o?.e(...[1.25, 2.25, 3.25], ...[0.25]) === 7);
assert(o?.['e'](...[1.25, 2.25, 3.25]) === 6.75);
assert(o?.['e'](...[1.25, 2.25, 3.25], ...[0.25]) === 7);
assert(o?.e?.(...[1.25, 2.25, 3.25]) === 6.75);
assert(o?.e?.(...[1.25, 2.25, 3.25], ...[0.25]) === 7);
assert(o?.['e']?.(...[1.25, 2.25, 3.25]) === 6.75);
assert(o?.['e']?.(...[1.25, 2.25, 3.25], ...[0.25]) === 7);
// Test short circuit
let count = 0;
assert(undefined?.[count++] === undefined);
assert(undefined?.[count++]() === undefined);
assert(undefined?.[count++]()() === undefined);
assert(null?.[count++] === undefined);
assert(null?.[count++]() === undefined);
assert(null?.[count++]()() === undefined);
assert(count === 0);
// Test optional call
var g = undefined;
function f () {
return 4.5;
}
assert(g?.() === undefined);
assert(this?.g?.() === undefined);
assert(this.g?.() === undefined);
expectTypeError(_ => {
this.g();
});
assert(f?.() === 4.5);
assert(this?.f?.() === 4.5);
assert(this.f?.() === 4.5);
assert(f() === 4.5);
// test direct eval
var a = 5.1;
eval('a = 5.2');
assert(a === 5.2);
eval?.('a = 5.3');
assert(a === 5.3);
eval?.(...['a = 5.4']);
assert(a === 5.4);
const saved_eval = eval;
eval = undefined;
eval?.('a = 5.5')
assert(a === 5.4);
eval?.(...['a = 5.5'])
assert(a === 5.4);
eval = saved_eval;
// test optional private property access
class A {
#a = 5.1;
test(o) {
return o?.#a;
}
}
let instance = new A;
assert(instance.test(instance) === 5.1);
assert(instance.test(undefined) === undefined);
assert(instance.test(null) === undefined);
expectTypeError(_ => {
instance.test({});
});
-35
View File
@@ -2003,16 +2003,12 @@
<test id="language/expressions/class/elements/async-gen-private-method/yield-promise-reject-next.js"><reason></reason></test>
<test id="language/expressions/class/elements/class-name-static-initializer-anonymous.js"><reason></reason></test>
<test id="language/expressions/class/elements/class-name-static-initializer-default-export.js"><reason></reason></test>
<test id="language/expressions/class/elements/grammar-private-field-optional-chaining.js"><reason></reason></test>
<test id="language/expressions/class/elements/private-field-after-optional-chain.js"><reason></reason></test>
<test id="language/statements/class/elements/async-gen-private-method-static/yield-promise-reject-next-catch.js"><reason></reason></test>
<test id="language/statements/class/elements/async-gen-private-method-static/yield-promise-reject-next-for-await-of-async-iterator.js"><reason></reason></test>
<test id="language/statements/class/elements/async-gen-private-method-static/yield-promise-reject-next.js"><reason></reason></test>
<test id="language/statements/class/elements/async-gen-private-method/yield-promise-reject-next-catch.js"><reason></reason></test>
<test id="language/statements/class/elements/async-gen-private-method/yield-promise-reject-next-for-await-of-async-iterator.js"><reason></reason></test>
<test id="language/statements/class/elements/async-gen-private-method/yield-promise-reject-next.js"><reason></reason></test>
<test id="language/statements/class/elements/grammar-private-field-optional-chaining.js"><reason></reason></test>
<test id="language/statements/class/elements/private-field-after-optional-chain.js"><reason></reason></test>
<!-- END - ESNext stage 3 proposals: class fields, private methods and static class features -->
<!-- ESNext stage 3 proposal: top level await
@@ -2512,37 +2508,6 @@
<test id="language/statements/while/tco-body.js"><reason></reason></test>
<!-- END - ES2015: Proper Tail Call (PTC) Optimization -->
<!-- ES2020: Optional Chaining
features: [optional-chaining]
https://github.com/tc39/proposal-optional-chaining
-->
<test id="language/expressions/optional-chaining/call-expression.js"><reason></reason></test>
<test id="language/expressions/optional-chaining/eval-optional-call.js"><reason></reason></test>
<test id="language/expressions/optional-chaining/iteration-statement-do.js"><reason></reason></test>
<test id="language/expressions/optional-chaining/iteration-statement-for-await-of.js"><reason></reason></test>
<test id="language/expressions/optional-chaining/iteration-statement-for-in.js"><reason></reason></test>
<test id="language/expressions/optional-chaining/iteration-statement-for-of-type-error.js"><reason></reason></test>
<test id="language/expressions/optional-chaining/iteration-statement-for.js"><reason></reason></test>
<test id="language/expressions/optional-chaining/iteration-statement-while.js"><reason></reason></test>
<test id="language/expressions/optional-chaining/member-expression-async-identifier.js"><reason></reason></test>
<test id="language/expressions/optional-chaining/member-expression-async-literal.js"><reason></reason></test>
<test id="language/expressions/optional-chaining/member-expression-async-this.js"><reason></reason></test>
<test id="language/expressions/optional-chaining/member-expression.js"><reason></reason></test>
<test id="language/expressions/optional-chaining/new-target-optional-call.js"><reason></reason></test>
<test id="language/expressions/optional-chaining/optional-call-preserves-this.js"><reason></reason></test>
<test id="language/expressions/optional-chaining/optional-chain-async-optional-chain-square-brackets.js"><reason></reason></test>
<test id="language/expressions/optional-chaining/optional-chain-async-square-brackets.js"><reason></reason></test>
<test id="language/expressions/optional-chaining/optional-chain-expression-optional-expression.js"><reason></reason></test>
<test id="language/expressions/optional-chaining/optional-chain-prod-arguments.js"><reason></reason></test>
<test id="language/expressions/optional-chaining/optional-chain-prod-expression.js"><reason></reason></test>
<test id="language/expressions/optional-chaining/optional-chain-prod-identifiername.js"><reason></reason></test>
<test id="language/expressions/optional-chaining/optional-chain.js"><reason></reason></test>
<test id="language/expressions/optional-chaining/optional-expression.js"><reason></reason></test>
<test id="language/expressions/optional-chaining/runtime-semantics-evaluation.js"><reason></reason></test>
<test id="language/expressions/optional-chaining/short-circuiting.js"><reason></reason></test>
<test id="language/expressions/optional-chaining/super-property-optional-call.js"><reason></reason></test>
<!-- END - ES2020: Optional Chaining -->
<!-- ES2017: Shared Memory and Atomics
features: [Atomics]
https://github.com/tc39/ecmascript_sharedmem