summaryrefslogtreecommitdiff
path: root/js/src/frontend
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/frontend')
-rw-r--r--js/src/frontend/BytecodeEmitter.cpp51
-rw-r--r--js/src/frontend/BytecodeEmitter.h17
-rw-r--r--js/src/frontend/FoldConstants.cpp11
-rw-r--r--js/src/frontend/FullParseHandler.h16
-rw-r--r--js/src/frontend/NameFunctions.cpp3
-rw-r--r--js/src/frontend/ParseNode.cpp66
-rw-r--r--js/src/frontend/ParseNode.h84
-rw-r--r--js/src/frontend/Parser.cpp88
-rw-r--r--js/src/frontend/Parser.h16
-rw-r--r--js/src/frontend/SharedContext.h5
-rw-r--r--js/src/frontend/SyntaxParseHandler.h13
-rw-r--r--js/src/frontend/TokenKind.h1
-rw-r--r--js/src/frontend/TokenStream.cpp46
13 files changed, 360 insertions, 57 deletions
diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
index e5a47c496a..462edfd91e 100644
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -1093,6 +1093,11 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
*answer = false;
return true;
+ case PNK_BIGINT:
+ MOZ_ASSERT(pn->is<BigIntLiteral>());
+ *answer = false;
+ return true;
+
// |this| can throw in derived class constructors, including nested arrow
// functions or eval.
case PNK_THIS:
@@ -4225,6 +4230,9 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje
case PNK_NUMBER:
vp.setNumber(as<NumericLiteral>().value());
return true;
+ case PNK_BIGINT:
+ vp.setBigInt(as<BigIntLiteral>().box()->value());
+ return true;
case PNK_TEMPLATE_STRING:
case PNK_STRING:
vp.setString(as<NameNode>().atom());
@@ -4835,6 +4843,15 @@ BytecodeEmitter::emitCopyDataProperties(CopyOption option)
}
bool
+BytecodeEmitter::emitBigIntOp(BigInt* bigint)
+{
+ if (!constList.append(BigIntValue(bigint))) {
+ return false;
+ }
+ return emitIndex32(JSOP_BIGINT, constList.length() - 1);
+}
+
+bool
BytecodeEmitter::emitIterator()
{
// Convert iterable to iterator.
@@ -7737,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;
@@ -9579,6 +9609,12 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::
return false;
break;
+ case PNK_BIGINT:
+ if (!emitBigIntOp(pn->as<BigIntLiteral>().box()->value())) {
+ return false;
+ }
+ break;
+
case PNK_REGEXP:
if (!emitRegExp(objectList.add(pn->as<RegExpLiteral>().objbox())))
return false;
@@ -10317,7 +10353,7 @@ CGConstList::finish(ConstArray* array)
MOZ_ASSERT(length() == array->length);
for (unsigned i = 0; i < length(); i++)
- array->vector[i] = list[i];
+ array->vector[i] = vector[i];
}
/*
@@ -10331,6 +10367,7 @@ CGConstList::finish(ConstArray* array)
unsigned
CGObjectList::add(ObjectBox* objbox)
{
+ MOZ_ASSERT(objbox->isObjectBox());
MOZ_ASSERT(!objbox->emitLink);
objbox->emitLink = lastbox;
lastbox = objbox;
@@ -10342,7 +10379,7 @@ CGObjectList::indexOf(JSObject* obj)
{
MOZ_ASSERT(length > 0);
unsigned index = length - 1;
- for (ObjectBox* box = lastbox; box->object != obj; box = box->emitLink)
+ for (ObjectBox* box = lastbox; box->object() != obj; box = box->emitLink)
index--;
return index;
}
@@ -10358,8 +10395,8 @@ CGObjectList::finish(ObjectArray* array)
do {
--cursor;
MOZ_ASSERT(!*cursor);
- MOZ_ASSERT(objbox->object->isTenured());
- *cursor = objbox->object;
+ MOZ_ASSERT(objbox->object()->isTenured());
+ *cursor = objbox->object();
} while ((objbox = objbox->emitLink) != nullptr);
MOZ_ASSERT(cursor == array->vector);
}
diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h
index 732a1f24f8..9ca4856cf5 100644
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -35,20 +35,21 @@ class SharedContext;
class TokenStream;
class CGConstList {
- Vector<Value> list;
+ Rooted<ValueVector> vector;
public:
- explicit CGConstList(ExclusiveContext* cx) : list(cx) {}
+ explicit CGConstList(ExclusiveContext* cx)
+ : vector(cx, ValueVector(cx))
+ { }
MOZ_MUST_USE bool append(const Value& v) {
- MOZ_ASSERT_IF(v.isString(), v.toString()->isAtom());
- return list.append(v);
+ return vector.append(v);
}
- size_t length() const { return list.length(); }
+ size_t length() const { return vector.length(); }
void finish(ConstArray* array);
};
struct CGObjectList {
uint32_t length; /* number of emitted so far objects */
- ObjectBox* lastbox; /* last emitted object */
+ ObjectBox* lastbox; /* last emitted object */
CGObjectList() : length(0), lastbox(nullptr) {}
@@ -198,7 +199,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
return innermostEmitterScope_;
}
- CGConstList constList; /* constants to be included with the script */
+ CGConstList constList; /* double and bigint values used by script */
CGObjectList objectList; /* list of emitted objects */
CGScopeList scopeList; /* list of emitted scopes */
CGTryNoteList tryNoteList; /* list of emitted try notes */
@@ -478,6 +479,8 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool emitNumberOp(double dval);
+ MOZ_MUST_USE bool emitBigIntOp(BigInt* bigint);
+
MOZ_MUST_USE bool emitThisLiteral(ThisLiteral* pn);
MOZ_MUST_USE bool emitGetFunctionThis(ParseNode* pn);
MOZ_MUST_USE bool emitGetFunctionThis(const mozilla::Maybe<uint32_t>& offset);
diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp
index 9fb963f63e..b457c43942 100644
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -395,6 +395,7 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
case PNK_THIS:
case PNK_ELISION:
case PNK_NUMBER:
+ case PNK_BIGINT:
case PNK_NEW:
case PNK_GENERATOR:
case PNK_GENEXP:
@@ -485,6 +486,7 @@ IsEffectless(ParseNode* node)
node->isKind(PNK_FALSE) ||
node->isKind(PNK_STRING) ||
node->isKind(PNK_TEMPLATE_STRING) ||
+ node->isKind(PNK_BIGINT) ||
node->isKind(PNK_NUMBER) ||
node->isKind(PNK_NULL) ||
node->isKind(PNK_RAW_UNDEFINED) ||
@@ -503,6 +505,9 @@ Boolish(ParseNode* pn, bool isNullish = false)
return (isNullish || isNonZeroNumber) ? Truthy : Falsy;
}
+ case PNK_BIGINT:
+ return (pn->as<BigIntLiteral>().box()->value()->isZero()) ? Falsy : Truthy;
+
case PNK_STRING:
case PNK_TEMPLATE_STRING: {
bool isNonZeroLengthString = (pn->as<NameNode>().atom()->length() > 0);
@@ -591,6 +596,8 @@ FoldTypeOfExpr(ExclusiveContext* cx, UnaryNode* node, Parser<FullParseHandler>&
result = cx->names().string;
else if (expr->isKind(PNK_NUMBER))
result = cx->names().number;
+ else if (expr->isKind(PNK_BIGINT))
+ result = cx->names().bigint;
else if (expr->isKind(PNK_NULL))
result = cx->names().object;
else if (expr->isKind(PNK_TRUE) || expr->isKind(PNK_FALSE))
@@ -1699,6 +1706,10 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
MOZ_ASSERT(pn->is<NumericLiteral>());
return true;
+ case PNK_BIGINT:
+ MOZ_ASSERT(pn->is<BigIntLiteral>());
+ return true;
+
case PNK_SUPERBASE:
case PNK_TYPEOFNAME: {
#ifdef DEBUG
diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h
index 14733d74f2..d2c8bbc2df 100644
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -151,6 +151,18 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
return new_<NumericLiteral>(value, decimalPoint, pos);
}
+ // The Boxer object here is any object that can allocate BigIntBoxes.
+ // Specifically, a Boxer has a .newBigIntBox(T) method that accepts a
+ // BigInt* argument and returns a BigIntBox*.
+ template <class Boxer>
+ BigIntLiteralType newBigInt(BigInt* bi, const TokenPos& pos, Boxer& boxer) {
+ BigIntBox* box = boxer.newBigIntBox(bi);
+ if (!box) {
+ return null();
+ }
+ return new_<BigIntLiteral>(box, pos);
+ }
+
BooleanLiteralType newBooleanLiteral(bool cond, const TokenPos& pos) {
return new_<BooleanLiteral>(cond, pos);
}
@@ -953,6 +965,10 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
return node->isKind(PNK_NAME);
}
+ bool isPrivateName(Node node) {
+ return node->isKind(PNK_NAME) && node->as<NameNode>().isPrivateName();
+ }
+
bool isArgumentsAnyParentheses(Node node, ExclusiveContext* cx) {
return node->isKind(PNK_NAME) && node->as<NameNode>().atom() == cx->names().arguments;
}
diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp
index 6ddb554ad7..3fbdd3fc8a 100644
--- a/js/src/frontend/NameFunctions.cpp
+++ b/js/src/frontend/NameFunctions.cpp
@@ -420,6 +420,9 @@ class NameResolver
MOZ_ASSERT(cur->is<NumericLiteral>());
break;
+ case PNK_BIGINT:
+ MOZ_ASSERT(cur->is<BigIntLiteral>());
+ break;
case PNK_TYPEOFNAME:
case PNK_SUPERBASE:
diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp
index 7ad470865f..065efa8380 100644
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -218,6 +218,10 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
MOZ_ASSERT(pn->is<NumericLiteral>());
return PushResult::Recyclable;
+ case PNK_BIGINT:
+ MOZ_ASSERT(pn->is<BigIntLiteral>());
+ return PushResult::Recyclable;
+
// Nodes with a single non-null child.
case PNK_TYPEOFNAME:
case PNK_TYPEOFEXPR:
@@ -716,6 +720,9 @@ ParseNode::dump(int indent)
case PN_NUMBER:
as<NumericLiteral>().dump(indent);
return;
+ case PN_BIGINT:
+ as<BigIntLiteral>().dump(indent);
+ return;
case PN_REGEXP:
as<RegExpLiteral>().dump(indent);
return;
@@ -760,6 +767,12 @@ NumericLiteral::dump(int indent)
}
void
+BigIntLiteral::dump(int indent)
+{
+ fprintf(stderr, "(%s)", parseNodeNames[size_t(getKind())]);
+}
+
+void
RegExpLiteral::dump(int indent)
{
fprintf(stderr, "(%s)", parseNodeNames[size_t(getKind())]);
@@ -962,23 +975,45 @@ LexicalScopeNode::dump(int indent)
}
#endif
-ObjectBox::ObjectBox(JSObject* object, ObjectBox* traceLink)
- : object(object),
- traceLink(traceLink),
+TraceListNode::TraceListNode(js::gc::Cell* gcThing, TraceListNode* traceLink)
+ : gcThing(gcThing),
+ traceLink(traceLink)
+{
+ MOZ_ASSERT(gcThing->isTenured());
+}
+
+BigIntBox*
+TraceListNode::asBigIntBox()
+{
+ MOZ_ASSERT(isBigIntBox());
+ return static_cast<BigIntBox*>(this);
+}
+
+ObjectBox*
+TraceListNode::asObjectBox()
+{
+ MOZ_ASSERT(isObjectBox());
+ return static_cast<ObjectBox*>(this);
+}
+
+BigIntBox::BigIntBox(BigInt* bi, TraceListNode* traceLink)
+ : TraceListNode(bi, traceLink)
+{
+}
+
+ObjectBox::ObjectBox(JSObject* obj, TraceListNode* traceLink)
+ : TraceListNode(obj, traceLink),
emitLink(nullptr)
{
- MOZ_ASSERT(!object->is<JSFunction>());
- MOZ_ASSERT(object->isTenured());
+ MOZ_ASSERT(!object()->is<JSFunction>());
}
-ObjectBox::ObjectBox(JSFunction* function, ObjectBox* traceLink)
- : object(function),
- traceLink(traceLink),
+ObjectBox::ObjectBox(JSFunction* function, TraceListNode* traceLink)
+ : TraceListNode(function, traceLink),
emitLink(nullptr)
{
- MOZ_ASSERT(object->is<JSFunction>());
+ MOZ_ASSERT(object()->is<JSFunction>());
MOZ_ASSERT(asFunctionBox()->function() == function);
- MOZ_ASSERT(object->isTenured());
}
FunctionBox*
@@ -989,16 +1024,17 @@ ObjectBox::asFunctionBox()
}
/* static */ void
-ObjectBox::TraceList(JSTracer* trc, ObjectBox* listHead)
+TraceListNode::TraceList(JSTracer* trc, TraceListNode* listHead)
{
- for (ObjectBox* box = listHead; box; box = box->traceLink)
- box->trace(trc);
+ for (TraceListNode* node = listHead; node; node = node->traceLink) {
+ node->trace(trc);
+ }
}
void
-ObjectBox::trace(JSTracer* trc)
+TraceListNode::trace(JSTracer* trc)
{
- TraceRoot(trc, &object, "parser.object");
+ TraceGenericPointerRoot(trc, &gcThing, "parser.traceListNode");
}
void
diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h
index 46977ee253..439a6d8937 100644
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -12,6 +12,7 @@
#include "builtin/ModuleObject.h"
#include "frontend/TokenStream.h"
+#include "vm/BigIntType.h"
namespace js {
namespace frontend {
@@ -20,6 +21,7 @@ class ParseContext;
class FullParseHandler;
class FunctionBox;
class ObjectBox;
+class BigIntBox;
#define FOR_EACH_PARSE_NODE_KIND(F) \
F(NOP) \
@@ -53,6 +55,7 @@ class ObjectBox;
F(OBJECT_PROPERTY_NAME) \
F(COMPUTED_NAME) \
F(NUMBER) \
+ F(BIGINT) \
F(STRING) \
F(TEMPLATE_STRING_LIST) \
F(TEMPLATE_STRING) \
@@ -524,6 +527,8 @@ IsTypeofKind(ParseNodeKind kind)
* regexp: RegExp model object
* PNK_NUMBER (NumericLiteral)
* value: double value of numeric literal
+ * PNK_BIGINT (BigIntLiteral)
+ * box: BigIntBox holding BigInt* value
* PNK_TRUE, PNK_FALSE (BooleanLiteral)
* pn_op: JSOp bytecode
* PNK_NULL (NullLiteral)
@@ -571,6 +576,7 @@ enum ParseNodeArity
PN_LIST, /* generic singly linked list */
PN_NAME, /* name, label, string */
PN_NUMBER, /* numeric literal */
+ PN_BIGINT, /* BigInt literal */
PN_REGEXP, /* regexp literal */
PN_LOOP, /* loop control (break/continue) */
PN_SCOPE /* lexical scope */
@@ -613,6 +619,7 @@ enum ParseNodeArity
macro(RawUndefinedLiteral, RawUndefinedLiteralType, asRawUndefinedLiteral) \
\
macro(NumericLiteral, NumericLiteralType, asNumericLiteral) \
+ macro(BigIntLiteral, BigIntLiteralType, asBigIntLiteral) \
\
macro(RegExpLiteral, RegExpLiteralType, asRegExpLiteral) \
\
@@ -828,6 +835,11 @@ class ParseNode
double value; /* aligned numeric literal value */
DecimalPoint decimalPoint; /* Whether the number has a decimal point */
} number;
+ struct {
+ private:
+ friend class BigIntLiteral;
+ BigIntBox* box;
+ } bigint;
class {
private:
friend class LoopControlStatement;
@@ -849,6 +861,7 @@ class ParseNode
/* True if pn is a parsenode representing a literal constant. */
bool isLiteral() const {
return isKind(PNK_NUMBER) ||
+ isKind(PNK_BIGINT) ||
isKind(PNK_STRING) ||
isKind(PNK_TRUE) ||
isKind(PNK_FALSE) ||
@@ -940,6 +953,10 @@ class NameNode : public ParseNode
JSAtom* atom() const {
return pn_u.name.atom;
}
+
+ bool isPrivateName() const {
+ return atom()->asPropertyName()->latin1OrTwoByteChar(0) == '#';
+ }
ParseNode* initializer() const {
return pn_u.name.initOrStmt;
@@ -1631,6 +1648,30 @@ class NumericLiteral : public ParseNode
}
};
+class BigIntLiteral : public ParseNode
+{
+ public:
+ BigIntLiteral(BigIntBox* bibox, const TokenPos& pos)
+ : ParseNode(PNK_BIGINT, JSOP_NOP, PN_BIGINT, pos)
+ {
+ pn_u.bigint.box = bibox;
+ }
+
+ static bool test(const ParseNode& node) {
+ bool match = node.isKind(PNK_BIGINT);
+ MOZ_ASSERT_IF(match, node.isArity(PN_BIGINT));
+ return match;
+ }
+
+#ifdef DEBUG
+ void dump(int indent);
+#endif
+
+ BigIntBox* box() const {
+ return pn_u.bigint.box;
+ }
+};
+
class LexicalScopeNode : public ParseNode
{
public:
@@ -2350,25 +2391,48 @@ ParseNode::isConstant()
}
}
-class ObjectBox
+class TraceListNode
{
- public:
- JSObject* object;
+ protected:
+ js::gc::Cell* gcThing;
+ TraceListNode* traceLink;
+
+ TraceListNode(js::gc::Cell* gcThing, TraceListNode* traceLink);
+
+ bool isBigIntBox() const { return gcThing->is<BigInt>(); }
+ bool isObjectBox() const { return gcThing->is<JSObject>(); }
+
+ BigIntBox* asBigIntBox();
+ ObjectBox* asObjectBox();
- ObjectBox(JSObject* object, ObjectBox* traceLink);
- bool isFunctionBox() { return object->is<JSFunction>(); }
- FunctionBox* asFunctionBox();
virtual void trace(JSTracer* trc);
- static void TraceList(JSTracer* trc, ObjectBox* listHead);
+ public:
+ static void TraceList(JSTracer* trc, TraceListNode* listHead);
+};
+
+class BigIntBox : public TraceListNode
+{
+ public:
+ BigIntBox(BigInt* bi, TraceListNode* link);
+ BigInt* value() const { return gcThing->as<BigInt>(); }
+};
+class ObjectBox : public TraceListNode
+{
protected:
friend struct CGObjectList;
-
- ObjectBox* traceLink;
ObjectBox* emitLink;
+
+ ObjectBox(JSFunction* function, TraceListNode* link);
- ObjectBox(JSFunction* function, ObjectBox* traceLink);
+ public:
+ ObjectBox(JSObject* obj, TraceListNode* link);
+
+ JSObject* object() const { return gcThing->as<JSObject>(); }
+
+ bool isFunctionBox() const { return object()->is<JSFunction>(); }
+ FunctionBox* asFunctionBox();
};
enum ParseReportKind
diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
index a1b669bf91..4e3181e06b 100644
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -438,7 +438,7 @@ UsedNameTracker::rewind(RewindToken token)
r.front().value().resetToScope(token.scriptId, token.scopeId);
}
-FunctionBox::FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* traceListHead,
+FunctionBox::FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, TraceListNode* traceListHead,
JSFunction* fun, uint32_t toStringStart,
Directives directives, bool extraWarnings,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind)
@@ -882,11 +882,11 @@ Parser<FullParseHandler>::setAwaitHandling(AwaitHandling awaitHandling)
parser->setAwaitHandling(awaitHandling);
}
-template <typename ParseHandler>
-ObjectBox*
-Parser<ParseHandler>::newObjectBox(JSObject* obj)
+template <typename BoxT, typename ArgT>
+BoxT*
+ParserBase::newTraceListNode(ArgT* arg)
{
- MOZ_ASSERT(obj);
+ MOZ_ASSERT(arg);
/*
* We use JSContext.tempLifoAlloc to allocate parsed objects and place them
@@ -896,15 +896,27 @@ Parser<ParseHandler>::newObjectBox(JSObject* obj)
* function.
*/
- ObjectBox* objbox = alloc.new_<ObjectBox>(obj, traceListHead);
- if (!objbox) {
+ BoxT* box = alloc.template new_<BoxT>(arg, traceListHead);
+ if (!box) {
ReportOutOfMemory(context);
return nullptr;
}
- traceListHead = objbox;
+ traceListHead = box;
+
+ return box;
+}
+
+ObjectBox*
+ParserBase::newObjectBox(JSObject* obj)
+{
+ return newTraceListNode<ObjectBox, JSObject>(obj);
+}
- return objbox;
+BigIntBox*
+ParserBase::newBigIntBox(BigInt* val)
+{
+ return newTraceListNode<BigIntBox, BigInt>(val);
}
template <typename ParseHandler>
@@ -8937,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
@@ -8973,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;
@@ -10579,6 +10613,37 @@ Parser<ParseHandler>::newRegExp()
return handler.newRegExp(reobj, pos(), *this);
}
+template <>
+BigIntLiteral*
+Parser<FullParseHandler>::newBigInt()
+{
+ // The token's charBuffer contains the DecimalIntegerLiteral or
+ // NumericLiteralBase production, and as such does not include the
+ // BigIntLiteralSuffix (the trailing "n"). Note that NumericLiteralBase
+ // productions may start with 0[bBoOxX], indicating binary/octal/hex.
+ const auto& chars = tokenStream.getTokenbuf();
+ mozilla::Range<const char16_t> source(chars.begin(), chars.length());
+
+ BigInt* b = js::ParseBigIntLiteral(context, source);
+ if (!b) {
+ return null();
+ }
+
+ // newBigInt immediately puts "b" in a BigIntBox, which is allocated using
+ // tempLifoAlloc, avoiding any potential GC. Therefore it's OK to pass a
+ // raw pointer.
+ return handler.newBigInt(b, pos(), *this);
+}
+
+template <>
+SyntaxParseHandler::BigIntLiteralType
+Parser<SyntaxParseHandler>::newBigInt()
+{
+ // The tokenizer has already checked the syntax of the bigint.
+
+ return handler.newBigInt();
+}
+
template <typename ParseHandler>
void
Parser<ParseHandler>::checkDestructuringAssignmentTarget(Node expr, TokenPos exprPos,
@@ -11474,6 +11539,9 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
case TOK_NUMBER:
return newNumber(tokenStream.currentToken());
+ case TOK_BIGINT:
+ return newBigInt();
+
case TOK_TRUE:
return handler.newBooleanLiteral(true, pos());
case TOK_FALSE:
diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h
index fd1bd034c4..4a8e038d6e 100644
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -779,8 +779,8 @@ class ParserBase : public StrictModeGetter
TokenStream tokenStream;
LifoAlloc::Mark tempPoolMark;
- /* list of parsed objects for GC tracing */
- ObjectBox* traceListHead;
+ /* list of parsed objects and BigInts for GC tracing */
+ TraceListNode* traceListHead;
/* innermost parse context (stack-allocated) */
ParseContext* pc;
@@ -915,6 +915,13 @@ class ParserBase : public StrictModeGetter
bool warnOnceAboutExprClosure();
bool warnOnceAboutForEach();
+ ObjectBox* newObjectBox(JSObject* obj);
+ BigIntBox* newBigIntBox(BigInt* val);
+
+private:
+ template <typename BoxT, typename ArgT>
+ BoxT* newTraceListNode(ArgT* arg);
+
protected:
enum InvokedPrediction { PredictUninvoked = false, PredictInvoked = true };
enum ForInitLocation { InForInit, NotInForInit };
@@ -1085,7 +1092,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
{
friend class Parser;
LifoAlloc::Mark mark;
- ObjectBox* traceListHead;
+ TraceListNode* traceListHead;
};
Mark mark() const {
Mark m;
@@ -1174,7 +1181,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
* Allocate a new parsed object or function container from
* cx->tempLifoAlloc.
*/
- ObjectBox* newObjectBox(JSObject* obj);
+ public:
FunctionBox* newFunctionBox(FunctionNodeType funNode, JSFunction* fun, uint32_t toStringStart,
Directives directives,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
@@ -1660,6 +1667,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
const mozilla::Maybe<DeclarationKind>& maybeDecl, ListNodeType literal);
ListNodeType arrayInitializer(YieldHandling yieldHandling, PossibleError* possibleError);
RegExpLiteralType newRegExp();
+ BigIntLiteralType newBigInt();
ListNodeType objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError);
diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h
index a241907482..bc0212054b 100644
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -443,7 +443,7 @@ class FunctionBox : public ObjectBox, public SharedContext
FunctionContextFlags funCxFlags;
- FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* traceListHead, JSFunction* fun,
+ FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, TraceListNode* traceListHead, JSFunction* fun,
uint32_t toStringStart, Directives directives, bool extraWarnings,
GeneratorKind generatorKind, FunctionAsyncKind asyncKind);
@@ -467,7 +467,8 @@ class FunctionBox : public ObjectBox, public SharedContext
void initWithEnclosingParseContext(ParseContext* enclosing, FunctionSyntaxKind kind);
ObjectBox* toObjectBox() override { return this; }
- JSFunction* function() const { return &object->as<JSFunction>(); }
+ JSFunction* function() const { return &object()->as<JSFunction>(); }
+ void clobberFunction(JSFunction* function) { gcThing = function; }
Scope* compilationEnclosingScope() const override {
// This method is used to distinguish the outermost SharedContext. If
diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h
index 130a5da61d..80ca13ce45 100644
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -104,6 +104,9 @@ class SyntaxParseHandler
// Node representing the "async" name, which may actually be a
// contextual keyword.
NodePotentialAsyncKeyword,
+
+ // Node representing a private name. Handled mostly like NodeUnparenthesizedName.
+ NodePrivateName,
// Valuable for recognizing potential destructuring patterns.
NodeUnparenthesizedArray,
@@ -212,6 +215,8 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
return NodePotentialAsyncKeyword;
if (name == cx->names().eval)
return NodeUnparenthesizedEvalName;
+ if (name->length() >= 1 && name->latin1OrTwoByteChar(0) == '#')
+ return NodePrivateName;
return NodeUnparenthesizedName;
}
@@ -224,6 +229,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
}
NumericLiteralType newNumber(double value, DecimalPoint decimalPoint, const TokenPos& pos) { return NodeGeneric; }
+ BigIntLiteralType newBigInt() { return NodeGeneric; }
BooleanLiteralType newBooleanLiteral(bool cond, const TokenPos& pos) { return NodeGeneric; }
NameNodeType newStringLiteral(JSAtom* atom, const TokenPos& pos) {
@@ -613,7 +619,8 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
return node == NodeUnparenthesizedArgumentsName ||
node == NodeUnparenthesizedEvalName ||
node == NodeUnparenthesizedName ||
- node == NodePotentialAsyncKeyword;
+ node == NodePotentialAsyncKeyword ||
+ node == NodePrivateName;
}
bool isNameAnyParentheses(Node node) {
@@ -624,6 +631,10 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
node == NodeParenthesizedName;
}
+ bool isPrivateName(Node node) {
+ return node == NodePrivateName;
+ }
+
bool isArgumentsAnyParentheses(Node node, ExclusiveContext* cx) {
return node == NodeUnparenthesizedArgumentsName || node == NodeParenthesizedArgumentsName;
}
diff --git a/js/src/frontend/TokenKind.h b/js/src/frontend/TokenKind.h
index 232e373dcf..3e1ff68bbd 100644
--- a/js/src/frontend/TokenKind.h
+++ b/js/src/frontend/TokenKind.h
@@ -74,6 +74,7 @@
macro(PRIVATE_NAME, "private identifier") \
macro(NUMBER, "numeric literal") \
macro(STRING, "string literal") \
+ macro(BIGINT, "bigint literal") \
\
/* start of template literal with substitutions */ \
macro(TEMPLATE_HEAD, "'${'") \
diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp
index a201e42a52..b11c7df584 100644
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -1382,6 +1382,7 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
const char16_t* identStart;
NameVisibility identVisibility;
bool hadUnicodeEscape;
+ bool isBigInt = false;
// Check if in the middle of a template string. Have to get this out of
// the way first.
@@ -1619,6 +1620,10 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
}
} while (true);
}
+ if (c == 'n') {
+ isBigInt = true;
+ c = getCharIgnoreEOL();
+ }
ungetCharIgnoreEOL(c);
if (c != EOF) {
@@ -1638,6 +1643,19 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
}
}
+ if (isBigInt) {
+ size_t length = userbuf.addressOfNextRawChar() - numStart - 1;
+ tokenbuf.clear();
+ if(!tokenbuf.reserve(length > 0 ? length : 1))
+ goto error;
+ if(length > 0)
+ tokenbuf.infallibleAppend(numStart, length);
+ else
+ tokenbuf.infallibleAppend("0", 1);
+ tp->type = TOK_BIGINT;
+ goto out;
+ }
+
// Unlike identifiers and strings, numbers cannot contain escaped
// chars, so we don't need to use tokenbuf. Instead we can just
// convert the char16_t characters in userbuf to the numeric value.
@@ -1677,7 +1695,7 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
//
if (c1kind == BasePrefix) {
tp = newToken(-1);
- int radix;
+ int radix = 10;
c = getCharIgnoreEOL();
if (c == 'x' || c == 'X') {
radix = 16;
@@ -1777,6 +1795,10 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
hasExp = false;
goto decimal_rest;
}
+ if (c == 'n') {
+ isBigInt = true;
+ c = getCharIgnoreEOL();
+ }
ungetCharIgnoreEOL(c);
if (c != EOF) {
@@ -1796,6 +1818,28 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
}
}
+ if (isBigInt) {
+ size_t length = userbuf.addressOfNextRawChar() - numStart - 1;
+ tokenbuf.clear();
+ if(!tokenbuf.reserve(radix == 10 ? length : (length + 2)))
+ goto error;
+ switch(radix)
+ {
+ case 2:
+ tokenbuf.infallibleAppend("0b", 2);
+ break;
+ case 8:
+ tokenbuf.infallibleAppend("0o", 2);
+ break;
+ case 16:
+ tokenbuf.infallibleAppend("0x", 2);
+ break;
+ }
+ tokenbuf.infallibleAppend(numStart, length);
+ tp->type = TOK_BIGINT;
+ goto out;
+ }
+
double dval;
const char16_t* dummy;
if (!GetPrefixInteger(cx, numStart, userbuf.addressOfNextRawChar(), radix,