Issue #1240 - Part 4 - Implement parser support for BigInt literals. https://bugzilla.mozilla.org/show_bug.cgi?id=1505849 Partially based on https://bugzilla.mozilla.org/show_bug.cgi?id=1456568 Un-result-ified the BigInt XDR code, so we can enable it. https://bugzilla.mozilla.org/show_bug.cgi?id=1419094 Uninitialised memory read with BigInt right-shift https://bugzilla.mozilla.org/show_bug.cgi?id=1679003

This commit is contained in:
Brian Smith 2023-07-18 20:23:02 -05:00 committed by roytam1
commit dc23241afb
27 changed files with 394 additions and 80 deletions

View file

@ -1047,7 +1047,6 @@ MaybeWrapValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
if (rval.isBigInt()) {
return JS_WrapValue(cx, rval);
}
MOZ_ASSERT(rval.isSymbol());
return true;
}

View file

@ -667,7 +667,7 @@ class MOZ_NON_PARAM alignas(8) Value
#if defined(JS_NUNBOX32)
return data.s.payload.bi;
#elif defined(JS_PUNBOX64)
return reinterpret_cast<JS::BigInt*>(data.asBits & JSVAL_SHIFTED_TAG_BIGINT);
return reinterpret_cast<JS::BigInt*>(data.asBits & JSVAL_PAYLOAD_MASK);
#endif
}

View file

@ -21,6 +21,7 @@
#include "frontend/TokenStream.h"
#include "js/CharacterEncoding.h"
#include "vm/RegExpObject.h"
#include "vm/BigIntType.h"
#include "jsobjinlines.h"
@ -3434,6 +3435,7 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
case PNK_STRING:
case PNK_REGEXP:
case PNK_NUMBER:
case PNK_BIGINT:
case PNK_TRUE:
case PNK_FALSE:
case PNK_NULL:
@ -3604,7 +3606,7 @@ ASTSerializer::literal(ParseNode* pn, MutableHandleValue dst)
case PNK_REGEXP:
{
RootedObject re1(cx, pn->as<RegExpLiteral>().objbox()->object);
RootedObject re1(cx, pn->as<RegExpLiteral>().objbox()->object());
LOCAL_ASSERT(re1 && re1->is<RegExpObject>());
RootedObject re2(cx, CloneRegExpObject(cx, re1));
@ -3619,6 +3621,13 @@ ASTSerializer::literal(ParseNode* pn, MutableHandleValue dst)
val.setNumber(pn->as<NumericLiteral>().value());
break;
case PNK_BIGINT:
{
BigInt* x = pn->as<BigIntLiteral>().box()->value();
val.setBigInt(x);
break;
}
case PNK_NULL:
val.setNull();
break;

View file

@ -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());
@ -4834,6 +4842,15 @@ BytecodeEmitter::emitCopyDataProperties(CopyOption option)
return true;
}
bool
BytecodeEmitter::emitBigIntOp(BigInt* bigint)
{
if (!constList.append(BigIntValue(bigint))) {
return false;
}
return emitIndex32(JSOP_BIGINT, constList.length() - 1);
}
bool
BytecodeEmitter::emitIterator()
{
@ -9579,6 +9596,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 +10340,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 +10354,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 +10366,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 +10382,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);
}

View file

@ -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);

View file

@ -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

View file

@ -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);
}

View file

@ -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:

View file

@ -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;
@ -759,6 +766,12 @@ NumericLiteral::dump(int indent)
}
}
void
BigIntLiteral::dump(int indent)
{
fprintf(stderr, "(%s)", parseNodeNames[size_t(getKind())]);
}
void
RegExpLiteral::dump(int indent)
{
@ -962,23 +975,45 @@ LexicalScopeNode::dump(int indent)
}
#endif
ObjectBox::ObjectBox(JSObject* object, ObjectBox* traceLink)
: object(object),
traceLink(traceLink),
emitLink(nullptr)
TraceListNode::TraceListNode(js::gc::Cell* gcThing, TraceListNode* traceLink)
: gcThing(gcThing),
traceLink(traceLink)
{
MOZ_ASSERT(!object->is<JSFunction>());
MOZ_ASSERT(object->isTenured());
MOZ_ASSERT(gcThing->isTenured());
}
ObjectBox::ObjectBox(JSFunction* function, ObjectBox* traceLink)
: object(function),
traceLink(traceLink),
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()->is<JSFunction>());
}
ObjectBox::ObjectBox(JSFunction* function, TraceListNode* traceLink)
: TraceListNode(function, traceLink),
emitLink(nullptr)
{
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

View file

@ -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) ||
@ -1631,6 +1644,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 +2387,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

View file

@ -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 objbox;
return box;
}
ObjectBox*
ParserBase::newObjectBox(JSObject* obj)
{
return newTraceListNode<ObjectBox, JSObject>(obj);
}
BigIntBox*
ParserBase::newBigIntBox(BigInt* val)
{
return newTraceListNode<BigIntBox, BigInt>(val);
}
template <typename ParseHandler>
@ -10579,6 +10591,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 +11517,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:

View file

@ -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);

View file

@ -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

View file

@ -224,6 +224,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) {

View file

@ -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, "'${'") \

View file

@ -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,16 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
}
}
if (isBigInt) {
size_t length = userbuf.addressOfNextRawChar() - numStart - 1;
tokenbuf.clear();
if(!tokenbuf.reserve(length))
goto error;
tokenbuf.infallibleAppend(numStart, length);
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.
@ -1777,6 +1792,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 +1815,16 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
}
}
if (isBigInt) {
size_t length = userbuf.addressOfNextRawChar() - numStart - 1;
tokenbuf.clear();
if(!tokenbuf.reserve(length))
goto error;
tokenbuf.infallibleAppend(numStart, length);
tp->type = TOK_BIGINT;
goto out;
}
double dval;
const char16_t* dummy;
if (!GetPrefixInteger(cx, numStart, userbuf.addressOfNextRawChar(), radix,

View file

@ -1620,6 +1620,13 @@ BaselineCompiler::emit_JSOP_DOUBLE()
return true;
}
bool
BaselineCompiler::emit_JSOP_BIGINT()
{
frame.push(script->getConst(GET_UINT32_INDEX(pc)));
return true;
}
bool
BaselineCompiler::emit_JSOP_STRING()
{

View file

@ -65,6 +65,7 @@ namespace jit {
_(JSOP_UINT16) \
_(JSOP_UINT24) \
_(JSOP_DOUBLE) \
_(JSOP_BIGINT) \
_(JSOP_STRING) \
_(JSOP_SYMBOL) \
_(JSOP_OBJECT) \

View file

@ -1765,6 +1765,7 @@ IonBuilder::inspectOpcode(JSOp op)
return jsop_compare(op);
case JSOP_DOUBLE:
case JSOP_BIGINT:
pushConstant(info().getConst(pc));
return true;

View file

@ -1039,6 +1039,9 @@ js::Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
break;
}
case JOF_BIGINT:
// Fallthrough.
case JOF_DOUBLE: {
RootedValue v(cx, script->getConst(GET_UINT32_INDEX(pc)));
JSAutoByteString bytes;

View file

@ -56,6 +56,7 @@ enum {
JOF_ATOMOBJECT = 19, /* uint16_t constant index + object index */
JOF_SCOPE = 20, /* unsigned 32-bit scope index */
JOF_ENVCOORD = 21, /* embedded ScopeCoordinate immediate */
JOF_BIGINT = 22, /* uint32_t index for BigInt value */
JOF_TYPEMASK = 0x001f, /* mask for above immediate types */
JOF_NAME = 1 << 5, /* name operation */

View file

@ -83,7 +83,8 @@ js::XDRScriptConst(XDRState<mode>* xdr, MutableHandleValue vp)
SCRIPT_NULL,
SCRIPT_OBJECT,
SCRIPT_VOID,
SCRIPT_HOLE
SCRIPT_HOLE,
SCRIPT_BIGINT
};
ConstTag tag;
@ -104,6 +105,8 @@ js::XDRScriptConst(XDRState<mode>* xdr, MutableHandleValue vp)
tag = SCRIPT_OBJECT;
} else if (vp.isMagic(JS_ELEMENTS_HOLE)) {
tag = SCRIPT_HOLE;
} else if (vp.isBigInt()) {
tag = SCRIPT_BIGINT;
} else {
MOZ_ASSERT(vp.isUndefined());
tag = SCRIPT_VOID;
@ -176,6 +179,20 @@ js::XDRScriptConst(XDRState<mode>* xdr, MutableHandleValue vp)
if (mode == XDR_DECODE)
vp.setMagic(JS_ELEMENTS_HOLE);
break;
case SCRIPT_BIGINT: {
RootedBigInt bi(cx);
if (mode == XDR_ENCODE) {
bi = vp.toBigInt();
}
if(!XDRBigInt(xdr, &bi))
return false;
if (mode == XDR_DECODE) {
vp.setBigInt(bi);
}
break;
}
default:
// Fail in debug, but only soft-fail in release
MOZ_ASSERT(false, "Bad XDR value kind");
@ -3418,6 +3435,38 @@ js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst,
}
}
/* Constants */
AutoValueVector consts(cx);
if (nconsts != 0) {
GCPtrValue* vector = src->consts()->vector;
RootedValue val(cx);
RootedValue clone(cx);
for (unsigned i = 0; i < nconsts; i++) {
val = vector[i];
if (val.isDouble()) {
clone = val;
} else if (val.isBigInt()) {
if (cx->zone() == val.toBigInt()->zone()) {
clone.setBigInt(val.toBigInt());
} else {
RootedBigInt b(cx, val.toBigInt());
BigInt* copy = BigInt::copy(cx, b);
if (!copy) {
return false;
}
clone.setBigInt(copy);
}
} else {
MOZ_ASSERT_UNREACHABLE("bad script consts() element");
}
if (!consts.append(clone)) {
return false;
}
}
}
/* Objects */
AutoObjectVector objects(cx);
@ -3508,7 +3557,7 @@ js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst,
GCPtrValue* vector = Rebase<GCPtrValue>(dst, src, src->consts()->vector);
dst->consts()->vector = vector;
for (unsigned i = 0; i < nconsts; ++i)
MOZ_ASSERT_IF(vector[i].isGCThing(), vector[i].toString()->isAtom());
vector[i].init(consts[i]);
}
if (nobjects != 0) {
GCPtrObject* vector = Rebase<GCPtrObject>(dst, src, src->objects()->vector);

View file

@ -1987,6 +1987,8 @@ BigInt* BigInt::rshByAbsolute(ExclusiveContext* cx, HandleBigInt x, HandleBigInt
return nullptr;
}
if (!bitsShift) {
// If roundingCanOverflow, manually initialize the overflow digit.
result->setDigit(resultLength - 1, 0);
for (int i = digitShift; i < length; i++) {
result->setDigit(i - digitShift, x->digit(i));
}
@ -3144,16 +3146,14 @@ JS::ubi::Node::Size JS::ubi::Concrete<BigInt>::size(
return size;
}
#if 0 // Future XDR support
template <XDRMode mode>
XDRResult js::XDRBigInt(XDRState<mode>* xdr, MutableHandleBigInt bi) {
JSContext* cx = xdr->cx();
bool js::XDRBigInt(XDRState<mode>* xdr, MutableHandleBigInt bi) {
ExclusiveContext* cx = xdr->cx();
uint8_t sign;
uint32_t length;
if (mode == XDR_ENCODE) {
cx->check(bi);
sign = static_cast<uint8_t>(bi->isNegative());
uint64_t sz = bi->digitLength() * sizeof(BigInt::Digit);
// As the maximum source code size is currently UINT32_MAX code units
@ -3165,8 +3165,10 @@ XDRResult js::XDRBigInt(XDRState<mode>* xdr, MutableHandleBigInt bi) {
length = static_cast<uint32_t>(sz);
}
MOZ_TRY(xdr->codeUint8(&sign));
MOZ_TRY(xdr->codeUint32(&length));
if(!xdr->codeUint8(&sign))
return false;
if(!xdr->codeUint32(&length))
return false;
MOZ_RELEASE_ASSERT(length % sizeof(BigInt::Digit) == 0);
uint32_t digitLength = length / sizeof(BigInt::Digit);
@ -3179,7 +3181,8 @@ XDRResult js::XDRBigInt(XDRState<mode>* xdr, MutableHandleBigInt bi) {
std::uninitialized_copy_n(bi->digits().Elements(), digitLength, buf.get());
}
MOZ_TRY(xdr->codeBytes(buf.get(), length));
if(!xdr->codeBytes(buf.get(), length))
return false;
if (mode == XDR_DECODE) {
BigInt* res = BigInt::createUninitialized(cx, digitLength, sign);
@ -3190,12 +3193,10 @@ XDRResult js::XDRBigInt(XDRState<mode>* xdr, MutableHandleBigInt bi) {
bi.set(res);
}
return Ok();
return true;
}
template XDRResult js::XDRBigInt(XDRState<XDR_ENCODE>* xdr,
MutableHandleBigInt bi);
template bool js::XDRBigInt(XDRState<XDR_ENCODE>* xdr, MutableHandleBigInt bi);
template bool js::XDRBigInt(XDRState<XDR_DECODE>* xdr, MutableHandleBigInt bi);
template XDRResult js::XDRBigInt(XDRState<XDR_DECODE>* xdr,
MutableHandleBigInt bi);
#endif

View file

@ -26,7 +26,6 @@
namespace JS {
#if 0 // Future XDR support
class BigInt;
} // namespace JS
@ -34,12 +33,11 @@ class BigInt;
namespace js {
template <XDRMode mode>
XDRResult XDRBigInt(XDRState<mode>* xdr, MutableHandle<JS::BigInt*> bi);
bool XDRBigInt(XDRState<mode>* xdr, MutableHandle<JS::BigInt*> bi);
} // namespace js
namespace JS {
#endif
class BigInt final : public js::gc::TenuredCell {
public:
@ -332,11 +330,8 @@ class BigInt final : public js::gc::TenuredCell {
friend struct JSStructuredCloneReader;
friend struct JSStructuredCloneWriter;
#if 0 // Future XDR support
template <js::XDRMode mode>
friend js::XDRResult js::XDRBigInt(js::XDRState<mode>* xdr,
MutableHandle<BigInt*> bi);
#endif
friend bool js::XDRBigInt(js::XDRState<mode>* xdr, MutableHandle<BigInt*> bi);
BigInt() = delete;
BigInt(const BigInt& other) = delete;

View file

@ -4125,6 +4125,13 @@ CASE(JSOP_IS_CONSTRUCTING)
PUSH_MAGIC(JS_IS_CONSTRUCTING);
END_CASE(JSOP_IS_CONSTRUCTING)
CASE(JSOP_BIGINT)
{
PUSH_COPY(script->getConst(GET_UINT32_INDEX(REGS.pc)));
MOZ_ASSERT(REGS.sp[-1].isBigInt());
}
END_CASE(JSOP_BIGINT)
DEFAULT()
{
char numBuf[12];

View file

@ -2357,14 +2357,20 @@
* Operands:
* Stack: arg => rval
*/ \
macro(JSOP_DYNAMIC_IMPORT, 234, "call-import", NULL, 1, 1, 1, JOF_BYTE)
macro(JSOP_DYNAMIC_IMPORT, 234, "call-import", NULL, 1, 1, 1, JOF_BYTE) \
/*
* Pushes a BigInt constant onto the stack.
* Category: Literals
* Type: Constants
* Operands: uint32_t constIndex
* Stack: => val
*/ \
macro(JSOP_BIGINT, 235, "bigint", NULL, 5, 0, 1, JOF_BIGINT)
/*
* In certain circumstances it may be useful to "pad out" the opcode space to
* a power of two. Use this macro to do so.
*/
#define FOR_EACH_TRAILING_UNUSED_OPCODE(macro) \
macro(235) \
macro(236) \
macro(237) \
macro(238) \

View file

@ -8709,7 +8709,7 @@ js::CompileAsmJS(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList,
// generating bytecode for asm.js functions, allowing this asm.js module
// function to be the finished result.
MOZ_ASSERT(funbox->function()->isInterpreted());
funbox->object = moduleFun;
funbox->clobberFunction(moduleFun);
// Success! Write to the console with a "warning" message.
*validated = true;