mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-22 09:45:41 +00:00
Bug 875002 - Allow shorthand properties in object literals; r=jorendorff
--HG-- extra : rebase_source : cf461ea4585b0a9fbd51f8260c4d8c4b76ed17ed
This commit is contained in:
parent
81c2062aad
commit
9dda5f5b76
@ -3218,7 +3218,7 @@ EmitDestructuringOpsHelper(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode
|
||||
pn3 = pn2;
|
||||
} else {
|
||||
JS_ASSERT(pn->isKind(PNK_OBJECT));
|
||||
JS_ASSERT(pn2->isKind(PNK_COLON));
|
||||
JS_ASSERT(pn2->isKind(PNK_COLON) || pn2->isKind(PNK_SHORTHAND));
|
||||
|
||||
ParseNode *key = pn2->pn_left;
|
||||
if (key->isKind(PNK_NUMBER)) {
|
||||
@ -5923,11 +5923,6 @@ EmitConditionalExpression(ExclusiveContext *cx, BytecodeEmitter *bce, Conditiona
|
||||
MOZ_NEVER_INLINE static bool
|
||||
EmitObject(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
{
|
||||
if (pn->pn_xflags & PNX_DESTRUCT) {
|
||||
bce->reportError(pn, JSMSG_BAD_OBJECT_INIT);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(pn->pn_xflags & PNX_NONCONST) && pn->pn_head && bce->checkSingletonContext())
|
||||
return EmitSingletonInitialiser(cx, bce, pn);
|
||||
|
||||
|
@ -252,20 +252,19 @@ class FullParseHandler
|
||||
return literal;
|
||||
}
|
||||
|
||||
bool addPropertyDefinition(ParseNode *literal, ParseNode *name, ParseNode *expr) {
|
||||
ParseNode *propdef = newBinary(PNK_COLON, name, expr, JSOP_INITPROP);
|
||||
bool addPropertyDefinition(ParseNode *literal, ParseNode *name, ParseNode *expr,
|
||||
bool isShorthand = false) {
|
||||
JS_ASSERT(literal->isArity(PN_LIST));
|
||||
ParseNode *propdef = newBinary(isShorthand ? PNK_SHORTHAND : PNK_COLON, name, expr,
|
||||
JSOP_INITPROP);
|
||||
if (isShorthand)
|
||||
literal->pn_xflags |= PNX_NONCONST;
|
||||
if (!propdef)
|
||||
return false;
|
||||
literal->append(propdef);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addShorthandPropertyDefinition(ParseNode *literal, ParseNode *name) {
|
||||
JS_ASSERT(literal->isArity(PN_LIST));
|
||||
literal->pn_xflags |= PNX_DESTRUCT | PNX_NONCONST; // XXX why PNX_DESTRUCT?
|
||||
return addPropertyDefinition(literal, name, name);
|
||||
}
|
||||
|
||||
bool addAccessorPropertyDefinition(ParseNode *literal, ParseNode *name, ParseNode *fn, JSOp op)
|
||||
{
|
||||
JS_ASSERT(literal->isArity(PN_LIST));
|
||||
|
@ -150,8 +150,9 @@ class NameResolver
|
||||
break;
|
||||
|
||||
case PNK_COLON:
|
||||
case PNK_SHORTHAND:
|
||||
/*
|
||||
* Record the PNK_COLON but skip the PNK_OBJECT so we're not
|
||||
* Record the PNK_COLON/SHORTHAND but skip the PNK_OBJECT so we're not
|
||||
* flagged as a contributor.
|
||||
*/
|
||||
pos--;
|
||||
@ -221,7 +222,7 @@ class NameResolver
|
||||
for (int pos = size - 1; pos >= 0; pos--) {
|
||||
ParseNode *node = toName[pos];
|
||||
|
||||
if (node->isKind(PNK_COLON)) {
|
||||
if (node->isKind(PNK_COLON) || node->isKind(PNK_SHORTHAND)) {
|
||||
ParseNode *left = node->pn_left;
|
||||
if (left->isKind(PNK_NAME) || left->isKind(PNK_STRING)) {
|
||||
if (!appendPropertyReference(left->pn_atom))
|
||||
|
@ -460,7 +460,7 @@ Parser<FullParseHandler>::cloneLeftHandSide(ParseNode *opn)
|
||||
ParseNode *pn2;
|
||||
if (opn->isKind(PNK_OBJECT)) {
|
||||
JS_ASSERT(opn2->isArity(PN_BINARY));
|
||||
JS_ASSERT(opn2->isKind(PNK_COLON));
|
||||
JS_ASSERT(opn2->isKind(PNK_COLON) || opn2->isKind(PNK_SHORTHAND));
|
||||
|
||||
ParseNode *tag = cloneParseTree(opn2->pn_left);
|
||||
if (!tag)
|
||||
@ -469,7 +469,7 @@ Parser<FullParseHandler>::cloneLeftHandSide(ParseNode *opn)
|
||||
if (!target)
|
||||
return nullptr;
|
||||
|
||||
pn2 = handler.new_<BinaryNode>(PNK_COLON, JSOP_INITPROP, opn2->pn_pos, tag, target);
|
||||
pn2 = handler.new_<BinaryNode>(opn2->getKind(), JSOP_INITPROP, opn2->pn_pos, tag, target);
|
||||
} else if (opn2->isArity(PN_NULLARY)) {
|
||||
JS_ASSERT(opn2->isKind(PNK_ELISION));
|
||||
pn2 = cloneParseTree(opn2);
|
||||
|
@ -73,6 +73,7 @@ class UpvarCookie
|
||||
F(COMMA) \
|
||||
F(CONDITIONAL) \
|
||||
F(COLON) \
|
||||
F(SHORTHAND) \
|
||||
F(POS) \
|
||||
F(NEG) \
|
||||
F(PREINCREMENT) \
|
||||
@ -388,9 +389,8 @@ enum ParseNodeKind
|
||||
* PNK_COLON binary key-value pair in object initializer or
|
||||
* destructuring lhs
|
||||
* pn_left: property id, pn_right: value
|
||||
* var {x} = object destructuring shorthand shares
|
||||
* PN_NAME node for x on left and right of PNK_COLON
|
||||
* node in PNK_OBJECT's list, has PNX_DESTRUCT flag
|
||||
* PNK_SHORTHAND binary Same fields as PNK_COLON. This is used for object
|
||||
* literal properties using shorthand ({x}).
|
||||
* PNK_NAME, name pn_atom: name, string, or object atom
|
||||
* PNK_STRING pn_op: JSOP_NAME, JSOP_STRING, or JSOP_OBJECT
|
||||
* If JSOP_NAME, pn_op may be JSOP_*ARG or JSOP_*VAR
|
||||
@ -677,12 +677,8 @@ class ParseNode
|
||||
#define PNX_GROUPINIT 0x02 /* var [a, b] = [c, d]; unit list */
|
||||
#define PNX_FUNCDEFS 0x04 /* contains top-level function statements */
|
||||
#define PNX_SETCALL 0x08 /* call expression in lvalue context */
|
||||
#define PNX_DESTRUCT 0x10 /* destructuring special cases:
|
||||
1. shorthand syntax used, at present
|
||||
object destructuring ({x,y}) only;
|
||||
2. code evaluating destructuring
|
||||
arguments occurs before function
|
||||
body */
|
||||
#define PNX_DESTRUCT 0x10 /* code evaluating destructuring
|
||||
arguments occurs before function body */
|
||||
#define PNX_SPECIALARRAYINIT 0x20 /* one or more of
|
||||
1. array initialiser has holes
|
||||
2. array initializer has spread node */
|
||||
|
@ -727,7 +727,7 @@ HasFinalReturn(ParseNode *pn)
|
||||
case PNK_RETURN:
|
||||
return ENDS_IN_RETURN;
|
||||
|
||||
case PNK_COLON:
|
||||
case PNK_LABEL:
|
||||
case PNK_LEXICALSCOPE:
|
||||
return HasFinalReturn(pn->expr());
|
||||
|
||||
@ -3194,7 +3194,7 @@ Parser<FullParseHandler>::checkDestructuring(BindData<FullParseHandler> *data,
|
||||
} else {
|
||||
JS_ASSERT(left->isKind(PNK_OBJECT));
|
||||
for (ParseNode *member = left->pn_head; member; member = member->pn_next) {
|
||||
MOZ_ASSERT(member->isKind(PNK_COLON));
|
||||
MOZ_ASSERT(member->isKind(PNK_COLON) || member->isKind(PNK_SHORTHAND));
|
||||
ParseNode *expr = member->pn_right;
|
||||
|
||||
if (expr->isKind(PNK_ARRAY) || expr->isKind(PNK_OBJECT)) {
|
||||
@ -4220,8 +4220,7 @@ Parser<FullParseHandler>::forStatement()
|
||||
isForDecl = true;
|
||||
tokenStream.consumeKnownToken(tt);
|
||||
pn1 = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST);
|
||||
}
|
||||
else if (tt == TOK_LET) {
|
||||
} else if (tt == TOK_LET) {
|
||||
handler.disableSyntaxParser();
|
||||
(void) tokenStream.getToken();
|
||||
if (tokenStream.peekToken() == TOK_LP) {
|
||||
@ -4233,8 +4232,7 @@ Parser<FullParseHandler>::forStatement()
|
||||
return null();
|
||||
pn1 = variables(PNK_LET, nullptr, blockObj, DontHoistVars);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
pn1 = expr();
|
||||
}
|
||||
pc->parsingForInit = false;
|
||||
@ -7303,8 +7301,7 @@ Parser<ParseHandler>::objectLiteral()
|
||||
|
||||
if (!handler.addPropertyDefinition(literal, propname, propexpr))
|
||||
return null();
|
||||
}
|
||||
else if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC)) {
|
||||
} else if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC)) {
|
||||
/*
|
||||
* Support, e.g., |var {x, y} = o| as destructuring shorthand
|
||||
* for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
|
||||
@ -7319,10 +7316,10 @@ Parser<ParseHandler>::objectLiteral()
|
||||
propname = newName(name);
|
||||
if (!propname)
|
||||
return null();
|
||||
if (!handler.addShorthandPropertyDefinition(literal, propname))
|
||||
Node ident = identifierName();
|
||||
if (!handler.addPropertyDefinition(literal, propname, ident, true))
|
||||
return null();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
report(ParseError, false, null(), JSMSG_COLON_AFTER_ID);
|
||||
return null();
|
||||
}
|
||||
|
@ -123,8 +123,7 @@ class SyntaxParseHandler
|
||||
|
||||
Node newObjectLiteral(uint32_t begin) { return NodeGeneric; }
|
||||
bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; }
|
||||
bool addPropertyDefinition(Node literal, Node name, Node expr) { return true; }
|
||||
bool addShorthandPropertyDefinition(Node literal, Node name) { return true; }
|
||||
bool addPropertyDefinition(Node literal, Node name, Node expr, bool isShorthand = false) { return true; }
|
||||
bool addAccessorPropertyDefinition(Node literal, Node name, Node fn, JSOp op) { return true; }
|
||||
|
||||
// Statements
|
||||
|
90
js/src/jit-test/tests/basic/object-shorthand.js
Normal file
90
js/src/jit-test/tests/basic/object-shorthand.js
Normal file
@ -0,0 +1,90 @@
|
||||
|
||||
load(libdir + 'asserts.js');
|
||||
|
||||
// globals:
|
||||
a = b = get = set = eval = arguments = 10;
|
||||
|
||||
assertEq({arguments}.arguments, 10);
|
||||
|
||||
var o = {a, b: b, get, set: set};
|
||||
assertEq(o.a, 10);
|
||||
assertEq(o.b, 10);
|
||||
assertEq(o.get, 10);
|
||||
assertEq(o.set, 10);
|
||||
|
||||
var names = ['a', 'get', 'set', 'eval'];
|
||||
// global
|
||||
names.forEach(ident =>
|
||||
assertEq(new Function('return {' + ident + '}.' + ident + ';')(), 10));
|
||||
// local
|
||||
names.forEach(ident =>
|
||||
assertEq(new Function('var ' + ident + ' = 20; return {' + ident + '}.' + ident + ';')(), 20));
|
||||
// scope
|
||||
names.forEach(ident =>
|
||||
assertEq(new Function('var ' + ident + ' = 30; return (function () {return {' + ident + '}.' + ident + ';})();')(), 30));
|
||||
|
||||
var reserved = [
|
||||
'break',
|
||||
'do',
|
||||
'in',
|
||||
'typeof',
|
||||
'case',
|
||||
'else',
|
||||
'instanceof',
|
||||
'var',
|
||||
'catch',
|
||||
'export',
|
||||
'new',
|
||||
'void',
|
||||
'class',
|
||||
'extends',
|
||||
'return',
|
||||
'while',
|
||||
'const',
|
||||
'finally',
|
||||
'super',
|
||||
'with',
|
||||
'continue',
|
||||
'for',
|
||||
'switch',
|
||||
'debugger',
|
||||
'function',
|
||||
'this',
|
||||
'delete',
|
||||
'import',
|
||||
'try',
|
||||
'enum',
|
||||
'null',
|
||||
'true',
|
||||
'false'
|
||||
];
|
||||
|
||||
// non-identifiers should also throw
|
||||
var nonidents = [
|
||||
'"str"',
|
||||
'0'
|
||||
];
|
||||
|
||||
reserved.concat(nonidents).forEach(ident =>
|
||||
assertThrowsInstanceOf(() => new Function('return {' + ident + '}'), SyntaxError));
|
||||
|
||||
var reservedStrict = [
|
||||
'implements',
|
||||
'interface',
|
||||
'package',
|
||||
'private',
|
||||
'protected',
|
||||
'public',
|
||||
'static',
|
||||
// XXX: according to 12.1.1, these should only be errors in strict code:
|
||||
// see https://bugzilla.mozilla.org/show_bug.cgi?id=1032150
|
||||
//'let',
|
||||
//'yield'
|
||||
];
|
||||
|
||||
reservedStrict.forEach(ident =>
|
||||
assertEq(new Function('var ' + ident + ' = 10; return {' + ident + '}.' + ident + ';')(), 10));
|
||||
|
||||
reservedStrict.concat(['let', 'yield']).forEach(ident =>
|
||||
assertThrowsInstanceOf(() => new Function('"use strict"; return {' + ident + '}'), SyntaxError));
|
||||
|
@ -1,4 +1,4 @@
|
||||
// |jit-test| dump-bytecode;error:SyntaxError
|
||||
// |jit-test| dump-bytecode
|
||||
|
||||
(function() {
|
||||
const x = ((function() {
|
||||
|
@ -275,7 +275,7 @@ FunctionStatementList(ParseNode *fn)
|
||||
static inline bool
|
||||
IsNormalObjectField(ExclusiveContext *cx, ParseNode *pn)
|
||||
{
|
||||
JS_ASSERT(pn->isKind(PNK_COLON));
|
||||
JS_ASSERT(pn->isKind(PNK_COLON) || pn->isKind(PNK_SHORTHAND));
|
||||
return pn->getOp() == JSOP_INITPROP &&
|
||||
BinaryLeft(pn)->isKind(PNK_NAME) &&
|
||||
BinaryLeft(pn)->name() != cx->names().proto;
|
||||
@ -291,7 +291,7 @@ ObjectNormalFieldName(ExclusiveContext *cx, ParseNode *pn)
|
||||
static inline ParseNode *
|
||||
ObjectFieldInitializer(ParseNode *pn)
|
||||
{
|
||||
JS_ASSERT(pn->isKind(PNK_COLON));
|
||||
JS_ASSERT(pn->isKind(PNK_COLON) || pn->isKind(PNK_SHORTHAND));
|
||||
return BinaryRight(pn);
|
||||
}
|
||||
|
||||
|
@ -274,7 +274,7 @@ MSG_DEF(JSMSG_BAD_DELETE_OPERAND, 220, 0, JSEXN_REFERENCEERR, "invalid delet
|
||||
MSG_DEF(JSMSG_BAD_INCOP_OPERAND, 221, 0, JSEXN_REFERENCEERR, "invalid increment/decrement operand")
|
||||
MSG_DEF(JSMSG_UNEXPECTED_TYPE, 222, 2, JSEXN_TYPEERR, "{0} is {1}")
|
||||
MSG_DEF(JSMSG_LET_DECL_NOT_IN_BLOCK, 223, 0, JSEXN_SYNTAXERR, "let declaration not directly within block")
|
||||
MSG_DEF(JSMSG_BAD_OBJECT_INIT, 224, 0, JSEXN_SYNTAXERR, "invalid object initializer")
|
||||
MSG_DEF(JSMSG_UNUSED224, 224, 0, JSEXN_NONE, "")
|
||||
MSG_DEF(JSMSG_CANT_SET_ARRAY_ATTRS, 225, 0, JSEXN_INTERNALERR, "can't set attributes on indexed array properties")
|
||||
MSG_DEF(JSMSG_EVAL_ARITY, 226, 0, JSEXN_TYPEERR, "eval accepts only one parameter")
|
||||
MSG_DEF(JSMSG_MISSING_FUN_ARG, 227, 2, JSEXN_TYPEERR, "missing argument {0} when calling function {1}")
|
||||
|
@ -532,8 +532,8 @@ class NodeBuilder
|
||||
bool catchClause(HandleValue var, HandleValue guard, HandleValue body, TokenPos *pos,
|
||||
MutableHandleValue dst);
|
||||
|
||||
bool propertyInitializer(HandleValue key, HandleValue val, PropKind kind, TokenPos *pos,
|
||||
MutableHandleValue dst);
|
||||
bool propertyInitializer(HandleValue key, HandleValue val, PropKind kind, bool isShorthand,
|
||||
TokenPos *pos, MutableHandleValue dst);
|
||||
|
||||
|
||||
/*
|
||||
@ -1246,8 +1246,8 @@ NodeBuilder::propertyPattern(HandleValue key, HandleValue patt, TokenPos *pos,
|
||||
}
|
||||
|
||||
bool
|
||||
NodeBuilder::propertyInitializer(HandleValue key, HandleValue val, PropKind kind, TokenPos *pos,
|
||||
MutableHandleValue dst)
|
||||
NodeBuilder::propertyInitializer(HandleValue key, HandleValue val, PropKind kind, bool isShorthand,
|
||||
TokenPos *pos, MutableHandleValue dst)
|
||||
{
|
||||
RootedValue kindName(cx);
|
||||
if (!atomValue(kind == PROP_INIT
|
||||
@ -1258,6 +1258,8 @@ NodeBuilder::propertyInitializer(HandleValue key, HandleValue val, PropKind kind
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedValue isShorthandVal(cx, BooleanValue(isShorthand));
|
||||
|
||||
RootedValue cb(cx, callbacks[AST_PROPERTY]);
|
||||
if (!cb.isNull())
|
||||
return callback(cb, kindName, key, val, pos, dst);
|
||||
@ -1266,6 +1268,7 @@ NodeBuilder::propertyInitializer(HandleValue key, HandleValue val, PropKind kind
|
||||
"key", key,
|
||||
"value", val,
|
||||
"kind", kindName,
|
||||
"shorthand", isShorthandVal,
|
||||
dst);
|
||||
}
|
||||
|
||||
@ -2802,11 +2805,6 @@ ASTSerializer::expression(ParseNode *pn, MutableHandleValue dst)
|
||||
|
||||
case PNK_OBJECT:
|
||||
{
|
||||
/* The parser notes any uninitialized properties by setting the PNX_DESTRUCT flag. */
|
||||
if (pn->pn_xflags & PNX_DESTRUCT) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_OBJECT_INIT);
|
||||
return false;
|
||||
}
|
||||
NodeVector elts(cx);
|
||||
if (!elts.reserve(pn->pn_count))
|
||||
return false;
|
||||
@ -2924,10 +2922,11 @@ ASTSerializer::property(ParseNode *pn, MutableHandleValue dst)
|
||||
LOCAL_NOT_REACHED("unexpected object-literal property");
|
||||
}
|
||||
|
||||
bool isShorthand = pn->isKind(PNK_SHORTHAND);
|
||||
RootedValue key(cx), val(cx);
|
||||
return propertyName(pn->pn_left, &key) &&
|
||||
expression(pn->pn_right, &val) &&
|
||||
builder.propertyInitializer(key, val, kind, &pn->pn_pos, dst);
|
||||
builder.propertyInitializer(key, val, kind, isShorthand, &pn->pn_pos, dst);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -343,6 +343,8 @@ assertExpr("[1,(2,3)]", arrExpr([lit(1),seqExpr([lit(2),lit(3)])]));
|
||||
assertExpr("[,(2,3)]", arrExpr([null,seqExpr([lit(2),lit(3)])]));
|
||||
assertExpr("({})", objExpr([]));
|
||||
assertExpr("({x:1})", objExpr([{ key: ident("x"), value: lit(1) }]));
|
||||
assertExpr("({x:x, y})", objExpr([{ key: ident("x"), value: ident("x"), shorthand: false },
|
||||
{ key: ident("y"), value: ident("y"), shorthand: true }]));
|
||||
assertExpr("({x:1, y:2})", objExpr([{ key: ident("x"), value: lit(1) },
|
||||
{ key: ident("y"), value: lit(2) } ]));
|
||||
assertExpr("({x:1, y:2, z:3})", objExpr([{ key: ident("x"), value: lit(1) },
|
||||
@ -999,13 +1001,6 @@ try {
|
||||
if (!thrown)
|
||||
throw new Error("builder exception not propagated");
|
||||
|
||||
// Missing property RHS's in an object literal should throw.
|
||||
try {
|
||||
Reflect.parse("({foo})");
|
||||
throw new Error("object literal missing property RHS didn't throw");
|
||||
} catch (e if e instanceof SyntaxError) { }
|
||||
|
||||
|
||||
// A simple proof-of-concept that the builder API can be used to generate other
|
||||
// formats, such as JsonMLAst:
|
||||
//
|
||||
|
@ -7,13 +7,7 @@
|
||||
*/
|
||||
|
||||
// Bug 696109 - fixed a precedence bug in with/while nodes
|
||||
try {
|
||||
Reflect.parse("with({foo})bar");
|
||||
throw new Error("supposed to be a syntax error");
|
||||
} catch (e if e instanceof SyntaxError) { }
|
||||
try {
|
||||
Reflect.parse("while({foo})bar");
|
||||
throw new Error("supposed to be a syntax error");
|
||||
} catch (e if e instanceof SyntaxError) { }
|
||||
Reflect.parse("with({foo})bar");
|
||||
Reflect.parse("while({foo})bar");
|
||||
|
||||
reportCompare(true, true);
|
||||
|
Loading…
Reference in New Issue
Block a user