summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartok <martok@martoks-place.de>2023-08-29 23:51:32 +0200
committerMartok <martok@martoks-place.de>2023-08-30 09:38:14 +0200
commit8b9d69f912cc52fb8a4b007041922bad8e131e82 (patch)
treef0ccd1aa04370e047d13b982f32ab639cef43be6
parent92ecaaeaa90e87655ce58ad560e7621cb8001c4d (diff)
downloaduxp-8b9d69f912cc52fb8a4b007041922bad8e131e82.tar.gz
Issue #2298 - Support '#x in obj' ergonomic brand check syntax
-rw-r--r--js/src/frontend/BytecodeEmitter.cpp19
-rw-r--r--js/src/frontend/Parser.cpp24
-rw-r--r--js/src/js.msg1
3 files changed, 40 insertions, 4 deletions
diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
index cb249c254c..462edfd91e 100644
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -7754,10 +7754,23 @@ bool
BytecodeEmitter::emitLeftAssociative(ListNode* node)
{
// Left-associative operator chain.
- if (!emitTree(node->head()))
- return false;
JSOp op = node->getOp();
- ParseNode* nextExpr = node->head()->pn_next;
+ ParseNode* headExpr = node->head();
+ if (op == JSOP_IN && headExpr->isKind(PNK_NAME) && headExpr->as<NameNode>().isPrivateName()) {
+ // {Goanna} The only way a "naked" private name can show up as the leftmost side of an in-chain
+ // is from an ergonomic brand check (`this.#x in ...` would be a PNK_DOT child node).
+ // Instead of going through the emitTree machinery, we pretend that this identifier
+ // reference is actually a string, which allows us to use the JSOP_IN interpreter routines.
+ // This erroneously doesn't call updateLineNumberNotes, but this is not a big issue:
+ // the begin pos is correct as we're on the start of the current tree, the end is on the
+ // same line anyway.
+ if (!emitAtomOp(headExpr->as<NameNode>().atom(), JSOP_STRING))
+ return false;
+ } else {
+ if (!emitTree(headExpr))
+ return false;
+ }
+ ParseNode* nextExpr = headExpr->pn_next;
do {
if (!emitTree(nextExpr))
return false;
diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
index 9da893f94f..4e3181e06b 100644
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -8949,6 +8949,15 @@ Parser<ParseHandler>::orExpr1(InHandling inHandling, YieldHandling yieldHandling
if (!tokenStream.getToken(&tok))
return null();
+ // Ensure that if we have a private name lhs we are legally constructing a
+ // `#x in obj` expression:
+ if (handler.isPrivateName(pn)) {
+ if (tok != TOK_IN) {
+ error(JSMSG_ILLEGAL_PRIVATE_NAME);
+ return null();
+ }
+ }
+
ParseNodeKind pnk;
if (tok == TOK_IN ? inHandling == InAllowed : TokenKindIsBinaryOp(tok)) {
// We're definitely not in a destructuring context, so report any
@@ -8985,7 +8994,20 @@ Parser<ParseHandler>::orExpr1(InHandling inHandling, YieldHandling yieldHandling
// If we have not detected a mixing error at this point, record that
// we have an unparenthesized expression, in case we have one later.
unparenthesizedExpression = EnforcedParentheses::CoalesceExpr;
- break;
+ break;
+ case TOK_IN:
+ // if the LHS is a private name, and the operator is In,
+ // ensure we're construcing an ergonomic brand check of
+ // '#x in y', rather than having a higher precedence operator
+ // like + cause a different reduction, such as
+ // 1 + #x in y.
+ if (handler.isPrivateName(pn)) {
+ if (depth > 0 && Precedence(kindStack[depth - 1]) >= Precedence(PNK_IN)) {
+ error(JSMSG_ILLEGAL_PRIVATE_NAME);
+ return null();
+ }
+ }
+ break;
default:
// Do nothing in other cases.
break;
diff --git a/js/src/js.msg b/js/src/js.msg
index 1cb571d189..340cfd6ad2 100644
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -364,6 +364,7 @@ MSG_DEF(JSMSG_BAD_NEW_OPTIONAL, 0, JSEXN_SYNTAXERR, "new keyword cannot b
MSG_DEF(JSMSG_BAD_OPTIONAL_TEMPLATE, 0, JSEXN_SYNTAXERR, "tagged template cannot be used with optional chain")
MSG_DEF(JSMSG_ESCAPED_KEYWORD, 0, JSEXN_SYNTAXERR, "keywords must be written literally, without embedded escapes")
MSG_DEF(JSMSG_FIELDS_NOT_SUPPORTED, 0, JSEXN_SYNTAXERR, "fields are not currently supported")
+MSG_DEF(JSMSG_ILLEGAL_PRIVATE_NAME, 0, JSEXN_SYNTAXERR, "private names aren't valid in this context")
// asm.js
MSG_DEF(JSMSG_USE_ASM_TYPE_FAIL, 1, JSEXN_TYPEERR, "asm.js type error: {0}")