зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
76482bccd7
Коммит
58a139cf97
|
@ -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);
|
Загрузка…
Ссылка в новой задаче