Bug 1605835: Allow BigInts as property names. r=mgaudet

BytecodeEmitter.cpp:
- Drive-by change in `emitObjLiteralValue()`: Call MOZ_CRASH instead of returning
  `false` for unhandled parse nodes. And also assert the function is only called
  with valid parse nodes in the first place.

NameFunctions.cpp:
- BigInts aren't currently supported for guessed names, so just I've only updated
  the assertion condition.

Parser.cpp:
- Update spec reference from `NumericLiteralBase` to `NonDecimalIntegerLiteral`.
- The new `bigIntAtom()` function runs counter to the deferred gc-things work,
  but this inevitable as long as function names are computed in the parser.

Differential Revision: https://phabricator.services.mozilla.com/D58757

--HG--
extra : moz-landing-system : lando
This commit is contained in:
André Bargull 2020-01-09 16:02:55 +00:00
Родитель 76482bccd7
Коммит 58a139cf97
12 изменённых файлов: 297 добавлений и 18 удалений

Просмотреть файл

@ -3123,7 +3123,8 @@ bool ASTSerializer::propertyName(ParseNode* key, MutableHandleValue dst) {
}
LOCAL_ASSERT(key->isKind(ParseNodeKind::StringExpr) ||
key->isKind(ParseNodeKind::NumberExpr));
key->isKind(ParseNodeKind::NumberExpr) ||
key->isKind(ParseNodeKind::BigIntExpr));
return literal(key, dst);
}

Просмотреть файл

@ -23,7 +23,6 @@
#include <algorithm>
#include <string.h>
#include "jsnum.h" // NumberToAtom
#include "jstypes.h" // JS_BIT
#include "ds/Nestable.h" // Nestable
@ -3735,6 +3734,11 @@ bool BytecodeEmitter::emitDestructuringOpsObject(ListNode* pattern,
// [stack]... SET? RHS LREF* RHS KEY
return false;
}
} else if (key->isKind(ParseNodeKind::BigIntExpr)) {
if (!emitBigIntOp(&key->as<BigIntLiteral>())) {
// [stack]... SET? RHS LREF* RHS KEY
return false;
}
} else if (key->isKind(ParseNodeKind::ObjectPropertyName) ||
key->isKind(ParseNodeKind::StringExpr)) {
if (!emitAtomOp(key->as<NameNode>().atom(), JSOP_GETPROP,
@ -7606,6 +7610,15 @@ void BytecodeEmitter::isPropertyListObjLiteralCompatible(ListNode* obj,
break;
}
}
// BigInt keys aren't yet supported.
if (key->isKind(ParseNodeKind::BigIntExpr)) {
keysOK = false;
break;
}
MOZ_ASSERT(key->isKind(ParseNodeKind::ObjectPropertyName) ||
key->isKind(ParseNodeKind::StringExpr) ||
key->isKind(ParseNodeKind::NumberExpr));
AccessorType accessorType =
prop->is<PropertyDefinition>()
@ -7753,8 +7766,18 @@ bool BytecodeEmitter::emitPropertyList(ListNode* obj, PropertyEmitter& pe,
if (key->isKind(ParseNodeKind::NumberExpr)) {
MOZ_ASSERT(accessorType == AccessorType::None);
NumericLiteral* literal = &key->as<NumericLiteral>();
RootedAtom keyAtom(cx, NumberToAtom(cx, literal->value()));
RootedAtom keyAtom(cx, key->as<NumericLiteral>().toAtom(cx));
if (!keyAtom) {
return false;
}
if (!emitAnonymousFunctionWithName(propVal, keyAtom)) {
// [stack] CTOR? OBJ CTOR? KEY VAL
return false;
}
} else if (key->isKind(ParseNodeKind::BigIntExpr)) {
MOZ_ASSERT(accessorType == AccessorType::None);
RootedAtom keyAtom(cx, key->as<BigIntLiteral>().toAtom(cx));
if (!keyAtom) {
return false;
}
@ -7806,16 +7829,24 @@ bool BytecodeEmitter::emitPropertyList(ListNode* obj, PropertyEmitter& pe,
(type == ClassBody && propdef->as<ClassMethod>().isStatic())
? PropertyEmitter::Kind::Static
: PropertyEmitter::Kind::Prototype;
if (key->isKind(ParseNodeKind::NumberExpr)) {
if (key->isKind(ParseNodeKind::NumberExpr) ||
key->isKind(ParseNodeKind::BigIntExpr)) {
// [stack] CTOR? OBJ
if (!pe.prepareForIndexPropKey(Some(propdef->pn_pos.begin), kind)) {
// [stack] CTOR? OBJ CTOR?
return false;
}
if (key->isKind(ParseNodeKind::NumberExpr)) {
if (!emitNumberOp(key->as<NumericLiteral>().value())) {
// [stack] CTOR? OBJ CTOR? KEY
return false;
}
} else {
if (!emitBigIntOp(&key->as<BigIntLiteral>())) {
// [stack] CTOR? OBJ CTOR? KEY
return false;
}
}
if (!pe.prepareForIndexPropValue()) {
// [stack] CTOR? OBJ CTOR? KEY
return false;
@ -8055,6 +8086,7 @@ bool BytecodeEmitter::isRHSObjLiteralCompatible(ParseNode* value) {
bool BytecodeEmitter::emitObjLiteralValue(ObjLiteralCreationData* data,
ParseNode* value) {
MOZ_ASSERT(isRHSObjLiteralCompatible(value));
if (value->isKind(ParseNodeKind::NumberExpr)) {
double numValue = value->as<NumericLiteral>().value();
int32_t i = 0;
@ -8093,7 +8125,7 @@ bool BytecodeEmitter::emitObjLiteralValue(ObjLiteralCreationData* data,
return false;
}
} else {
return false;
MOZ_CRASH("Unexpected parse node");
}
return true;
}

Просмотреть файл

@ -40,7 +40,6 @@
#include "frontend/ValueUsage.h" // ValueUsage
#include "js/RootingAPI.h" // JS::Rooted, JS::Handle
#include "js/TypeDecls.h" // jsbytecode
#include "vm/BigIntType.h" // BigInt
#include "vm/BytecodeUtil.h" // JSOp
#include "vm/Instrumentation.h" // InstrumentationKind
#include "vm/Interpreter.h" // CheckIsObjectKind, CheckIsCallableKind

Просмотреть файл

@ -459,7 +459,7 @@ static bool FoldType(JSContext* cx, FullParseHandler* handler, ParseNode** pnp,
case ParseNodeKind::StringExpr:
if (pn->isKind(ParseNodeKind::NumberExpr)) {
JSAtom* atom = NumberToAtom(cx, pn->as<NumericLiteral>().value());
JSAtom* atom = pn->as<NumericLiteral>().toAtom(cx);
if (!atom) {
return false;
}
@ -1090,12 +1090,13 @@ static bool FoldElement(JSContext* cx, FullParseHandler* handler,
name = atom->asPropertyName();
}
} else if (key->isKind(ParseNodeKind::NumberExpr)) {
double number = key->as<NumericLiteral>().value();
auto* numeric = &key->as<NumericLiteral>();
double number = numeric->value();
if (number != ToUint32(number)) {
// Optimization 2: We have something like expr[3.14]. The number
// isn't an array index, so it converts to a string ("3.14"),
// enabling optimization 3 below.
JSAtom* atom = NumberToAtom(cx, number);
JSAtom* atom = numeric->toAtom(cx);
if (!atom) {
return false;
}

Просмотреть файл

@ -924,6 +924,7 @@ class FullParseHandler {
bool isUsableAsObjectPropertyName(Node node) {
return node->isKind(ParseNodeKind::NumberExpr) ||
node->isKind(ParseNodeKind::BigIntExpr) ||
node->isKind(ParseNodeKind::ObjectPropertyName) ||
node->isKind(ParseNodeKind::StringExpr) ||
node->isKind(ParseNodeKind::ComputedName);

Просмотреть файл

@ -286,7 +286,8 @@ class NameResolver : public ParseNodeVisitor<NameResolver> {
return false;
}
} else {
MOZ_ASSERT(left->isKind(ParseNodeKind::ComputedName));
MOZ_ASSERT(left->isKind(ParseNodeKind::ComputedName) ||
left->isKind(ParseNodeKind::BigIntExpr));
}
} else {
// Don't have consecutive '<' characters, and also don't start

Просмотреть файл

@ -12,6 +12,7 @@
#include "jsnum.h"
#include "frontend/Parser.h"
#include "vm/BigIntType.h"
#include "vm/RegExpObject.h"
#include "vm/JSContext-inl.h"
@ -417,6 +418,18 @@ bool BigIntLiteral::isZero() {
BigInt* BigIntLiteral::value() { return box()->value(); }
JSAtom* BigIntLiteral::toAtom(JSContext* cx) {
RootedBigInt bi(cx, getOrCreate(cx));
if (!bi) {
return nullptr;
}
return BigIntToAtom<CanGC>(cx, bi);
}
JSAtom* NumericLiteral::toAtom(JSContext* cx) const {
return NumberToAtom(cx, value());
}
RegExpObject* RegExpCreationData::createRegExp(JSContext* cx) const {
MOZ_ASSERT(buf_);
return RegExpObject::createSyntaxChecked(cx, buf_.get(), length_, flags_,

Просмотреть файл

@ -1514,6 +1514,9 @@ class NumericLiteral : public ParseNode {
void setValue(double v) { value_ = v; }
void setDecimalPoint(DecimalPoint d) { decimalPoint_ = d; }
// Return the decimal string representation of this numeric literal.
JSAtom* toAtom(JSContext* cx) const;
};
class BigIntLiteral : public ParseNode {
@ -1563,6 +1566,9 @@ class BigIntLiteral : public ParseNode {
// Can be used when deferred allocation mode is enabled.
BigInt* getOrCreate(JSContext* cx);
// Return the decimal string representation of this BigInt literal.
JSAtom* toAtom(JSContext* cx);
bool isZero();
};

Просмотреть файл

@ -46,6 +46,7 @@
#include "frontend/TokenStream.h"
#include "irregexp/RegExpParser.h"
#include "js/RegExpFlags.h" // JS::RegExpFlags
#include "vm/BigIntType.h"
#include "vm/BytecodeUtil.h"
#include "vm/JSAtom.h"
#include "vm/JSContext.h"
@ -9690,9 +9691,9 @@ GeneralParser<ParseHandler, Unit>::newRegExp() {
template <typename Unit>
BigIntLiteral* Parser<FullParseHandler, Unit>::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.
// NonDecimalIntegerLiteral production, and as such does not include the
// BigIntLiteralSuffix (the trailing "n"). Note that NonDecimalIntegerLiteral
// productions start with 0[bBoOxX], indicating binary/octal/hex.
const auto& chars = tokenStream.getCharBuffer();
if (this->parseInfo_.isDeferred()) {
@ -9737,6 +9738,19 @@ GeneralParser<ParseHandler, Unit>::newBigInt() {
return asFinalParser()->newBigInt();
}
template <class ParseHandler, typename Unit>
JSAtom* GeneralParser<ParseHandler, Unit>::bigIntAtom() {
// See newBigInt() for a description about |chars'| contents.
const auto& chars = tokenStream.getCharBuffer();
mozilla::Range<const char16_t> source(chars.begin(), chars.length());
RootedBigInt bi(cx_, js::ParseBigIntLiteral(cx_, source));
if (!bi) {
return nullptr;
}
return BigIntToAtom<CanGC>(cx_, bi);
}
// |exprPossibleError| is the PossibleError state within |expr|,
// |possibleError| is the surrounding PossibleError state.
template <class ParseHandler, typename Unit>
@ -10005,6 +10019,13 @@ typename ParseHandler::Node GeneralParser<ParseHandler, Unit>::propertyName(
}
return newNumber(anyChars.currentToken());
case TokenKind::BigInt:
propAtom.set(bigIntAtom());
if (!propAtom.get()) {
return null();
}
return newBigInt();
case TokenKind::String: {
propAtom.set(anyChars.currentToken().atom());
uint32_t index;
@ -10034,7 +10055,7 @@ typename ParseHandler::Node GeneralParser<ParseHandler, Unit>::propertyName(
static bool TokenKindCanStartPropertyName(TokenKind tt) {
return TokenKindIsPossibleIdentifierName(tt) || tt == TokenKind::String ||
tt == TokenKind::Number || tt == TokenKind::LeftBracket ||
tt == TokenKind::Mul;
tt == TokenKind::Mul || tt == TokenKind::BigInt;
}
template <class ParseHandler, typename Unit>

Просмотреть файл

@ -1430,6 +1430,8 @@ class MOZ_STACK_CLASS GeneralParser : public PerHandlerParser<ParseHandler> {
inline BigIntLiteralType newBigInt();
JSAtom* bigIntAtom();
protected:
// Match the current token against the BindingIdentifier production with
// the given Yield parameter. If there is no match, report a syntax

Просмотреть файл

@ -0,0 +1,23 @@
// BigInts currently don't participate when computing guessed function names.
if (typeof displayName !== "function") {
var {displayName} = SpecialPowers.Cu.getJSTestingFunctions();
}
var p = {};
p[1] = function(){};
p[2n] = function(){};
assertEq(displayName(p[1]), "p[1]");
assertEq(displayName(p[2]), "");
var q = {
1: [function(){}],
2n: [function(){}],
};
assertEq(displayName(q[1][0]), "q[1]<");
assertEq(displayName(q[2][0]), "q<");
if (typeof reportCompare === "function")
reportCompare(true, true);

Просмотреть файл

@ -0,0 +1,179 @@
// BigInt literals as property keys.
{
let o = {
0n: "0",
1n: "1",
// 2**31
2147483647n: "2^31-1",
2147483648n: "2^31",
2147483649n: "2^31+1",
// 2**32
4294967295n: "2^32-1",
4294967296n: "2^32",
4294967297n: "2^32+1",
// 2n**63n
9223372036854775807n: "2^63-1",
9223372036854775808n: "2^63",
9223372036854775809n: "2^63+1",
// 2n**64n
18446744073709551615n: "2^64-1",
18446744073709551616n: "2^64",
18446744073709551617n: "2^64+1",
};
assertEq(o[0], "0");
assertEq(o[1], "1");
assertEq(o[2147483647], "2^31-1");
assertEq(o[2147483648], "2^31");
assertEq(o[2147483649], "2^31+1");
assertEq(o[4294967295], "2^32-1");
assertEq(o[4294967296], "2^32");
assertEq(o[4294967297], "2^32+1");
assertEq(o["9223372036854775807"], "2^63-1");
assertEq(o["9223372036854775808"], "2^63");
assertEq(o["9223372036854775809"], "2^63+1");
assertEq(o["18446744073709551615"], "2^64-1");
assertEq(o["18446744073709551616"], "2^64");
assertEq(o["18446744073709551617"], "2^64+1");
}
// With non-decimal different base.
{
let o = {
0b1n: "1",
0o2n: "2",
0x3n: "3",
};
assertEq(o[1], "1");
assertEq(o[2], "2");
assertEq(o[3], "3");
}
// With numeric separators.
{
let o = {
1_2_3n: "123",
};
assertEq(o[123], "123");
}
// BigInt literals as method property names.
{
let o = {
1n() {},
*2n() {},
async 3n() {},
async* 4n() {},
get 5n() {},
set 6n(x) {},
};
assertEqArray(Object.getOwnPropertyNames(o), [
"1", "2", "3", "4", "5", "6",
]);
assertEq(o[1].name, "1");
assertEq(o[2].name, "2");
assertEq(o[3].name, "3");
assertEq(o[4].name, "4");
assertEq(Object.getOwnPropertyDescriptor(o, 5).get.name, "get 5");
assertEq(Object.getOwnPropertyDescriptor(o, 6).set.name, "set 6");
}
// BigInt literals as class method property names.
{
class C {
1n() {}
*2n() {}
async 3n() {}
async* 4n() {}
get 5n() {}
set 6n(x) {}
}
let o = C.prototype;
assertEqArray(Object.getOwnPropertyNames(o), [
"1", "2", "3", "4", "5", "6",
"constructor",
]);
assertEq(o[1].name, "1");
assertEq(o[2].name, "2");
assertEq(o[3].name, "3");
assertEq(o[4].name, "4");
assertEq(Object.getOwnPropertyDescriptor(o, 5).get.name, "get 5");
assertEq(Object.getOwnPropertyDescriptor(o, 6).set.name, "set 6");
}
// BigInt literals as static class method property names.
{
class C {
static 1n() {}
static *2n() {}
static async 3n() {}
static async* 4n() {}
static get 5n() {}
static set 6n(x) {}
}
let o = C;
// NB: Sort names because lazily resolved "length" and "name" properties are
// inserted in the wrong order.
assertEqArray(Object.getOwnPropertyNames(o).sort(), [
"1", "2", "3", "4", "5", "6",
"length", "name", "prototype",
]);
assertEq(o[1].name, "1");
assertEq(o[2].name, "2");
assertEq(o[3].name, "3");
assertEq(o[4].name, "4");
assertEq(Object.getOwnPropertyDescriptor(o, 5).get.name, "get 5");
assertEq(Object.getOwnPropertyDescriptor(o, 6).set.name, "set 6");
}
// BigInt literals as class field property names.
{
let o = new class {
1n;
2n = "ok";
};
assertEq(o[1], undefined);
assertEq(o[2], "ok");
}
// In binding destructuring contexts.
{
let {0n: a} = ["ok"];
assertEq(a, "ok");
}
// In assignment destructuring contexts.
{
let a;
({0n: a} = ["ok"]);
assertEq(a, "ok");
}
// BigInt literals as inferred names.
{
let o = {
0xan: function(){},
};
assertEq(o[10].name, "10");
}
if (typeof reportCompare === "function")
reportCompare(true, true);