diff --git a/rhino/src/main/java/org/mozilla/javascript/CodeGenerator.java b/rhino/src/main/java/org/mozilla/javascript/CodeGenerator.java index 8abe18f550..7bf3954773 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: @@ -962,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)); @@ -1057,6 +1064,7 @@ private void generateCallFunAndThis(Node left) { stackChange(2); break; } + case Token.GETPROP_OPTIONAL: case Token.GETPROP: case Token.GETELEM: { @@ -1068,6 +1076,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 e0c74906a1..38cb6a8378 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++) { @@ -917,7 +923,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) { @@ -1239,7 +1245,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); @@ -1875,18 +1881,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..0ec7113290 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: { @@ -2260,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/Parser.java b/rhino/src/main/java/org/mozilla/javascript/Parser.java index 09aab64f8c..ea2a26bf7b 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Parser.java +++ b/rhino/src/main/java/org/mozilla/javascript/Parser.java @@ -2795,6 +2795,7 @@ private AstNode memberExprTail(boolean allowCallSyntax, AstNode pn) throws IOExc int tt = peekToken(); switch (tt) { case Token.DOT: + case Token.QUESTION_DOT: case Token.DOTDOT: lineno = ts.lineno; pn = propertyAccess(tt, pn); @@ -2924,8 +2925,28 @@ 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(); + pn.setType(Token.QUESTION_DOT); + 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); + f.setType(Token.CALL_OPTIONAL); + return f; + } + switch (token) { case Token.THROW: // needed for generator.throw(); @@ -2957,7 +2978,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}) @@ -2982,9 +3002,20 @@ 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.QUESTION_DOT && result instanceof PropertyGet) { + result.setType(Token.QUESTION_DOT); + } return result; } + 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/ScriptRuntime.java b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java index 337f7e0bba..0a1b6720f7 100644 --- a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java +++ b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java @@ -1739,6 +1739,22 @@ 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 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); @@ -1971,6 +1987,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) { @@ -2617,6 +2638,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/SpecialOptionalRef.java b/rhino/src/main/java/org/mozilla/javascript/SpecialOptionalRef.java new file mode 100644 index 0000000000..d2f88cd1ee --- /dev/null +++ b/rhino/src/main/java/org/mozilla/javascript/SpecialOptionalRef.java @@ -0,0 +1,29 @@ +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); + } + + @Deprecated + @Override + public Object set(Context cx, Object value) { + throw new IllegalStateException(); + } +} diff --git a/rhino/src/main/java/org/mozilla/javascript/Token.java b/rhino/src/main/java/org/mozilla/javascript/Token.java index d1493280f7..ca323a9f37 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Token.java +++ b/rhino/src/main/java/org/mozilla/javascript/Token.java @@ -120,117 +120,121 @@ 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 ... - NULLISH_COALESCING = 178, // nullish coalescing (??) - LAST_TOKEN = 178; + 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, + NULLISH_COALESCING = 182, // nullish coalescing (??) + LAST_TOKEN = 183; /** * Returns a name for the token. If Rhino is compiled with certain hardcoded debugging flags in @@ -607,6 +611,8 @@ public static String typeToName(int token) { return "TEMPLATE_LITERAL_SUBST"; case TAGGED_TEMPLATE_LITERAL: return "TAGGED_TEMPLATE_LITERAL"; + case QUESTION_DOT: + 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 d84f589813..56e3840122 100644 --- a/rhino/src/main/java/org/mozilla/javascript/TokenStream.java +++ b/rhino/src/main/java/org/mozilla/javascript/TokenStream.java @@ -1149,6 +1149,9 @@ && peekChar() == '!' if (matchChar('?')) { return Token.NULLISH_COALESCING; } + if (matchChar('.')) { + return Token.QUESTION_DOT; + } return Token.HOOK; case ':': if (matchChar(':')) { 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 45225ebb53..f87694daac 100644 --- a/rhino/src/main/java/org/mozilla/javascript/ast/AstNode.java +++ b/rhino/src/main/java/org/mozilla/javascript/ast/AstNode.java @@ -126,6 +126,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/main/java/org/mozilla/javascript/optimizer/BodyCodegen.java b/rhino/src/main/java/org/mozilla/javascript/optimizer/BodyCodegen.java index c0c4aeb90f..cfcdd9318d 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 @@ -1517,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: @@ -2317,7 +2336,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 +2356,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 +4099,36 @@ 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 + /* + 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( + "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) { 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/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..95ea83c554 --- /dev/null +++ b/rhino/src/test/java/org/mozilla/javascript/tests/OptionalChainingOperatorTests.java @@ -0,0 +1,104 @@ +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(); + assertEquals( + Undefined.instance, + cx.evaluateString( + scope, "var nul = null; nul?.a", sourceName, 1, null)); + + 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)); + + 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)); + + // 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, + // cx.evaluateString( + // scope, + // "{}?.a", + // sourceName, + // 1, + // null)); + // assertEquals( + // Undefined.instance, + // cx.evaluateString( + // scope, + // "var b = {}; for (const key of b.a) ", + // sourceName, + // 1, + // null)); + + return null; + }); + } +} diff --git a/tests/test262 b/tests/test262 index 98c7e9114e..242f6f98f0 160000 --- a/tests/test262 +++ b/tests/test262 @@ -1 +1 @@ -Subproject commit 98c7e9114e7eb663f895953c3750c6b270660a51 +Subproject commit 242f6f98f0f86c0a3276929b4a450438526057cb diff --git a/tests/testsrc/test262.properties b/tests/testsrc/test262.properties index 5e9ffee1f3..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]} @@ -6201,7 +6123,32 @@ language/expressions/object 863/1169 (73.82%) yield-non-strict-access.js non-strict yield-non-strict-syntax.js non-strict -~language/expressions/optional-chaining +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 + 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 + super-property-optional-call.js language/expressions/postfix-decrement 9/37 (24.32%) arguments.js strict @@ -7072,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 @@ -7105,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 @@ -7114,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 @@ -7592,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 @@ -8505,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 @@ -8516,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