From ffdc3a5c0f1a47f8b15a7e7c8c72fe7e0acc3662 Mon Sep 17 00:00:00 2001 From: Grzegorz Caban Date: Thu, 29 Aug 2024 21:42:31 +0100 Subject: [PATCH 1/6] optional chaining operator by transforming into conditional expression in IRFactory --- .../org/mozilla/javascript/IRFactory.java | 15 ++++++ .../java/org/mozilla/javascript/Parser.java | 10 ++++ .../java/org/mozilla/javascript/Token.java | 5 +- .../org/mozilla/javascript/TokenStream.java | 3 ++ .../tests/OptionalChainingOperatorTests.java | 49 +++++++++++++++++++ 5 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 rhino/src/test/java/org/mozilla/javascript/tests/OptionalChainingOperatorTests.java diff --git a/rhino/src/main/java/org/mozilla/javascript/IRFactory.java b/rhino/src/main/java/org/mozilla/javascript/IRFactory.java index 5464161e20..285009184e 100644 --- a/rhino/src/main/java/org/mozilla/javascript/IRFactory.java +++ b/rhino/src/main/java/org/mozilla/javascript/IRFactory.java @@ -2038,6 +2038,21 @@ private static Node createBinary(int nodeType, Node left, Node right) { } break; } + case Token.DOT_QUESTION: + { + // a?.b == (a == null || a == undefined) ? undefined : a.b + Node undefinedNode = new Name(0, "undefined"); + Node nullNode = new Node(Token.NULL); + + Node conditional = + new Node( + Token.OR, + new Node(Token.SHEQ, nullNode, left), + new Node(Token.SHEQ, undefinedNode, left)); + + Node node = new Node(Token.HOOK, conditional, new Name(0, "undefined"), right); + return node; + } } return new Node(nodeType, left, right); diff --git a/rhino/src/main/java/org/mozilla/javascript/Parser.java b/rhino/src/main/java/org/mozilla/javascript/Parser.java index 1d8a75c127..080177f655 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Parser.java +++ b/rhino/src/main/java/org/mozilla/javascript/Parser.java @@ -2776,6 +2776,7 @@ private AstNode memberExprTail(boolean allowCallSyntax, AstNode pn) throws IOExc int tt = peekToken(); switch (tt) { case Token.DOT: + case Token.DOT_QUESTION: case Token.DOTDOT: lineno = ts.lineno; pn = propertyAccess(tt, pn); @@ -2963,6 +2964,15 @@ private AstNode propertyAccess(int tt, AstNode pn) throws IOException { result.setLineno(pn.getLineno()); result.setLeft(pn); // do this after setting position result.setRight(ref); + + if (tt == Token.DOT_QUESTION && result instanceof PropertyGet) { + result = + new InfixExpression( + Token.DOT_QUESTION, + ((PropertyGet) result).getTarget(), + result, + result.lineno); + } return result; } diff --git a/rhino/src/main/java/org/mozilla/javascript/Token.java b/rhino/src/main/java/org/mozilla/javascript/Token.java index eb85b876ac..32e93554de 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Token.java +++ b/rhino/src/main/java/org/mozilla/javascript/Token.java @@ -227,7 +227,8 @@ public static enum CommentType { TEMPLATE_LITERAL_SUBST = 173, // template literal - substitution TAGGED_TEMPLATE_LITERAL = 174, // template literal - tagged/handler DOTDOTDOT = 175, // spread/rest ... - LAST_TOKEN = 175; + DOT_QUESTION = 176, + LAST_TOKEN = 177; /** * Returns a name for the token. If Rhino is compiled with certain hardcoded debugging flags in @@ -598,6 +599,8 @@ public static String typeToName(int token) { return "TEMPLATE_LITERAL_SUBST"; case TAGGED_TEMPLATE_LITERAL: return "TAGGED_TEMPLATE_LITERAL"; + case DOT_QUESTION: + return "DOT_QUESTION"; } // Token without name diff --git a/rhino/src/main/java/org/mozilla/javascript/TokenStream.java b/rhino/src/main/java/org/mozilla/javascript/TokenStream.java index 4c8fc39fbf..2f83439f2a 100644 --- a/rhino/src/main/java/org/mozilla/javascript/TokenStream.java +++ b/rhino/src/main/java/org/mozilla/javascript/TokenStream.java @@ -1146,6 +1146,9 @@ && peekChar() == '!' case ',': return Token.COMMA; case '?': + if (matchChar('.')) { + return Token.DOT_QUESTION; + } return Token.HOOK; case ':': if (matchChar(':')) { diff --git a/rhino/src/test/java/org/mozilla/javascript/tests/OptionalChainingOperatorTests.java b/rhino/src/test/java/org/mozilla/javascript/tests/OptionalChainingOperatorTests.java new file mode 100644 index 0000000000..deefeccbfd --- /dev/null +++ b/rhino/src/test/java/org/mozilla/javascript/tests/OptionalChainingOperatorTests.java @@ -0,0 +1,49 @@ +package org.mozilla.javascript.tests; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.Undefined; + +public class OptionalChainingOperatorTests { + + @Test + public void testOptionalChainingOperator() { + + Utils.runWithAllOptimizationLevels( + cx -> { + String sourceName = "optionalChainingOperator"; + Scriptable scope = cx.initStandardObjects(); + + String script = " var a = {name: 'val'}; a.outerProp?.innerProp"; + assertEquals( + Undefined.instance, + cx.evaluateString(scope, script, sourceName, 1, null)); + + String script2 = + " var a = {outerProp: {innerProp: 'val' } }; a.outerProp?.innerProp"; + assertEquals("val", cx.evaluateString(scope, script2, sourceName, 1, null)); + + String script3 = + " var a = {outerProp: {innerProp: { innerInnerProp: {name: 'val' } } } }; a.outerProp?.innerProp?.missingProp?.name"; + assertEquals( + Undefined.instance, + cx.evaluateString(scope, script3, sourceName, 1, null)); + + String script4 = + " var a = {outerProp: {innerProp: { innerInnerProp: {name: 'val' } } } }; a.outerProp?.innerProp?.innerInnerProp?.name"; + assertEquals("val", cx.evaluateString(scope, script4, sourceName, 1, null)); + + // NOT WORKING YET + // String script5 = " var a = {}; + // a.someNonExistentMethod?.()"; + // assertEquals( + // Undefined.instance, + // cx.evaluateString(scope, script5, sourceName, 1, + // null)); + + return null; + }); + } +} From 8227258ca7c9f67726ddc7fab91a28f2eb635d1d Mon Sep 17 00:00:00 2001 From: Grzegorz Caban Date: Mon, 2 Sep 2024 12:43:48 +0100 Subject: [PATCH 2/6] handling optional method call (i.e. a.method?.()) in parser. Enabling language/expressions/optional-chaining section in test262 and reworking the solution to pass them, including the necessity to clone ASTNode to avoid problems introduced due to structural sharing betweek left and right of left.?rigth. --- .../org/mozilla/javascript/IRFactory.java | 15 ----- .../java/org/mozilla/javascript/Parser.java | 58 +++++++++++++++---- .../java/org/mozilla/javascript/Token.java | 4 +- .../org/mozilla/javascript/TokenStream.java | 2 +- .../org/mozilla/javascript/ast/AstNode.java | 1 + .../tests/OptionalChainingOperatorTests.java | 56 +++++++++++++++--- tests/testsrc/test262.properties | 28 ++++++++- 7 files changed, 128 insertions(+), 36 deletions(-) diff --git a/rhino/src/main/java/org/mozilla/javascript/IRFactory.java b/rhino/src/main/java/org/mozilla/javascript/IRFactory.java index 285009184e..5464161e20 100644 --- a/rhino/src/main/java/org/mozilla/javascript/IRFactory.java +++ b/rhino/src/main/java/org/mozilla/javascript/IRFactory.java @@ -2038,21 +2038,6 @@ private static Node createBinary(int nodeType, Node left, Node right) { } break; } - case Token.DOT_QUESTION: - { - // a?.b == (a == null || a == undefined) ? undefined : a.b - Node undefinedNode = new Name(0, "undefined"); - Node nullNode = new Node(Token.NULL); - - Node conditional = - new Node( - Token.OR, - new Node(Token.SHEQ, nullNode, left), - new Node(Token.SHEQ, undefinedNode, left)); - - Node node = new Node(Token.HOOK, conditional, new Name(0, "undefined"), right); - return node; - } } return new Node(nodeType, left, right); diff --git a/rhino/src/main/java/org/mozilla/javascript/Parser.java b/rhino/src/main/java/org/mozilla/javascript/Parser.java index 080177f655..e2a564e0c5 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Parser.java +++ b/rhino/src/main/java/org/mozilla/javascript/Parser.java @@ -2776,7 +2776,7 @@ private AstNode memberExprTail(boolean allowCallSyntax, AstNode pn) throws IOExc int tt = peekToken(); switch (tt) { case Token.DOT: - case Token.DOT_QUESTION: + case Token.QUESTION_DOT: case Token.DOTDOT: lineno = ts.lineno; pn = propertyAccess(tt, pn); @@ -2906,8 +2906,27 @@ private AstNode propertyAccess(int tt, AstNode pn) throws IOException { } AstNode ref = null; // right side of . or .. operator - int token = nextToken(); + if (token == Token.LP && tt == Token.QUESTION_DOT) { + // optional chaining operator method call, o.func.?() + var pos = pn.getPosition(); + consumeToken(); + checkCallRequiresActivation(pn); + FunctionCall f = new FunctionCall(pos); + f.setTarget(pn); + // Assign the line number for the function call to where + // the paren appeared, not where the name expression started. + f.setLineno(lineno); + f.setLp(ts.tokenBeg - pos); + List args = argumentList(); + if (args != null && args.size() > ARGC_LIMIT) reportError("msg.too.many.function.args"); + f.setArguments(args); + f.setRp(ts.tokenBeg - pos); + f.setLength(ts.tokenEnd - pos); + + return conditionalPropertyAccess(pn, f); + } + switch (token) { case Token.THROW: // needed for generator.throw(); @@ -2939,7 +2958,6 @@ private AstNode propertyAccess(int tt, AstNode pn) throws IOException { ref = propertyName(-1, memberTypeFlags); break; } - default: if (compilerEnv.isReservedKeywordAsIdentifier()) { // allow keywords as property names, e.g. ({if: 1}) @@ -2965,17 +2983,37 @@ private AstNode propertyAccess(int tt, AstNode pn) throws IOException { result.setLeft(pn); // do this after setting position result.setRight(ref); - if (tt == Token.DOT_QUESTION && result instanceof PropertyGet) { - result = - new InfixExpression( - Token.DOT_QUESTION, - ((PropertyGet) result).getTarget(), - result, - result.lineno); + if (tt == Token.QUESTION_DOT && result instanceof PropertyGet) { + return conditionalPropertyAccess(pn, result); } return result; } + private static AstNode conditionalPropertyAccess(AstNode target, AstNode propAccessNode) { + var targetCopy = cloneNode(target); + var targetCopy2 = cloneNode(target); + + AstNode undefinedNode = new Name(0, "undefined"); + AstNode nullNode = new KeywordLiteral(0, 4, Token.NULL); + + AstNode nullEq = new InfixExpression(Token.SHEQ, targetCopy, nullNode, 0); + + AstNode undefinedEq = new InfixExpression(Token.SHEQ, targetCopy2, undefinedNode, 0); + var conditionalExpr = new ConditionalExpression(); + conditionalExpr.setTestExpression(new InfixExpression(Token.OR, nullEq, undefinedEq, 0)); + conditionalExpr.setTrueExpression(new Name(0, "undefined")); + conditionalExpr.setFalseExpression(propAccessNode); + + return conditionalExpr; + } + + private static AstNode cloneNode(AstNode target) { + var newParser = new Parser(); + var root = newParser.parse(target.toSource(), "", 1); + var targetCopy = ((ExpressionStatement) root.first).getExpression(); + return targetCopy; + } + /** * Xml attribute expression: * diff --git a/rhino/src/main/java/org/mozilla/javascript/Token.java b/rhino/src/main/java/org/mozilla/javascript/Token.java index 32e93554de..dccd1d685e 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Token.java +++ b/rhino/src/main/java/org/mozilla/javascript/Token.java @@ -227,7 +227,7 @@ public static enum CommentType { TEMPLATE_LITERAL_SUBST = 173, // template literal - substitution TAGGED_TEMPLATE_LITERAL = 174, // template literal - tagged/handler DOTDOTDOT = 175, // spread/rest ... - DOT_QUESTION = 176, + QUESTION_DOT = 176, LAST_TOKEN = 177; /** @@ -599,7 +599,7 @@ public static String typeToName(int token) { return "TEMPLATE_LITERAL_SUBST"; case TAGGED_TEMPLATE_LITERAL: return "TAGGED_TEMPLATE_LITERAL"; - case DOT_QUESTION: + case QUESTION_DOT: return "DOT_QUESTION"; } diff --git a/rhino/src/main/java/org/mozilla/javascript/TokenStream.java b/rhino/src/main/java/org/mozilla/javascript/TokenStream.java index 2f83439f2a..7dd1ea414b 100644 --- a/rhino/src/main/java/org/mozilla/javascript/TokenStream.java +++ b/rhino/src/main/java/org/mozilla/javascript/TokenStream.java @@ -1147,7 +1147,7 @@ && peekChar() == '!' return Token.COMMA; case '?': if (matchChar('.')) { - return Token.DOT_QUESTION; + return Token.QUESTION_DOT; } return Token.HOOK; case ':': diff --git a/rhino/src/main/java/org/mozilla/javascript/ast/AstNode.java b/rhino/src/main/java/org/mozilla/javascript/ast/AstNode.java index fa64982e38..54147c3443 100644 --- a/rhino/src/main/java/org/mozilla/javascript/ast/AstNode.java +++ b/rhino/src/main/java/org/mozilla/javascript/ast/AstNode.java @@ -123,6 +123,7 @@ public abstract class AstNode extends Node implements Comparable { operatorNames.put(Token.ASSIGN_BITXOR, "^="); operatorNames.put(Token.ASSIGN_EXP, "**="); operatorNames.put(Token.VOID, "void"); + operatorNames.put(Token.QUESTION_DOT, "?."); StringBuilder sb = new StringBuilder(); INDENTATIONS[0] = sb.toString(); diff --git a/rhino/src/test/java/org/mozilla/javascript/tests/OptionalChainingOperatorTests.java b/rhino/src/test/java/org/mozilla/javascript/tests/OptionalChainingOperatorTests.java index deefeccbfd..b39fa56a44 100644 --- a/rhino/src/test/java/org/mozilla/javascript/tests/OptionalChainingOperatorTests.java +++ b/rhino/src/test/java/org/mozilla/javascript/tests/OptionalChainingOperatorTests.java @@ -15,6 +15,10 @@ public void testOptionalChainingOperator() { cx -> { String sourceName = "optionalChainingOperator"; Scriptable scope = cx.initStandardObjects(); + assertEquals( + Undefined.instance, + cx.evaluateString( + scope, "var nul = null; nul?.a", sourceName, 1, null)); String script = " var a = {name: 'val'}; a.outerProp?.innerProp"; assertEquals( @@ -26,22 +30,60 @@ public void testOptionalChainingOperator() { assertEquals("val", cx.evaluateString(scope, script2, sourceName, 1, null)); String script3 = - " var a = {outerProp: {innerProp: { innerInnerProp: {name: 'val' } } } }; a.outerProp?.innerProp?.missingProp?.name"; + "var a = {outerProp: {innerProp: { innerInnerProp: {name: 'val' } } } }; " + + "a.outerProp?.innerProp?.missingProp?.name"; assertEquals( Undefined.instance, cx.evaluateString(scope, script3, sourceName, 1, null)); String script4 = - " var a = {outerProp: {innerProp: { innerInnerProp: {name: 'val' } } } }; a.outerProp?.innerProp?.innerInnerProp?.name"; + " var a = {outerProp: {innerProp: { innerInnerProp: {name: 'val' } } } }; " + + "a.outerProp?.innerProp?.innerInnerProp?.name"; assertEquals("val", cx.evaluateString(scope, script4, sourceName, 1, null)); - // NOT WORKING YET - // String script5 = " var a = {}; - // a.someNonExistentMethod?.()"; + String script5 = " var a = {}; a.someNonExistentMethod?.()"; + assertEquals( + Undefined.instance, + cx.evaluateString(scope, script5, sourceName, 1, null)); + assertEquals( + Undefined.instance, + cx.evaluateString( + scope, + "function fn3 () {\n" + + " return () => {\n" + + " return null;\n" + + " };\n" + + "}" + + " fn3()()?.a", + sourceName, + 1, + null)); + assertEquals( + Undefined.instance, + cx.evaluateString( + scope, + " var a = {}; a.someNonExistentMethod?.()", + sourceName, + 1, + null)); + + // NOT WORKING + // assertEquals( + // Undefined.instance, + // cx.evaluateString( + // scope, + // "{}?.a", + // sourceName, + // 1, + // null)); // assertEquals( // Undefined.instance, - // cx.evaluateString(scope, script5, sourceName, 1, - // null)); + // cx.evaluateString( + // scope, + // "var b = {}; for (const key of b.a) ", + // sourceName, + // 1, + // null)); return null; }); diff --git a/tests/testsrc/test262.properties b/tests/testsrc/test262.properties index bda92a6188..baa439151e 100644 --- a/tests/testsrc/test262.properties +++ b/tests/testsrc/test262.properties @@ -6150,7 +6150,33 @@ language/expressions/object 866/1169 (74.08%) yield-non-strict-access.js non-strict yield-non-strict-syntax.js non-strict -~language/expressions/optional-chaining +language/expressions/optional-chaining 26/38 (68.42%) + call-expression.js + early-errors-tail-position-null-optchain-template-string.js + early-errors-tail-position-null-optchain-template-string-esi.js + early-errors-tail-position-optchain-template-string.js + early-errors-tail-position-optchain-template-string-esi.js + eval-optional-call.js + iteration-statement-for.js + iteration-statement-for-await-of.js {unsupported: [async]} + iteration-statement-for-in.js + iteration-statement-for-of-type-error.js + member-expression.js + member-expression-async-identifier.js {unsupported: [async]} + member-expression-async-literal.js {unsupported: [async]} + member-expression-async-this.js {unsupported: [async]} + new-target-optional-call.js + optional-call-preserves-this.js + optional-chain.js + optional-chain-async-optional-chain-square-brackets.js {unsupported: [async]} + optional-chain-async-square-brackets.js {unsupported: [async]} + optional-chain-expression-optional-expression.js + optional-chain-prod-arguments.js + optional-chain-prod-expression.js + punctuator-decimal-lookahead.js + short-circuiting.js + static-semantics-simple-assignment.js + super-property-optional-call.js language/expressions/postfix-decrement 9/37 (24.32%) arguments.js strict From e1727ceeb40cff9fa151cfe7d5510ef6343976d2 Mon Sep 17 00:00:00 2001 From: nabacg Date: Thu, 5 Sep 2024 18:19:04 +0100 Subject: [PATCH 3/6] adding few new rhino ByteCodes to handle Optional property get and optional method call for `?.` operator --- .../org/mozilla/javascript/CodeGenerator.java | 12 ++ .../org/mozilla/javascript/IRFactory.java | 29 ++- .../java/org/mozilla/javascript/Icode.java | 6 +- .../org/mozilla/javascript/Interpreter.java | 24 +++ .../java/org/mozilla/javascript/Parser.java | 9 +- .../org/mozilla/javascript/ScriptRuntime.java | 22 ++ .../java/org/mozilla/javascript/Token.java | 199 +++++++++--------- .../javascript/optimizer/BodyCodegen.java | 54 ++++- .../javascript/optimizer/OptRuntime.java | 8 + tests/testsrc/test262.properties | 3 +- 10 files changed, 249 insertions(+), 117 deletions(-) diff --git a/rhino/src/main/java/org/mozilla/javascript/CodeGenerator.java b/rhino/src/main/java/org/mozilla/javascript/CodeGenerator.java index 8abe18f550..d24c68a4a8 100644 --- a/rhino/src/main/java/org/mozilla/javascript/CodeGenerator.java +++ b/rhino/src/main/java/org/mozilla/javascript/CodeGenerator.java @@ -602,6 +602,7 @@ private void visitExpression(Node node, int contextFlags) { case Token.REF_CALL: case Token.CALL: + case Token.CALL_OPTIONAL: case Token.NEW: { if (type == Token.NEW) { @@ -686,6 +687,11 @@ private void visitExpression(Node node, int contextFlags) { resolveForwardGoto(afterElseJumpStart); } break; + case Token.GETPROP_OPTIONAL: + visitExpression(child, 0); + child = child.getNext(); + addStringOp(type, child.getString()); + break; case Token.GETPROP: case Token.GETPROPNOWARN: @@ -1057,6 +1063,7 @@ private void generateCallFunAndThis(Node left) { stackChange(2); break; } + case Token.GETPROP_OPTIONAL: case Token.GETPROP: case Token.GETELEM: { @@ -1068,6 +1075,11 @@ private void generateCallFunAndThis(Node left) { // stack: ... target -> ... function thisObj addStringOp(Icode_PROP_AND_THIS, property); stackChange(1); + } else if (type == Token.GETPROP_OPTIONAL) { + String property = id.getString(); + // stack: ... target -> ... function thisObj + addStringOp(Icode_PROP_AND_THIS_OPTIONAL, property); + stackChange(1); } else { visitExpression(id, 0); // stack: ... target id -> ... function thisObj diff --git a/rhino/src/main/java/org/mozilla/javascript/IRFactory.java b/rhino/src/main/java/org/mozilla/javascript/IRFactory.java index 9e05b5fe29..02df9043a6 100644 --- a/rhino/src/main/java/org/mozilla/javascript/IRFactory.java +++ b/rhino/src/main/java/org/mozilla/javascript/IRFactory.java @@ -141,6 +141,7 @@ private Node transform(AstNode node) { return transformBlock(node); case Token.BREAK: return transformBreak((BreakStatement) node); + case Token.CALL_OPTIONAL: case Token.CALL: return transformFunctionCall((FunctionCall) node); case Token.CONTINUE: @@ -161,6 +162,7 @@ private Node transform(AstNode node) { return transformGenExpr((GeneratorExpression) node); case Token.GETELEM: return transformElementGet((ElementGet) node); + case Token.QUESTION_DOT: case Token.GETPROP: return transformPropertyGet((PropertyGet) node); case Token.HOOK: @@ -347,7 +349,8 @@ private Node arrayCompTransformHelper(ArrayComprehension node, String arrayName) Node call = createCallOrNew( Token.CALL, - createPropertyGet(parser.createName(arrayName), null, "push", 0)); + createPropertyGet( + parser.createName(arrayName), null, "push", 0, node.type)); Node body = new Node(Token.EXPR_VOID, call, lineno); @@ -603,7 +606,10 @@ private Node transformFunction(FunctionNode fn) { } private Node transformFunctionCall(FunctionCall node) { - Node call = createCallOrNew(Token.CALL, transform(node.getTarget())); + Node call = + createCallOrNew( + node.type == Token.CALL_OPTIONAL ? Token.CALL_OPTIONAL : Token.CALL, + transform(node.getTarget())); call.setLineno(node.getLineno()); List args = node.getArguments(); for (int i = 0; i < args.size(); i++) { @@ -889,7 +895,7 @@ private Node transformComputedPropertyKey(ComputedPropertyKey node) { private Node transformPropertyGet(PropertyGet node) { Node target = transform(node.getTarget()); String name = node.getProperty().getIdentifier(); - return createPropertyGet(target, null, name, 0); + return createPropertyGet(target, null, name, 0, node.type); } private Node transformTemplateLiteral(TemplateLiteral node) { @@ -1211,7 +1217,7 @@ private Node transformXmlRef(Node pn, XmlRef node, int memberTypeFlags) { String ns = namespace != null ? namespace.getIdentifier() : null; if (node instanceof XmlPropRef) { String name = ((XmlPropRef) node).getPropName().getIdentifier(); - return createPropertyGet(pn, ns, name, memberTypeFlags); + return createPropertyGet(pn, ns, name, memberTypeFlags, node.type); } Node expr = transform(((XmlElemRef) node).getExpression()); return createElementGet(pn, ns, expr, memberTypeFlags); @@ -1847,18 +1853,27 @@ private static Node createIncDec(int nodeType, boolean post, Node child) { } private Node createPropertyGet( - Node target, String namespace, String name, int memberTypeFlags) { + Node target, String namespace, String name, int memberTypeFlags, int type) { if (namespace == null && memberTypeFlags == 0) { if (target == null) { return parser.createName(name); } parser.checkActivationName(name, Token.GETPROP); if (ScriptRuntime.isSpecialProperty(name)) { - Node ref = new Node(Token.REF_SPECIAL, target); + Node ref = + new Node( + type == Token.QUESTION_DOT + ? Token.REF_SPECIAL_OPTIONAL + : Token.REF_SPECIAL, + target); ref.putProp(Node.NAME_PROP, name); return new Node(Token.GET_REF, ref); } - return new Node(Token.GETPROP, target, Node.newString(name)); + + return new Node( + type == Token.QUESTION_DOT ? Token.GETPROP_OPTIONAL : Token.GETPROP, + target, + Node.newString(name)); } Node elem = Node.newString(name); memberTypeFlags |= Node.PROPERTY_FLAG; diff --git a/rhino/src/main/java/org/mozilla/javascript/Icode.java b/rhino/src/main/java/org/mozilla/javascript/Icode.java index 046cb02367..7bcce424bc 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Icode.java +++ b/rhino/src/main/java/org/mozilla/javascript/Icode.java @@ -142,9 +142,9 @@ abstract class Icode { Icode_TEMPLATE_LITERAL_CALLSITE = -74, Icode_LITERAL_KEYS = -75, Icode_LITERAL_KEY_SET = -76, - + Icode_PROP_AND_THIS_OPTIONAL = -77, // Last icode - MIN_ICODE = -76; + MIN_ICODE = -77; static String bytecodeName(int bytecode) { if (!validBytecode(bytecode)) { @@ -190,6 +190,8 @@ static String bytecodeName(int bytecode) { return "NAME_AND_THIS"; case Icode_PROP_AND_THIS: return "PROP_AND_THIS"; + case Icode_PROP_AND_THIS_OPTIONAL: + return "PROP_AND_THIS_OPTIONAL"; case Icode_ELEM_AND_THIS: return "ELEM_AND_THIS"; case Icode_VALUE_AND_THIS: diff --git a/rhino/src/main/java/org/mozilla/javascript/Interpreter.java b/rhino/src/main/java/org/mozilla/javascript/Interpreter.java index 7e0f6f74c0..b0400bfaa5 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Interpreter.java +++ b/rhino/src/main/java/org/mozilla/javascript/Interpreter.java @@ -1598,6 +1598,16 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object throwabl lhs, stringReg, cx, frame.scope); continue Loop; } + case Token.GETPROP_OPTIONAL: + { + Object lhs = stack[stackTop]; + if (lhs == DBL_MRK) + lhs = ScriptRuntime.wrapNumber(sDbl[stackTop]); + stack[stackTop] = + ScriptRuntime.getObjectPropOptional( + lhs, stringReg, cx, frame.scope); + continue Loop; + } case Token.SETPROP: { Object rhs = stack[stackTop]; @@ -1694,6 +1704,19 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object throwabl ++stackTop; stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); continue Loop; + case Icode_PROP_AND_THIS_OPTIONAL: + { + Object obj = stack[stackTop]; + if (obj == DBL_MRK) + obj = ScriptRuntime.wrapNumber(sDbl[stackTop]); + // stringReg: property + stack[stackTop] = + ScriptRuntime.getPropFunctionAndThisOptional( + obj, stringReg, cx, frame.scope); + ++stackTop; + stack[stackTop] = ScriptRuntime.lastStoredScriptable(cx); + continue Loop; + } case Icode_PROP_AND_THIS: { Object obj = stack[stackTop]; @@ -1744,6 +1767,7 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object throwabl continue Loop; } case Token.CALL: + case Token.CALL_OPTIONAL: case Icode_TAIL_CALL: case Token.REF_CALL: { diff --git a/rhino/src/main/java/org/mozilla/javascript/Parser.java b/rhino/src/main/java/org/mozilla/javascript/Parser.java index e2a564e0c5..9b9bf0c84c 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Parser.java +++ b/rhino/src/main/java/org/mozilla/javascript/Parser.java @@ -2908,8 +2908,9 @@ private AstNode propertyAccess(int tt, AstNode pn) throws IOException { AstNode ref = null; // right side of . or .. operator int token = nextToken(); if (token == Token.LP && tt == Token.QUESTION_DOT) { - // optional chaining operator method call, o.func.?() + // optional chaining operator method call, o.func?.() var pos = pn.getPosition(); + pn.setType(Token.QUESTION_DOT); consumeToken(); checkCallRequiresActivation(pn); FunctionCall f = new FunctionCall(pos); @@ -2923,8 +2924,8 @@ private AstNode propertyAccess(int tt, AstNode pn) throws IOException { f.setArguments(args); f.setRp(ts.tokenBeg - pos); f.setLength(ts.tokenEnd - pos); - - return conditionalPropertyAccess(pn, f); + f.setType(Token.CALL_OPTIONAL); + return f; } switch (token) { @@ -2984,7 +2985,7 @@ private AstNode propertyAccess(int tt, AstNode pn) throws IOException { result.setRight(ref); if (tt == Token.QUESTION_DOT && result instanceof PropertyGet) { - return conditionalPropertyAccess(pn, result); + result.setType(Token.QUESTION_DOT); } return result; } diff --git a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java index 337f7e0bba..c24e593e72 100644 --- a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java +++ b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java @@ -1739,6 +1739,14 @@ public static Object getObjectProp(Object obj, String property, Context cx, Scri return getObjectProp(sobj, property, cx); } + public static Object getObjectPropOptional( + Object obj, String property, Context cx, Scriptable scope) { + if (obj == null || Undefined.isUndefined(obj)) { + return Undefined.instance; + } + return getObjectProp(obj, property, cx, scope); + } + public static Object getObjectProp(Scriptable obj, String property, Context cx) { Object result = ScriptableObject.getProperty(obj, property); @@ -2617,6 +2625,20 @@ public static Callable getElemFunctionAndThis( return (Callable) value; } + public static Callable getPropFunctionAndThisOptional( + Object obj, String property, Context cx, Scriptable scope) { + + Scriptable thisObj = toObjectOrNull(cx, obj, scope); + if (thisObj == null) { + throw undefCallError(obj, property); + } + + Object value = ScriptableObject.getProperty(thisObj, property); + if (Scriptable.NOT_FOUND == value || Undefined.isUndefined(value) || value == null) + return (cx1, scope1, thisObj2, args) -> Undefined.instance; + return getPropFunctionAndThisHelper(obj, property, cx, thisObj); + } + /** * Prepare for calling obj.property(...): return function corresponding to obj.property and make * obj properly converted to Scriptable available as ScriptRuntime.lastStoredScriptable() for diff --git a/rhino/src/main/java/org/mozilla/javascript/Token.java b/rhino/src/main/java/org/mozilla/javascript/Token.java index 337577d60d..e7d479f82a 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Token.java +++ b/rhino/src/main/java/org/mozilla/javascript/Token.java @@ -120,117 +120,120 @@ public static enum CommentType { REF_NS_MEMBER = 80, // Reference for x.ns::y, x..ns::y etc. REF_NAME = 81, // Reference for @y, @[y] etc. REF_NS_NAME = 82, // Reference for ns::y, @ns::y@[y] etc. - BIGINT = 83; // ES2020 BigInt + BIGINT = 83, // ES2020 BigInt + GETPROP_OPTIONAL = 84, + REF_SPECIAL_OPTIONAL = 85, + CALL_OPTIONAL = 86; // End of interpreter bytecodes - public static final int LAST_BYTECODE_TOKEN = BIGINT, - TRY = 84, - SEMI = 85, // semicolon - LB = 86, // left and right brackets - RB = 87, - LC = 88, // left and right curlies (braces) - RC = 89, - LP = 90, // left and right parentheses - RP = 91, - COMMA = 92, // comma operator - ASSIGN = 93, // simple assignment (=) - ASSIGN_BITOR = 94, // |= - ASSIGN_LOGICAL_OR = 95, // ||= - ASSIGN_BITXOR = 96, // ^= - ASSIGN_BITAND = 97, // |= - ASSIGN_LOGICAL_AND = 98, // &&= - ASSIGN_LSH = 99, // <<= - ASSIGN_RSH = 100, // >>= - ASSIGN_URSH = 101, // >>>= - ASSIGN_ADD = 102, // += - ASSIGN_SUB = 103, // -= - ASSIGN_MUL = 104, // *= - ASSIGN_DIV = 105, // /= - ASSIGN_MOD = 106, // %= - ASSIGN_EXP = 107; // **= + public static final int LAST_BYTECODE_TOKEN = CALL_OPTIONAL, + TRY = 87, + SEMI = 88, // semicolon + LB = 89, // left and right brackets + RB = 90, + LC = 91, // left and right curlies (braces) + RC = 92, + LP = 93, // left and right parentheses + RP = 94, + COMMA = 95, // comma operator + ASSIGN = 96, // simple assignment (=) + ASSIGN_BITOR = 97, // |= + ASSIGN_LOGICAL_OR = 98, // ||= + ASSIGN_BITXOR = 99, // ^= + ASSIGN_BITAND = 100, // |= + ASSIGN_LOGICAL_AND = 101, // &&= + ASSIGN_LSH = 102, // <<= + ASSIGN_RSH = 103, // >>= + ASSIGN_URSH = 104, // >>>= + ASSIGN_ADD = 105, // += + ASSIGN_SUB = 106, // -= + ASSIGN_MUL = 107, // *= + ASSIGN_DIV = 108, // /= + ASSIGN_MOD = 109, // %= + ASSIGN_EXP = 110; // **= public static final int FIRST_ASSIGN = ASSIGN, LAST_ASSIGN = ASSIGN_EXP, - HOOK = 108, // conditional (?:) - COLON = 109, - OR = 110, // logical or (||) - AND = 111, // logical and (&&) - INC = 112, // increment/decrement (++ --) - DEC = 113, - DOT = 114, // member operator (.) - FUNCTION = 115, // function keyword - EXPORT = 116, // export keyword - IMPORT = 117, // import keyword - IF = 118, // if keyword - ELSE = 119, // else keyword - SWITCH = 120, // switch keyword - CASE = 121, // case keyword - DEFAULT = 122, // default keyword - WHILE = 123, // while keyword - DO = 124, // do keyword - FOR = 125, // for keyword - BREAK = 126, // break keyword - CONTINUE = 127, // continue keyword - VAR = 128, // var keyword - WITH = 129, // with keyword - CATCH = 130, // catch keyword - FINALLY = 131, // finally keyword - VOID = 132, // void keyword - RESERVED = 133, // reserved keywords - EMPTY = 134, - COMPUTED_PROPERTY = 135, // computed property in object initializer [x] + HOOK = 111, // conditional (?:) + COLON = 112, + OR = 113, // logical or (||) + AND = 114, // logical and (&&) + INC = 115, // increment/decrement (++ --) + DEC = 116, + DOT = 117, // member operator (.) + FUNCTION = 118, // function keyword + EXPORT = 119, // export keyword + IMPORT = 120, // import keyword + IF = 121, // if keyword + ELSE = 122, // else keyword + SWITCH = 123, // switch keyword + CASE = 124, // case keyword + DEFAULT = 125, // default keyword + WHILE = 126, // while keyword + DO = 127, // do keyword + FOR = 128, // for keyword + BREAK = 129, // break keyword + CONTINUE = 130, // continue keyword + VAR = 131, // var keyword + WITH = 132, // with keyword + CATCH = 133, // catch keyword + FINALLY = 134, // finally keyword + VOID = 135, // void keyword + RESERVED = 136, // reserved keywords + EMPTY = 137, + COMPUTED_PROPERTY = 138, // computed property in object initializer [x] /* types used for the parse tree - these never get returned * by the scanner. */ - BLOCK = 136, // statement block - LABEL = 137, // label - TARGET = 138, - LOOP = 139, - EXPR_VOID = 140, // expression statement in functions - EXPR_RESULT = 141, // expression statement in scripts - JSR = 142, - SCRIPT = 143, // top-level node for entire script - TYPEOFNAME = 144, // for typeof(simple-name) - USE_STACK = 145, - SETPROP_OP = 146, // x.y op= something - SETELEM_OP = 147, // x[y] op= something - LOCAL_BLOCK = 148, - SET_REF_OP = 149, // *reference op= something + BLOCK = 139, // statement block + LABEL = 140, // label + TARGET = 141, + LOOP = 142, + EXPR_VOID = 143, // expression statement in functions + EXPR_RESULT = 144, // expression statement in scripts + JSR = 145, + SCRIPT = 146, // top-level node for entire script + TYPEOFNAME = 147, // for typeof(simple-name) + USE_STACK = 148, + SETPROP_OP = 149, // x.y op= something + SETELEM_OP = 150, // x[y] op= something + LOCAL_BLOCK = 151, + SET_REF_OP = 152, // *reference op= something // For XML support: - DOTDOT = 150, // member operator (..) - COLONCOLON = 151, // namespace::name - XML = 152, // XML type - DOTQUERY = 153, // .() -- e.g., x.emps.emp.(name == "terry") - XMLATTR = 154, // @ - XMLEND = 155, + DOTDOT = 153, // member operator (..) + COLONCOLON = 154, // namespace::name + XML = 155, // XML type + DOTQUERY = 156, // .() -- e.g., x.emps.emp.(name == "terry") + XMLATTR = 157, // @ + XMLEND = 158, // Optimizer-only-tokens - TO_OBJECT = 156, - TO_DOUBLE = 157, - GET = 158, // JS 1.5 get pseudo keyword - SET = 159, // JS 1.5 set pseudo keyword - LET = 160, // JS 1.7 let pseudo keyword - CONST = 161, - SETCONST = 162, - SETCONSTVAR = 163, - ARRAYCOMP = 164, // array comprehension - LETEXPR = 165, - WITHEXPR = 166, - DEBUGGER = 167, - COMMENT = 168, - GENEXPR = 169, - METHOD = 170, // ES6 MethodDefinition - ARROW = 171, // ES6 ArrowFunction - YIELD_STAR = 172, // ES6 "yield *", a specialization of yield - TEMPLATE_LITERAL = 173, // template literal - TEMPLATE_CHARS = 174, // template literal - literal section - TEMPLATE_LITERAL_SUBST = 175, // template literal - substitution - TAGGED_TEMPLATE_LITERAL = 176, // template literal - tagged/handler - DOTDOTDOT = 177, // spread/rest ... - QUESTION_DOT = 178, - LAST_TOKEN = 179; + TO_OBJECT = 159, + TO_DOUBLE = 160, + GET = 161, // JS 1.5 get pseudo keyword + SET = 162, // JS 1.5 set pseudo keyword + LET = 163, // JS 1.7 let pseudo keyword + CONST = 164, + SETCONST = 165, + SETCONSTVAR = 166, + ARRAYCOMP = 167, // array comprehension + LETEXPR = 168, + WITHEXPR = 169, + DEBUGGER = 170, + COMMENT = 171, + GENEXPR = 172, + METHOD = 173, // ES6 MethodDefinition + ARROW = 174, // ES6 ArrowFunction + YIELD_STAR = 175, // ES6 "yield *", a specialization of yield + TEMPLATE_LITERAL = 176, // template literal + TEMPLATE_CHARS = 177, // template literal - literal section + TEMPLATE_LITERAL_SUBST = 178, // template literal - substitution + TAGGED_TEMPLATE_LITERAL = 179, // template literal - tagged/handler + DOTDOTDOT = 180, // spread/rest ... + QUESTION_DOT = 181, + LAST_TOKEN = 182; /** * Returns a name for the token. If Rhino is compiled with certain hardcoded debugging flags in diff --git a/rhino/src/main/java/org/mozilla/javascript/optimizer/BodyCodegen.java b/rhino/src/main/java/org/mozilla/javascript/optimizer/BodyCodegen.java index c0c4aeb90f..193b10acde 100644 --- a/rhino/src/main/java/org/mozilla/javascript/optimizer/BodyCodegen.java +++ b/rhino/src/main/java/org/mozilla/javascript/optimizer/BodyCodegen.java @@ -997,6 +997,7 @@ private void generateExpression(Node node, Node parent) { break; case Token.CALL: + case Token.CALL_OPTIONAL: case Token.NEW: { int specialType = node.getIntProp(Node.SPECIALCALL_PROP, Node.NON_SPECIALCALL); @@ -1006,7 +1007,7 @@ private void generateExpression(Node node, Node parent) { if (target != null) { visitOptimizedCall(node, target, type, child); - } else if (type == Token.CALL) { + } else if (type == Token.CALL || type == Token.CALL_OPTIONAL) { visitStandardCall(node, child); } else { visitStandardNew(node, child); @@ -1359,6 +1360,9 @@ private void generateExpression(Node node, Node parent) { case Token.GETPROPNOWARN: visitGetProp(node, child); break; + case Token.GETPROP_OPTIONAL: + visitGetPropOptional(node, child); + break; case Token.GETELEM: generateExpression(child, node); // object @@ -2317,7 +2321,8 @@ private void visitSpecialCall(Node node, int type, int specialType, Node child) } private void visitStandardCall(Node node, Node child) { - if (node.getType() != Token.CALL) throw Codegen.badTree(); + if (node.getType() != Token.CALL && node.getType() != Token.CALL_OPTIONAL) + throw Codegen.badTree(); Node firstArgChild = child.getNext(); int childType = child.getType(); @@ -2336,14 +2341,14 @@ private void visitStandardCall(Node node, Node child) { + "Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + ")Ljava/lang/Object;"; - } else if (childType == Token.GETPROP) { + } else if (childType == Token.GETPROP || childType == Token.GETPROP_OPTIONAL) { // x.name() call Node propTarget = child.getFirstChild(); generateExpression(propTarget, node); Node id = propTarget.getNext(); String property = id.getString(); cfw.addPush(property); - methodName = "callProp0"; + methodName = childType == Token.GETPROP ? "callProp0" : "callProp0Optional"; signature = "(Ljava/lang/Object;" + "Ljava/lang/String;" @@ -4079,6 +4084,47 @@ private void visitGetProp(Node node, Node child) { } } + private void visitGetPropOptional(Node node, Node child) { + generateExpression(child, node); // object + Node nameChild = child.getNext(); + generateExpression(nameChild, node); // the name + + cfw.addALoad(contextLocal); + cfw.addALoad(variableObjectLocal); + addScriptRuntimeInvoke( + "getObjectPropOptional", + "(Ljava/lang/Object;" + + "Ljava/lang/String;" + + "Lorg/mozilla/javascript/Context;" + + "Lorg/mozilla/javascript/Scriptable;" + + ")Ljava/lang/Object;"); + // TODO - check if we can skip those casts too + /* + for 'this.foo' we call getObjectProp(Scriptable...) which can + skip some casting overhead. + */ + // int childType = child.getType(); + // if (childType == Token.THIS && nameChild.getType() == Token.STRING) { + // cfw.addALoad(contextLocal); + // addScriptRuntimeInvoke( + // "getObjectProp", + // "(Lorg/mozilla/javascript/Scriptable;" + // + "Ljava/lang/String;" + // + "Lorg/mozilla/javascript/Context;" + // + ")Ljava/lang/Object;"); + // } else { + // cfw.addALoad(contextLocal); + // cfw.addALoad(variableObjectLocal); + // addScriptRuntimeInvoke( + // "getObjectPropOptional", + // "(Ljava/lang/Object;" + // + "Ljava/lang/String;" + // + "Lorg/mozilla/javascript/Context;" + // + "Lorg/mozilla/javascript/Scriptable;" + // + ")Ljava/lang/Object;"); + // } + } + private void visitSetProp(int type, Node node, Node child) { Node objectChild = child; generateExpression(child, node); diff --git a/rhino/src/main/java/org/mozilla/javascript/optimizer/OptRuntime.java b/rhino/src/main/java/org/mozilla/javascript/optimizer/OptRuntime.java index 6e117874fd..c1f46510be 100644 --- a/rhino/src/main/java/org/mozilla/javascript/optimizer/OptRuntime.java +++ b/rhino/src/main/java/org/mozilla/javascript/optimizer/OptRuntime.java @@ -73,6 +73,14 @@ public static Object callProp0(Object value, String property, Context cx, Script return f.call(cx, scope, thisObj, ScriptRuntime.emptyArgs); } + /** Implement x?.property() call shrinking optimizer code. */ + public static Object callProp0Optional( + Object value, String property, Context cx, Scriptable scope) { + Callable f = getPropFunctionAndThisOptional(value, property, cx, scope); + Scriptable thisObj = lastStoredScriptable(cx); + return f.call(cx, scope, thisObj, ScriptRuntime.emptyArgs); + } + public static Object add(Object val1, double val2, Context cx) { if (val1 instanceof Double) { return ((Double) val1) + val2; diff --git a/tests/testsrc/test262.properties b/tests/testsrc/test262.properties index 49d107e54c..7892e75143 100644 --- a/tests/testsrc/test262.properties +++ b/tests/testsrc/test262.properties @@ -6137,7 +6137,7 @@ language/expressions/object 864/1169 (73.91%) yield-non-strict-access.js non-strict yield-non-strict-syntax.js non-strict -language/expressions/optional-chaining 26/38 (68.42%) +language/expressions/optional-chaining 25/38 (65.79%) call-expression.js early-errors-tail-position-null-optchain-template-string.js early-errors-tail-position-null-optchain-template-string-esi.js @@ -6162,7 +6162,6 @@ language/expressions/optional-chaining 26/38 (68.42%) optional-chain-prod-expression.js punctuator-decimal-lookahead.js short-circuiting.js - static-semantics-simple-assignment.js super-property-optional-call.js language/expressions/postfix-decrement 9/37 (24.32%) From 7d8acd19817b4c2f8ebf61c016e8cb88a290b082 Mon Sep 17 00:00:00 2001 From: Grzegorz Caban Date: Sun, 8 Sep 2024 14:33:11 +0100 Subject: [PATCH 4/6] support for ?. for REF_SPECIAL property access --- .../org/mozilla/javascript/CodeGenerator.java | 1 + .../org/mozilla/javascript/Interpreter.java | 11 ++++++++ .../org/mozilla/javascript/ScriptRuntime.java | 5 ++++ .../javascript/SpecialOptionalRef.java | 28 +++++++++++++++++++ .../javascript/optimizer/BodyCodegen.java | 17 ++++++++++- .../tests/OptionalChainingOperatorTests.java | 13 +++++++++ 6 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 rhino/src/main/java/org/mozilla/javascript/SpecialOptionalRef.java diff --git a/rhino/src/main/java/org/mozilla/javascript/CodeGenerator.java b/rhino/src/main/java/org/mozilla/javascript/CodeGenerator.java index d24c68a4a8..7bf3954773 100644 --- a/rhino/src/main/java/org/mozilla/javascript/CodeGenerator.java +++ b/rhino/src/main/java/org/mozilla/javascript/CodeGenerator.java @@ -968,6 +968,7 @@ private void visitExpression(Node node, int contextFlags) { visitArrayComprehension(node, child, child.getNext()); break; + case Token.REF_SPECIAL_OPTIONAL: case Token.REF_SPECIAL: visitExpression(child, 0); addStringOp(type, (String) node.getProp(Node.NAME_PROP)); diff --git a/rhino/src/main/java/org/mozilla/javascript/Interpreter.java b/rhino/src/main/java/org/mozilla/javascript/Interpreter.java index b0400bfaa5..0ec7113290 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Interpreter.java +++ b/rhino/src/main/java/org/mozilla/javascript/Interpreter.java @@ -2284,6 +2284,17 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object throwabl obj, stringReg, cx, frame.scope); continue Loop; } + case Token.REF_SPECIAL_OPTIONAL: + { + // stringReg: name of special property + Object obj = stack[stackTop]; + if (obj == DBL_MRK) + obj = ScriptRuntime.wrapNumber(sDbl[stackTop]); + stack[stackTop] = + ScriptRuntime.optionalSpecialRef( + obj, stringReg, cx, frame.scope); + continue Loop; + } case Token.REF_MEMBER: { // indexReg: flags diff --git a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java index c24e593e72..c2fe608e83 100644 --- a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java +++ b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java @@ -1979,6 +1979,11 @@ public static Ref specialRef(Object obj, String specialProperty, Context cx, Scr return SpecialRef.createSpecial(cx, scope, obj, specialProperty); } + public static Ref optionalSpecialRef( + Object obj, String specialProperty, Context cx, Scriptable scope) { + return SpecialOptionalRef.create(cx, scope, obj, specialProperty); + } + /** @deprecated Use {@link #delete(Object, Object, Context, Scriptable, boolean)} instead */ @Deprecated public static Object delete(Object obj, Object id, Context cx) { diff --git a/rhino/src/main/java/org/mozilla/javascript/SpecialOptionalRef.java b/rhino/src/main/java/org/mozilla/javascript/SpecialOptionalRef.java new file mode 100644 index 0000000000..8098e81e53 --- /dev/null +++ b/rhino/src/main/java/org/mozilla/javascript/SpecialOptionalRef.java @@ -0,0 +1,28 @@ +package org.mozilla.javascript; + +class SpecialOptionalRef extends Ref { + Ref specialRef; + + private SpecialOptionalRef(Ref specialRef) { + this.specialRef = specialRef; + } + + public static Ref create(Context cx, Scriptable scope, Object object, String name) { + Scriptable target = ScriptRuntime.toObjectOrNull(cx, object, scope); + if (target != null && target != Undefined.instance) { + return new SpecialOptionalRef(SpecialRef.createSpecial(cx, scope, object, name)); + } + return new SpecialOptionalRef(null); + } + + @Override + public Object get(Context cx) { + if (specialRef == null) return Undefined.instance; + return specialRef.get(cx); + } + + @Override + public Object set(Context cx, Object value) { + throw new IllegalStateException(); + } +} diff --git a/rhino/src/main/java/org/mozilla/javascript/optimizer/BodyCodegen.java b/rhino/src/main/java/org/mozilla/javascript/optimizer/BodyCodegen.java index 193b10acde..4fab1fe85e 100644 --- a/rhino/src/main/java/org/mozilla/javascript/optimizer/BodyCodegen.java +++ b/rhino/src/main/java/org/mozilla/javascript/optimizer/BodyCodegen.java @@ -1521,7 +1521,22 @@ private void generateExpression(Node node, Node parent) { + ")Lorg/mozilla/javascript/Ref;"); } break; - + case Token.REF_SPECIAL_OPTIONAL: + { + String special = (String) node.getProp(Node.NAME_PROP); + generateExpression(child, node); + cfw.addPush(special); + cfw.addALoad(contextLocal); + cfw.addALoad(variableObjectLocal); + addScriptRuntimeInvoke( + "optionalSpecialRef", + "(Ljava/lang/Object;" + + "Ljava/lang/String;" + + "Lorg/mozilla/javascript/Context;" + + "Lorg/mozilla/javascript/Scriptable;" + + ")Lorg/mozilla/javascript/Ref;"); + } + break; case Token.REF_MEMBER: case Token.REF_NS_MEMBER: case Token.REF_NAME: diff --git a/rhino/src/test/java/org/mozilla/javascript/tests/OptionalChainingOperatorTests.java b/rhino/src/test/java/org/mozilla/javascript/tests/OptionalChainingOperatorTests.java index b39fa56a44..95ea83c554 100644 --- a/rhino/src/test/java/org/mozilla/javascript/tests/OptionalChainingOperatorTests.java +++ b/rhino/src/test/java/org/mozilla/javascript/tests/OptionalChainingOperatorTests.java @@ -67,6 +67,19 @@ public void testOptionalChainingOperator() { 1, null)); + // SpecialRef and Optional Chaining operator + assertEquals( + Undefined.instance, + cx.evaluateString( + scope, " var a = null; a?.__proto__", sourceName, 1, null)); + assertEquals( + Undefined.instance, + cx.evaluateString(scope, "a?.__proto__", sourceName, 1, null)); + + assertEquals( + Undefined.instance, + cx.evaluateString(scope, "a?.__parent__", sourceName, 1, null)); + // NOT WORKING // assertEquals( // Undefined.instance, From 938cf99fe21b5693b6058dec4aebce738978b045 Mon Sep 17 00:00:00 2001 From: nabacg Date: Thu, 19 Sep 2024 12:35:07 +0100 Subject: [PATCH 5/6] code cleanup and potential BodyGen visitGetPropOptional optimistion similar to visitGetProp --- .../java/org/mozilla/javascript/Parser.java | 18 ------- .../org/mozilla/javascript/ScriptRuntime.java | 8 +++ .../javascript/SpecialOptionalRef.java | 1 + .../javascript/optimizer/BodyCodegen.java | 53 ++++++++----------- 4 files changed, 30 insertions(+), 50 deletions(-) diff --git a/rhino/src/main/java/org/mozilla/javascript/Parser.java b/rhino/src/main/java/org/mozilla/javascript/Parser.java index 5811766377..ea2a26bf7b 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Parser.java +++ b/rhino/src/main/java/org/mozilla/javascript/Parser.java @@ -3009,24 +3009,6 @@ private AstNode propertyAccess(int tt, AstNode pn) throws IOException { return result; } - private static AstNode conditionalPropertyAccess(AstNode target, AstNode propAccessNode) { - var targetCopy = cloneNode(target); - var targetCopy2 = cloneNode(target); - - AstNode undefinedNode = new Name(0, "undefined"); - AstNode nullNode = new KeywordLiteral(0, 4, Token.NULL); - - AstNode nullEq = new InfixExpression(Token.SHEQ, targetCopy, nullNode, 0); - - AstNode undefinedEq = new InfixExpression(Token.SHEQ, targetCopy2, undefinedNode, 0); - var conditionalExpr = new ConditionalExpression(); - conditionalExpr.setTestExpression(new InfixExpression(Token.OR, nullEq, undefinedEq, 0)); - conditionalExpr.setTrueExpression(new Name(0, "undefined")); - conditionalExpr.setFalseExpression(propAccessNode); - - return conditionalExpr; - } - private static AstNode cloneNode(AstNode target) { var newParser = new Parser(); var root = newParser.parse(target.toSource(), "", 1); diff --git a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java index c2fe608e83..0a1b6720f7 100644 --- a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java +++ b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java @@ -1747,6 +1747,14 @@ public static Object getObjectPropOptional( return getObjectProp(obj, property, cx, scope); } + public static Object getObjectPropOptional( + Scriptable obj, String property, Context cx, Scriptable scope) { + if (obj == null || Undefined.isUndefined(obj)) { + return Undefined.instance; + } + return getObjectProp(obj, property, cx); + } + public static Object getObjectProp(Scriptable obj, String property, Context cx) { Object result = ScriptableObject.getProperty(obj, property); diff --git a/rhino/src/main/java/org/mozilla/javascript/SpecialOptionalRef.java b/rhino/src/main/java/org/mozilla/javascript/SpecialOptionalRef.java index 8098e81e53..d2f88cd1ee 100644 --- a/rhino/src/main/java/org/mozilla/javascript/SpecialOptionalRef.java +++ b/rhino/src/main/java/org/mozilla/javascript/SpecialOptionalRef.java @@ -21,6 +21,7 @@ public Object get(Context cx) { return specialRef.get(cx); } + @Deprecated @Override public Object set(Context cx, Object value) { throw new IllegalStateException(); diff --git a/rhino/src/main/java/org/mozilla/javascript/optimizer/BodyCodegen.java b/rhino/src/main/java/org/mozilla/javascript/optimizer/BodyCodegen.java index 4fab1fe85e..cfcdd9318d 100644 --- a/rhino/src/main/java/org/mozilla/javascript/optimizer/BodyCodegen.java +++ b/rhino/src/main/java/org/mozilla/javascript/optimizer/BodyCodegen.java @@ -4103,41 +4103,30 @@ private void visitGetPropOptional(Node node, Node child) { generateExpression(child, node); // object Node nameChild = child.getNext(); generateExpression(nameChild, node); // the name - - cfw.addALoad(contextLocal); - cfw.addALoad(variableObjectLocal); - addScriptRuntimeInvoke( - "getObjectPropOptional", - "(Ljava/lang/Object;" - + "Ljava/lang/String;" - + "Lorg/mozilla/javascript/Context;" - + "Lorg/mozilla/javascript/Scriptable;" - + ")Ljava/lang/Object;"); - // TODO - check if we can skip those casts too /* - for 'this.foo' we call getObjectProp(Scriptable...) which can + for 'this.foo' we call getObjectPropOptional(Scriptable...) which can skip some casting overhead. */ - // int childType = child.getType(); - // if (childType == Token.THIS && nameChild.getType() == Token.STRING) { - // cfw.addALoad(contextLocal); - // addScriptRuntimeInvoke( - // "getObjectProp", - // "(Lorg/mozilla/javascript/Scriptable;" - // + "Ljava/lang/String;" - // + "Lorg/mozilla/javascript/Context;" - // + ")Ljava/lang/Object;"); - // } else { - // cfw.addALoad(contextLocal); - // cfw.addALoad(variableObjectLocal); - // addScriptRuntimeInvoke( - // "getObjectPropOptional", - // "(Ljava/lang/Object;" - // + "Ljava/lang/String;" - // + "Lorg/mozilla/javascript/Context;" - // + "Lorg/mozilla/javascript/Scriptable;" - // + ")Ljava/lang/Object;"); - // } + int childType = child.getType(); + if (childType == Token.THIS && nameChild.getType() == Token.STRING) { + cfw.addALoad(contextLocal); + addScriptRuntimeInvoke( + "getObjectPropOptional", + "(Lorg/mozilla/javascript/Scriptable;" + + "Ljava/lang/String;" + + "Lorg/mozilla/javascript/Context;" + + ")Ljava/lang/Object;"); + } else { + cfw.addALoad(contextLocal); + cfw.addALoad(variableObjectLocal); + addScriptRuntimeInvoke( + "getObjectPropOptional", + "(Ljava/lang/Object;" + + "Ljava/lang/String;" + + "Lorg/mozilla/javascript/Context;" + + "Lorg/mozilla/javascript/Scriptable;" + + ")Ljava/lang/Object;"); + } } private void visitSetProp(int type, Node node, Node child) { From e4d6d61b4d9f294d582037cc15fd3c0a469d99f0 Mon Sep 17 00:00:00 2001 From: nabacg Date: Thu, 19 Sep 2024 12:47:01 +0100 Subject: [PATCH 6/6] updated test262.properties --- tests/testsrc/test262.properties | 124 +++++-------------------------- 1 file changed, 17 insertions(+), 107 deletions(-) diff --git a/tests/testsrc/test262.properties b/tests/testsrc/test262.properties index bbfd150f88..750db49f39 100644 --- a/tests/testsrc/test262.properties +++ b/tests/testsrc/test262.properties @@ -2,7 +2,7 @@ ~annexB -harness 23/115 (20.0%) +harness 22/115 (19.13%) assert-notsamevalue-tostring.js {unsupported: [async-functions]} assert-samevalue-tostring.js {unsupported: [async-functions]} assert-tostring.js {unsupported: [async-functions]} @@ -11,7 +11,6 @@ harness 23/115 (20.0%) asyncHelpers-asyncTest-then-resolves.js {unsupported: [async]} asyncHelpers-throwsAsync-custom.js {unsupported: [async]} asyncHelpers-throwsAsync-custom-typeerror.js {unsupported: [async]} - asyncHelpers-throwsAsync-func-never-settles.js {unsupported: [async]} asyncHelpers-throwsAsync-func-throws-sync.js {unsupported: [async]} asyncHelpers-throwsAsync-incorrect-ctor.js {unsupported: [async]} asyncHelpers-throwsAsync-invalid-func.js {unsupported: [async]} @@ -27,7 +26,7 @@ harness 23/115 (20.0%) isConstructor.js {unsupported: [Reflect.construct]} nativeFunctionMatcher.js -built-ins/Array 383/3074 (12.46%) +built-ins/Array 364/3055 (11.91%) fromAsync 94/94 (100.0%) from/calling-from-valid-1-noStrict.js non-strict Spec pretty clearly says this should be undefined from/elements-deleted-after.js Checking to see if length changed, but spec says it should not @@ -225,9 +224,6 @@ built-ins/Array 383/3074 (12.46%) prototype/reduce/15.4.4.21-9-c-ii-4-s.js non-strict prototype/reduce/callbackfn-resize-arraybuffer.js {unsupported: [resizable-arraybuffer]} prototype/reduce/not-a-constructor.js {unsupported: [Reflect.construct]} - prototype/reduce/resizable-buffer.js {unsupported: [resizable-arraybuffer]} - prototype/reduce/resizable-buffer-grow-mid-iteration.js {unsupported: [resizable-arraybuffer]} - prototype/reduce/resizable-buffer-shrink-mid-iteration.js {unsupported: [resizable-arraybuffer]} prototype/reverse/length-exceeding-integer-limit-with-proxy.js prototype/reverse/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/reverse/resizable-buffer.js {unsupported: [resizable-arraybuffer]} @@ -237,8 +233,6 @@ built-ins/Array 383/3074 (12.46%) prototype/shift/set-length-zero-array-is-frozen.js prototype/shift/set-length-zero-array-length-is-non-writable.js prototype/shift/throws-when-this-value-length-is-writable-false.js - prototype/slice/coerced-start-end-grow.js {unsupported: [resizable-arraybuffer]} - prototype/slice/coerced-start-end-shrink.js {unsupported: [resizable-arraybuffer]} prototype/slice/create-ctor-non-object.js prototype/slice/create-ctor-poisoned.js prototype/slice/create-proto-from-ctor-realm-non-array.js @@ -252,20 +246,12 @@ built-ins/Array 383/3074 (12.46%) prototype/slice/create-species-poisoned.js prototype/slice/length-exceeding-integer-limit-proxied-array.js prototype/slice/not-a-constructor.js {unsupported: [Reflect.construct]} - prototype/slice/resizable-buffer.js {unsupported: [resizable-arraybuffer]} prototype/slice/target-array-non-extensible.js prototype/slice/target-array-with-non-configurable-property.js prototype/some/15.4.4.17-5-1-s.js non-strict prototype/some/callbackfn-resize-arraybuffer.js {unsupported: [resizable-arraybuffer]} prototype/some/not-a-constructor.js {unsupported: [Reflect.construct]} - prototype/some/resizable-buffer.js {unsupported: [resizable-arraybuffer]} - prototype/some/resizable-buffer-grow-mid-iteration.js {unsupported: [resizable-arraybuffer]} - prototype/some/resizable-buffer-shrink-mid-iteration.js {unsupported: [resizable-arraybuffer]} - prototype/sort/comparefn-grow.js {unsupported: [resizable-arraybuffer]} - prototype/sort/comparefn-resizable-buffer.js {unsupported: [resizable-arraybuffer]} - prototype/sort/comparefn-shrink.js {unsupported: [resizable-arraybuffer]} prototype/sort/not-a-constructor.js {unsupported: [Reflect.construct]} - prototype/sort/resizable-buffer-default-comparator.js {unsupported: [resizable-arraybuffer]} prototype/sort/S15.4.4.11_A8.js non-strict prototype/splice/clamps-length-to-integer-limit.js prototype/splice/create-ctor-non-object.js @@ -293,9 +279,6 @@ built-ins/Array 383/3074 (12.46%) prototype/toLocaleString/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/toLocaleString/primitive_this_value.js strict prototype/toLocaleString/primitive_this_value_getter.js strict - prototype/toLocaleString/resizable-buffer.js {unsupported: [resizable-arraybuffer]} - prototype/toLocaleString/user-provided-tolocalestring-grow.js {unsupported: [resizable-arraybuffer]} - prototype/toLocaleString/user-provided-tolocalestring-shrink.js {unsupported: [resizable-arraybuffer]} prototype/toReversed/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/toSorted/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/toSpliced/not-a-constructor.js {unsupported: [Reflect.construct]} @@ -309,9 +292,6 @@ built-ins/Array 383/3074 (12.46%) prototype/unshift/set-length-zero-array-length-is-non-writable.js prototype/unshift/throws-with-string-receiver.js prototype/values/not-a-constructor.js {unsupported: [Reflect.construct]} - prototype/values/resizable-buffer.js {unsupported: [resizable-arraybuffer]} - prototype/values/resizable-buffer-grow-mid-iteration.js {unsupported: [resizable-arraybuffer]} - prototype/values/resizable-buffer-shrink-mid-iteration.js {unsupported: [resizable-arraybuffer]} prototype/with/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/methods-called-as-functions.js is-a-constructor.js {unsupported: [Reflect.construct]} @@ -769,7 +749,7 @@ built-ins/Error 6/41 (14.63%) ~built-ins/FinalizationRegistry -built-ins/Function 187/508 (36.81%) +built-ins/Function 186/507 (36.69%) internals/Call 2/2 (100.0%) internals/Construct 6/6 (100.0%) length/S15.3.5.1_A1_T3.js strict @@ -782,7 +762,6 @@ built-ins/Function 187/508 (36.81%) prototype/apply/argarray-not-object.js prototype/apply/argarray-not-object-realm.js prototype/apply/not-a-constructor.js {unsupported: [Reflect.construct]} - prototype/apply/resizable-buffer.js {unsupported: [resizable-arraybuffer]} prototype/apply/S15.3.4.3_A3_T1.js non-interpreted prototype/apply/S15.3.4.3_A3_T2.js non-interpreted prototype/apply/S15.3.4.3_A3_T3.js non-interpreted @@ -1031,7 +1010,7 @@ built-ins/Map 13/171 (7.6%) built-ins/MapIteratorPrototype 0/11 (0.0%) -built-ins/Math 51/327 (15.6%) +built-ins/Math 51/326 (15.64%) abs/not-a-constructor.js {unsupported: [Reflect.construct]} acosh/not-a-constructor.js {unsupported: [Reflect.construct]} acos/not-a-constructor.js {unsupported: [Reflect.construct]} @@ -1073,7 +1052,7 @@ built-ins/Math 51/327 (15.6%) built-ins/NaN 0/6 (0.0%) -built-ins/NativeErrors 31/123 (25.2%) +built-ins/NativeErrors 25/117 (21.37%) AggregateError/errors-iterabletolist-failures.js AggregateError/is-a-constructor.js {unsupported: [Reflect.construct]} AggregateError/message-tostring-abrupt.js @@ -1090,8 +1069,6 @@ built-ins/NativeErrors 31/123 (25.2%) ReferenceError/prototype/not-error-object.js ReferenceError/is-a-constructor.js {unsupported: [Reflect.construct]} ReferenceError/proto-from-ctor-realm.js {unsupported: [Reflect]} - SuppressedError/prototype 2/2 (100.0%) - SuppressedError 4/4 (100.0%) SyntaxError/prototype/not-error-object.js SyntaxError/is-a-constructor.js {unsupported: [Reflect.construct]} SyntaxError/proto-from-ctor-realm.js {unsupported: [Reflect]} @@ -1128,7 +1105,7 @@ built-ins/Number 24/335 (7.16%) S9.3.1_A3_T1_U180E.js {unsupported: [u180e]} S9.3.1_A3_T2_U180E.js {unsupported: [u180e]} -built-ins/Object 222/3408 (6.51%) +built-ins/Object 217/3403 (6.38%) assign/assignment-to-readonly-property-of-target-must-throw-a-typeerror-exception.js assign/not-a-constructor.js {unsupported: [Reflect.construct]} assign/source-own-prop-desc-missing.js {unsupported: [Proxy]} @@ -1164,7 +1141,6 @@ built-ins/Object 222/3408 (6.51%) defineProperties/not-a-constructor.js {unsupported: [Reflect.construct]} defineProperties/property-description-must-be-an-object-not-symbol.js defineProperties/proxy-no-ownkeys-returned-keys-order.js {unsupported: [Proxy]} - defineProperties/typedarray-backed-by-resizable-buffer.js {unsupported: [resizable-arraybuffer]} defineProperty/15.2.3.6-4-116.js non-strict defineProperty/15.2.3.6-4-117.js non-strict defineProperty/15.2.3.6-4-122.js @@ -1183,11 +1159,8 @@ built-ins/Object 222/3408 (6.51%) defineProperty/15.2.3.6-4-293-3.js non-strict defineProperty/15.2.3.6-4-293-4.js strict defineProperty/15.2.3.6-4-336.js - defineProperty/coerced-P-grow.js {unsupported: [resizable-arraybuffer]} - defineProperty/coerced-P-shrink.js {unsupported: [resizable-arraybuffer]} defineProperty/not-a-constructor.js {unsupported: [Reflect.construct]} defineProperty/property-description-must-be-an-object-not-symbol.js - defineProperty/typedarray-backed-by-resizable-buffer.js {unsupported: [resizable-arraybuffer]} entries/not-a-constructor.js {unsupported: [Reflect.construct]} entries/observable-operations.js {unsupported: [Proxy]} entries/order-after-define-property-with-function.js @@ -1196,7 +1169,6 @@ built-ins/Object 222/3408 (6.51%) freeze/proxy-no-ownkeys-returned-keys-order.js {unsupported: [Proxy, Reflect]} freeze/proxy-with-defineProperty-handler.js {unsupported: [Proxy, Reflect]} freeze/throws-when-false.js - freeze/typedarray-backed-by-resizable-buffer.js {unsupported: [resizable-arraybuffer]} fromEntries/not-a-constructor.js {unsupported: [Reflect.construct]} fromEntries/to-property-key.js fromEntries/uses-keys-not-iterator.js @@ -1750,7 +1722,7 @@ built-ins/Promise 406/631 (64.34%) ~built-ins/Reflect -built-ins/RegExp 1169/1854 (63.05%) +built-ins/RegExp 1169/1853 (63.09%) CharacterClassEscapes 24/24 (100.0%) dotall 4/4 (100.0%) escape 20/20 (100.0%) @@ -2310,10 +2282,8 @@ built-ins/String 140/1182 (11.84%) built-ins/StringIteratorPrototype 0/7 (0.0%) -built-ins/Symbol 36/94 (38.3%) - asyncDispose/prop-desc.js +built-ins/Symbol 34/92 (36.96%) asyncIterator/prop-desc.js - dispose/prop-desc.js for/cross-realm.js for/description.js for/not-a-constructor.js {unsupported: [Reflect.construct]} @@ -2357,7 +2327,7 @@ built-ins/ThrowTypeError 8/14 (57.14%) unique-per-realm-non-simple.js unique-per-realm-unmapped-args.js -built-ins/TypedArray 1091/1422 (76.72%) +built-ins/TypedArray 1055/1386 (76.12%) from/arylk-get-length-error.js from/arylk-to-length-error.js from/from-array-mapper-detaches-result.js @@ -2751,9 +2721,6 @@ built-ins/TypedArray 1091/1422 (76.72%) prototype/reduce/name.js prototype/reduce/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/reduce/prop-desc.js - prototype/reduce/resizable-buffer.js {unsupported: [resizable-arraybuffer]} - prototype/reduce/resizable-buffer-grow-mid-iteration.js {unsupported: [resizable-arraybuffer]} - prototype/reduce/resizable-buffer-shrink-mid-iteration.js {unsupported: [resizable-arraybuffer]} prototype/reduce/return-abrupt-from-this-out-of-bounds.js {unsupported: [resizable-arraybuffer]} prototype/reduce/this-is-not-object.js prototype/reduce/this-is-not-typedarray-instance.js @@ -2788,7 +2755,6 @@ built-ins/TypedArray 1091/1422 (76.72%) prototype/set/array-arg-targetbuffer-detached-on-get-src-value-no-throw.js prototype/set/array-arg-targetbuffer-detached-on-tointeger-offset-throws.js prototype/set/array-arg-targetbuffer-detached-throws.js - prototype/set/array-arg-value-conversion-resizes-array-buffer.js {unsupported: [resizable-arraybuffer]} prototype/set/invoked-as-func.js prototype/set/invoked-as-method.js prototype/set/length.js @@ -2796,11 +2762,6 @@ built-ins/TypedArray 1091/1422 (76.72%) prototype/set/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/set/prop-desc.js prototype/set/src-typedarray-big-throws.js - prototype/set/target-grow-mid-iteration.js {unsupported: [resizable-arraybuffer]} - prototype/set/target-grow-source-length-getter.js {unsupported: [resizable-arraybuffer]} - prototype/set/target-shrink-mid-iteration.js {unsupported: [resizable-arraybuffer]} - prototype/set/target-shrink-source-length-getter.js {unsupported: [resizable-arraybuffer]} - prototype/set/this-backed-by-resizable-buffer.js {unsupported: [resizable-arraybuffer]} prototype/set/this-is-not-object.js prototype/set/this-is-not-typedarray-instance.js prototype/set/typedarray-arg-negative-integer-offset-throws.js @@ -2811,7 +2772,6 @@ built-ins/TypedArray 1091/1422 (76.72%) prototype/set/typedarray-arg-set-values-same-buffer-same-type-resized.js {unsupported: [resizable-arraybuffer]} prototype/set/typedarray-arg-set-values-same-buffer-same-type-sab.js {unsupported: [SharedArrayBuffer]} prototype/set/typedarray-arg-src-arraylength-internal.js - prototype/set/typedarray-arg-src-backed-by-resizable-buffer.js {unsupported: [resizable-arraybuffer]} prototype/set/typedarray-arg-src-byteoffset-internal.js prototype/set/typedarray-arg-src-range-greather-than-target-throws-rangeerror.js prototype/set/typedarray-arg-srcbuffer-detached-during-tointeger-offset-throws.js @@ -2821,8 +2781,6 @@ built-ins/TypedArray 1091/1422 (76.72%) prototype/set/typedarray-arg-targetbuffer-detached-during-tointeger-offset-throws.js prototype/slice/BigInt 38/38 (100.0%) prototype/slice/arraylength-internal.js - prototype/slice/coerced-start-end-grow.js {unsupported: [resizable-arraybuffer]} - prototype/slice/coerced-start-end-shrink.js {unsupported: [resizable-arraybuffer]} prototype/slice/detached-buffer.js prototype/slice/detached-buffer-custom-ctor-other-targettype.js prototype/slice/detached-buffer-custom-ctor-same-targettype.js @@ -2836,7 +2794,6 @@ built-ins/TypedArray 1091/1422 (76.72%) prototype/slice/name.js prototype/slice/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/slice/prop-desc.js - prototype/slice/resizable-buffer.js {unsupported: [resizable-arraybuffer]} prototype/slice/return-abrupt-from-this-out-of-bounds.js {unsupported: [resizable-arraybuffer]} prototype/slice/set-values-from-different-ctor-type.js prototype/slice/speciesctor-destination-resizable.js {unsupported: [resizable-arraybuffer]} @@ -2844,7 +2801,6 @@ built-ins/TypedArray 1091/1422 (76.72%) prototype/slice/speciesctor-get-species-custom-ctor-invocation.js prototype/slice/speciesctor-get-species-custom-ctor-length-throws.js prototype/slice/speciesctor-get-species-custom-ctor-length-throws-resizable-arraybuffer.js {unsupported: [resizable-arraybuffer]} - prototype/slice/speciesctor-resize.js {unsupported: [resizable-arraybuffer]} prototype/slice/this-is-not-object.js prototype/slice/this-is-not-typedarray-instance.js prototype/some/BigInt 16/16 (100.0%) @@ -2859,17 +2815,11 @@ built-ins/TypedArray 1091/1422 (76.72%) prototype/some/name.js prototype/some/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/some/prop-desc.js - prototype/some/resizable-buffer.js {unsupported: [resizable-arraybuffer]} - prototype/some/resizable-buffer-grow-mid-iteration.js {unsupported: [resizable-arraybuffer]} - prototype/some/resizable-buffer-shrink-mid-iteration.js {unsupported: [resizable-arraybuffer]} prototype/some/return-abrupt-from-this-out-of-bounds.js {unsupported: [resizable-arraybuffer]} prototype/some/this-is-not-object.js prototype/some/this-is-not-typedarray-instance.js prototype/sort/BigInt 10/10 (100.0%) prototype/sort/arraylength-internal.js - prototype/sort/comparefn-grow.js {unsupported: [resizable-arraybuffer]} - prototype/sort/comparefn-resizable-buffer.js {unsupported: [resizable-arraybuffer]} - prototype/sort/comparefn-shrink.js {unsupported: [resizable-arraybuffer]} prototype/sort/detached-buffer.js prototype/sort/invoked-as-func.js prototype/sort/invoked-as-method.js @@ -2877,14 +2827,11 @@ built-ins/TypedArray 1091/1422 (76.72%) prototype/sort/name.js prototype/sort/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/sort/prop-desc.js - prototype/sort/resizable-buffer-default-comparator.js {unsupported: [resizable-arraybuffer]} prototype/sort/return-abrupt-from-this-out-of-bounds.js {unsupported: [resizable-arraybuffer]} prototype/sort/sort-tonumber.js prototype/sort/this-is-not-object.js prototype/sort/this-is-not-typedarray-instance.js prototype/subarray/BigInt 27/27 (100.0%) - prototype/subarray/coerced-begin-end-grow.js {unsupported: [resizable-arraybuffer]} - prototype/subarray/coerced-begin-end-shrink.js {unsupported: [resizable-arraybuffer]} prototype/subarray/detached-buffer.js prototype/subarray/infinity.js prototype/subarray/invoked-as-func.js @@ -2893,7 +2840,6 @@ built-ins/TypedArray 1091/1422 (76.72%) prototype/subarray/name.js prototype/subarray/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/subarray/prop-desc.js - prototype/subarray/resizable-buffer.js {unsupported: [resizable-arraybuffer]} prototype/subarray/result-byteOffset-from-out-of-bounds.js {unsupported: [resizable-arraybuffer]} prototype/subarray/speciesctor-get-ctor.js prototype/subarray/speciesctor-get-ctor-abrupt.js @@ -2927,12 +2873,9 @@ built-ins/TypedArray 1091/1422 (76.72%) prototype/toLocaleString/name.js prototype/toLocaleString/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/toLocaleString/prop-desc.js - prototype/toLocaleString/resizable-buffer.js {unsupported: [resizable-arraybuffer]} prototype/toLocaleString/return-abrupt-from-this-out-of-bounds.js {unsupported: [resizable-arraybuffer]} prototype/toLocaleString/this-is-not-object.js prototype/toLocaleString/this-is-not-typedarray-instance.js - prototype/toLocaleString/user-provided-tolocalestring-grow.js {unsupported: [resizable-arraybuffer]} - prototype/toLocaleString/user-provided-tolocalestring-shrink.js {unsupported: [resizable-arraybuffer]} prototype/toReversed/metadata 3/3 (100.0%) prototype/toReversed/length-property-ignored.js prototype/toReversed/not-a-constructor.js {unsupported: [Reflect.construct]} @@ -2953,9 +2896,6 @@ built-ins/TypedArray 1091/1422 (76.72%) prototype/values/name.js prototype/values/not-a-constructor.js {unsupported: [Reflect.construct]} prototype/values/prop-desc.js - prototype/values/resizable-buffer.js {unsupported: [resizable-arraybuffer]} - prototype/values/resizable-buffer-grow-mid-iteration.js {unsupported: [resizable-arraybuffer]} - prototype/values/resizable-buffer-shrink-mid-iteration.js {unsupported: [resizable-arraybuffer]} prototype/values/return-abrupt-from-this-out-of-bounds.js {unsupported: [resizable-arraybuffer]} prototype/values/this-is-not-object.js prototype/values/this-is-not-typedarray-instance.js @@ -2964,18 +2904,13 @@ built-ins/TypedArray 1091/1422 (76.72%) prototype/with/index-validated-against-current-length.js {unsupported: [resizable-arraybuffer]} prototype/with/length-property-ignored.js prototype/with/not-a-constructor.js {unsupported: [Reflect.construct]} - prototype 4/4 (100.0%) + prototype 3/3 (100.0%) Symbol.species 4/4 (100.0%) invoked.js name.js - out-of-bounds-behaves-like-detached.js {unsupported: [resizable-arraybuffer]} - out-of-bounds-get-and-set.js {unsupported: [resizable-arraybuffer]} - out-of-bounds-has.js {unsupported: [resizable-arraybuffer]} prototype.js - resizable-buffer-length-tracking-1.js {unsupported: [resizable-arraybuffer]} - resizable-buffer-length-tracking-2.js {unsupported: [resizable-arraybuffer]} -built-ins/TypedArrayConstructors 597/735 (81.22%) +built-ins/TypedArrayConstructors 583/721 (80.86%) BigInt64Array/prototype 4/4 (100.0%) BigInt64Array 8/8 (100.0%) BigUint64Array/prototype 4/4 (100.0%) @@ -3012,7 +2947,6 @@ built-ins/TypedArrayConstructors 597/735 (81.22%) ctors/buffer-arg/new-instance-extensibility-sab.js {unsupported: [SharedArrayBuffer]} ctors/buffer-arg/proto-from-ctor-realm.js {unsupported: [Reflect]} ctors/buffer-arg/proto-from-ctor-realm-sab.js {unsupported: [SharedArrayBuffer, Reflect]} - ctors/buffer-arg/resizable-out-of-bounds.js {unsupported: [resizable-arraybuffer]} ctors/buffer-arg/returns-new-instance-sab.js {unsupported: [SharedArrayBuffer]} ctors/buffer-arg/toindex-bytelength-sab.js {unsupported: [SharedArrayBuffer]} ctors/buffer-arg/toindex-byteoffset-sab.js {unsupported: [SharedArrayBuffer]} @@ -3057,7 +2991,6 @@ built-ins/TypedArrayConstructors 597/735 (81.22%) ctors/typedarray-arg/custom-proto-access-throws.js {unsupported: [Reflect]} ctors/typedarray-arg/proto-from-ctor-realm.js {unsupported: [Reflect]} ctors/typedarray-arg/src-typedarray-big-throws.js - ctors/typedarray-arg/src-typedarray-resizable-buffer.js {unsupported: [resizable-arraybuffer]} ctors/typedarray-arg/use-custom-proto-if-object.js {unsupported: [Reflect]} ctors/typedarray-arg/use-default-proto-if-custom-proto-is-not-object.js ctors/no-species.js @@ -3173,26 +3106,18 @@ built-ins/TypedArrayConstructors 597/735 (81.22%) internals/HasProperty 17/17 (100.0%) internals/OwnPropertyKeys/BigInt 4/4 (100.0%) internals/OwnPropertyKeys 6/6 (100.0%) - internals/Set/BigInt 27/27 (100.0%) + internals/Set/BigInt 23/23 (100.0%) internals/Set/detached-buffer.js internals/Set/detached-buffer-key-is-not-numeric-index.js {unsupported: [Reflect]} internals/Set/detached-buffer-key-is-symbol.js {unsupported: [Reflect]} internals/Set/detached-buffer-realm.js internals/Set/indexed-value.js {unsupported: [Reflect]} - internals/Set/key-is-canonical-invalid-index-prototype-chain-set.js {unsupported: [Proxy]} - internals/Set/key-is-canonical-invalid-index-reflect-set.js {unsupported: [Reflect]} - internals/Set/key-is-in-bounds-receiver-is-not-typed-array.js {unsupported: [Reflect.set]} internals/Set/key-is-minus-zero.js {unsupported: [Reflect]} internals/Set/key-is-not-canonical-index.js {unsupported: [Reflect]} internals/Set/key-is-not-integer.js {unsupported: [Reflect]} internals/Set/key-is-not-numeric-index.js {unsupported: [Reflect]} internals/Set/key-is-out-of-bounds.js {unsupported: [Reflect]} - internals/Set/key-is-out-of-bounds-receiver-is-not-object.js {unsupported: [Reflect.set]} - internals/Set/key-is-out-of-bounds-receiver-is-not-typed-array.js {unsupported: [Reflect.set]} - internals/Set/key-is-out-of-bounds-receiver-is-proto.js {unsupported: [Reflect.set]} internals/Set/key-is-symbol.js {unsupported: [Reflect]} - internals/Set/key-is-valid-index-prototype-chain-set.js {unsupported: [Proxy]} - internals/Set/key-is-valid-index-reflect-set.js {unsupported: [Reflect]} internals/Set/resized-out-of-bounds-to-in-bounds-index.js {unsupported: [resizable-arraybuffer]} internals/Set/tonumber-value-detached-buffer.js {unsupported: [Reflect]} internals/Set/tonumber-value-throws.js @@ -3615,20 +3540,18 @@ language/comments 9/52 (17.31%) multi-line-asi-line-separator.js multi-line-asi-paragraph-separator.js -language/computed-property-names 37/48 (77.08%) +language/computed-property-names 35/48 (72.92%) class/accessor 4/4 (100.0%) class/method 11/11 (100.0%) class/static 14/14 (100.0%) - object/accessor/getter.js object/accessor/getter-super.js - object/accessor/setter.js object/accessor/setter-super.js object/method/generator.js object/method/super.js to-name-side-effects/class.js to-name-side-effects/numbers-class.js -language/destructuring 12/18 (66.67%) +language/destructuring 11/17 (64.71%) binding/syntax/array-elements-with-initializer.js binding/syntax/array-elements-with-object-patterns.js binding/syntax/array-rest-elements.js @@ -3640,7 +3563,6 @@ language/destructuring 12/18 (66.67%) binding/syntax/recursive-array-and-object-patterns.js binding/initialization-requires-object-coercible-null.js binding/initialization-requires-object-coercible-undefined.js - binding/typedarray-backed-by-resizable-buffer.js {unsupported: [resizable-arraybuffer]} language/directive-prologue 18/62 (29.03%) 14.1-1-s.js {non-strict: [-1]} @@ -7097,7 +7019,7 @@ language/statements/for 264/385 (68.57%) ~language/statements/for-await-of -language/statements/for-in 40/115 (34.78%) +language/statements/for-in 39/114 (34.21%) dstr/obj-rest-not-last-element-invalid.js {unsupported: [object-rest]} 12.6.4-2.js cptn-decl-abrupt-empty.js @@ -7130,7 +7052,6 @@ language/statements/for-in 40/115 (34.78%) let-block-with-newline.js non-strict let-identifier-with-newline.js non-strict order-enumerable-shadowed.js - resizable-buffer.js {unsupported: [resizable-arraybuffer]} scope-body-lex-boundary.js scope-body-lex-close.js scope-body-lex-open.js @@ -7139,7 +7060,7 @@ language/statements/for-in 40/115 (34.78%) scope-head-lex-open.js scope-head-var-none.js non-strict -language/statements/for-of 482/741 (65.05%) +language/statements/for-of 477/736 (64.81%) dstr/array-elem-init-assignment.js dstr/array-elem-init-evaluation.js dstr/array-elem-init-fn-name-arrow.js @@ -7617,11 +7538,6 @@ language/statements/for-of 482/741 (65.05%) scope-head-lex-close.js scope-head-lex-open.js scope-head-var-none.js non-strict - typedarray-backed-by-resizable-buffer.js {unsupported: [resizable-arraybuffer]} - typedarray-backed-by-resizable-buffer-grow-before-end.js {unsupported: [resizable-arraybuffer]} - typedarray-backed-by-resizable-buffer-grow-mid-iteration.js {unsupported: [resizable-arraybuffer]} - typedarray-backed-by-resizable-buffer-shrink-mid-iteration.js {unsupported: [resizable-arraybuffer]} - typedarray-backed-by-resizable-buffer-shrink-to-zero-mid-iteration.js {unsupported: [resizable-arraybuffer]} language/statements/function 230/451 (51.0%) dstr/ary-init-iter-close.js @@ -8530,7 +8446,7 @@ language/statements/while 13/38 (34.21%) let-identifier-with-newline.js non-strict tco-body.js {unsupported: [tail-call-optimization]} -language/statements/with 26/175 (14.86%) +language/statements/with 20/169 (11.83%) 12.10.1-8-s.js non-strict binding-blocked-by-unscopables.js non-strict cptn-abrupt-empty.js non-strict @@ -8541,16 +8457,10 @@ language/statements/with 26/175 (14.86%) decl-fun.js non-strict decl-gen.js non-strict decl-let.js non-strict - get-mutable-binding-binding-deleted-in-get-unscopables.js non-strict - get-mutable-binding-binding-deleted-in-get-unscopables-strict-mode.js non-strict has-property-err.js {unsupported: [Proxy]} labelled-fn-stmt.js non-strict let-array-with-newline.js non-strict let-block-with-newline.js non-strict - set-mutable-binding-binding-deleted-in-get-unscopables.js non-strict - set-mutable-binding-binding-deleted-in-get-unscopables-strict-mode.js non-strict - set-mutable-binding-binding-deleted-with-typed-array-in-proto-chain.js non-strict - set-mutable-binding-binding-deleted-with-typed-array-in-proto-chain-strict-mode.js non-strict strict-fn-decl-nested-1.js non-strict strict-fn-expr.js strict strict-fn-method.js strict