Bug 336376: Keywords are treated as identifiers in object literals or after '::', '.', '..' unless they are part of namespace prefix. I.e. now {try: 1}, obj.delete(), xml..in, ns::new are valid while xml.try::elem is not allowed and xml.function::child continues to refer to the method named child. r=brendan

This commit is contained in:
igor%mir2.org 2006-05-09 02:26:27 +00:00
Родитель 270acad293
Коммит 544353a33f
4 изменённых файлов: 162 добавлений и 108 удалений

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

@ -287,3 +287,4 @@ MSG_DEF(JSMSG_UTF8_CHAR_TOO_LARGE, 204, 1, JSEXN_TYPEERR, "UTF-8 character {0
MSG_DEF(JSMSG_MALFORMED_UTF8_CHAR, 205, 1, JSEXN_TYPEERR, "malformed UTF-8 character sequence at offset {0}")
MSG_DEF(JSMSG_USER_DEFINED_ERROR, 206, 0, JSEXN_ERR, "JS_ReportError was called")
MSG_DEF(JSMSG_WRONG_CONSTRUCTOR, 207, 1, JSEXN_TYPEERR, "wrong construtor called for {0}")
MSG_DEF(JSMSG_KEYWORD_NOT_NS, 208, 0, JSEXN_SYNTAXERR, "keyword is used as namespace")

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

@ -93,6 +93,10 @@ typedef JSParseNode *
JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
JSBool allowCallSyntax);
typedef JSParseNode *
JSPrimaryParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
JSTokenType tt, JSBool afterDot);
static JSParser FunctionStmt;
static JSParser FunctionExpr;
static JSParser Statements;
@ -113,7 +117,7 @@ static JSParser AddExpr;
static JSParser MulExpr;
static JSParser UnaryExpr;
static JSMemberParser MemberExpr;
static JSParser PrimaryExpr;
static JSPrimaryParser PrimaryExpr;
/*
* Insist that the next token be of type tt, or report errno and return null.
@ -1220,6 +1224,7 @@ ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
pn2 = NewParseNode(cx, ts, PN_NAME, tc);
if (!pn2)
return NULL;
ts->flags |= TSF_KEYWORD_IS_NAME;
if (js_MatchToken(cx, ts, TOK_STAR)) {
pn2->pn_op = JSOP_IMPORTALL;
pn2->pn_atom = NULL;
@ -1230,6 +1235,7 @@ ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
pn2->pn_slot = -1;
pn2->pn_attrs = 0;
}
ts->flags &= ~TSF_KEYWORD_IS_NAME;
pn2->pn_expr = pn;
pn2->pn_pos.begin = pn->pn_pos.begin;
pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
@ -2854,11 +2860,9 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
/* Check for new expression first. */
ts->flags |= TSF_OPERAND;
tt = js_PeekToken(cx, ts);
tt = js_GetToken(cx, ts);
ts->flags &= ~TSF_OPERAND;
if (tt == TOK_NEW) {
(void) js_GetToken(cx, ts);
pn = NewParseNode(cx, ts, PN_LIST, tc);
if (!pn)
return NULL;
@ -2878,7 +2882,7 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
}
pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
} else {
pn = PrimaryExpr(cx, ts, tc);
pn = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
if (!pn)
return NULL;
@ -2904,7 +2908,10 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
if (!pn2)
return NULL;
#if JS_HAS_XML_SUPPORT
pn3 = PrimaryExpr(cx, ts, tc);
ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME;
tt = js_GetToken(cx, ts);
ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE);
if (!pn3)
return NULL;
tt = pn3->pn_type;
@ -2940,7 +2947,9 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
pn2->pn_right = pn3;
}
#else
ts->flags |= TSF_KEYWORD_IS_NAME;
MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
ts->flags &= ~TSF_KEYWORD_IS_NAME;
pn2->pn_op = JSOP_GETPROP;
pn2->pn_expr = pn;
pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
@ -2952,7 +2961,10 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
pn2 = NewParseNode(cx, ts, PN_BINARY, tc);
if (!pn2)
return NULL;
pn3 = PrimaryExpr(cx, ts, tc);
ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME;
tt = js_GetToken(cx, ts);
ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE);
if (!pn3)
return NULL;
tt = pn3->pn_type;
@ -3156,7 +3168,9 @@ QualifiedSuffix(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
if (pn->pn_op == JSOP_QNAMEPART)
pn->pn_op = JSOP_NAME;
ts->flags |= TSF_KEYWORD_IS_NAME;
tt = js_GetToken(cx, ts);
ts->flags &= ~TSF_KEYWORD_IS_NAME;
if (tt == TOK_STAR || tt == TOK_NAME) {
/* Inline and specialize PropertySelector for JSOP_QNAMECONST. */
pn2->pn_op = JSOP_QNAMECONST;
@ -3709,15 +3723,22 @@ js_ParseXMLTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,
#endif /* JS_HAS_XMLSUPPORT */
static JSParseNode *
PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
JSTokenType tt, JSBool afterDot)
{
JSTokenType tt;
JSParseNode *pn, *pn2, *pn3;
JSBool afterComma;
JSOp op;
#if JS_HAS_GETTER_SETTER
JSAtom *atom;
JSRuntime *rt;
#endif
#if JS_HAS_XML_SUPPORT
JSString *str;
#endif
#if JS_HAS_SHARP_VARS
JSParseNode *defsharp;
JSBool notsharp;
@ -3734,10 +3755,6 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
CHECK_RECURSION();
ts->flags |= TSF_OPERAND;
tt = js_GetToken(cx, ts);
ts->flags &= ~TSF_OPERAND;
#if JS_HAS_GETTER_SETTER
if (tt == TOK_NAME) {
tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
@ -3748,6 +3765,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
switch (tt) {
case TOK_FUNCTION:
#if JS_HAS_XML_SUPPORT
if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
pn2 = NewParseNode(cx, ts, PN_NULLARY, tc);
if (!pn2)
@ -3758,6 +3776,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
return NULL;
break;
}
#endif
pn = FunctionExpr(cx, ts, tc);
if (!pn)
return NULL;
@ -3839,90 +3858,99 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
#endif
PN_INIT_LIST(pn);
if (!js_MatchToken(cx, ts, TOK_RC)) {
do {
JSOp op;
tt = js_GetToken(cx, ts);
switch (tt) {
case TOK_NUMBER:
pn3 = NewParseNode(cx, ts, PN_NULLARY, tc);
if (pn3)
pn3->pn_dval = CURRENT_TOKEN(ts).t_dval;
break;
case TOK_NAME:
afterComma = JS_FALSE;
for (;;) {
ts->flags |= TSF_KEYWORD_IS_NAME;
tt = js_GetToken(cx, ts);
ts->flags &= ~TSF_KEYWORD_IS_NAME;
switch (tt) {
case TOK_NUMBER:
pn3 = NewParseNode(cx, ts, PN_NULLARY, tc);
if (pn3)
pn3->pn_dval = CURRENT_TOKEN(ts).t_dval;
break;
case TOK_NAME:
#if JS_HAS_GETTER_SETTER
atom = CURRENT_TOKEN(ts).t_atom;
rt = cx->runtime;
if (atom == rt->atomState.getAtom ||
atom == rt->atomState.setAtom) {
op = (atom == rt->atomState.getAtom)
? JSOP_GETTER
: JSOP_SETTER;
if (js_MatchToken(cx, ts, TOK_NAME)) {
pn3 = NewParseNode(cx, ts, PN_NAME, tc);
if (!pn3)
return NULL;
pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
pn3->pn_expr = NULL;
/* We have to fake a 'function' token here. */
CURRENT_TOKEN(ts).t_op = JSOP_NOP;
CURRENT_TOKEN(ts).type = TOK_FUNCTION;
pn2 = FunctionExpr(cx, ts, tc);
pn2 = NewBinary(cx, TOK_COLON, op, pn3, pn2, tc);
goto skip;
}
}
/* else fall thru ... */
#endif
case TOK_STRING:
pn3 = NewParseNode(cx, ts, PN_NULLARY, tc);
if (pn3)
atom = CURRENT_TOKEN(ts).t_atom;
rt = cx->runtime;
if (atom == rt->atomState.getAtom ||
atom == rt->atomState.setAtom) {
op = (atom == rt->atomState.getAtom)
? JSOP_GETTER
: JSOP_SETTER;
if (js_MatchToken(cx, ts, TOK_NAME)) {
pn3 = NewParseNode(cx, ts, PN_NAME, tc);
if (!pn3)
return NULL;
pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
break;
case TOK_RC:
if (!js_ReportCompileErrorNumber(cx, ts,
JSREPORT_TS |
JSREPORT_WARNING |
JSREPORT_STRICT,
JSMSG_TRAILING_COMMA)) {
return NULL;
pn3->pn_expr = NULL;
/* We have to fake a 'function' token here. */
CURRENT_TOKEN(ts).t_op = JSOP_NOP;
CURRENT_TOKEN(ts).type = TOK_FUNCTION;
pn2 = FunctionExpr(cx, ts, tc);
pn2 = NewBinary(cx, TOK_COLON, op, pn3, pn2, tc);
goto skip;
}
goto end_obj_init;
default:
js_ReportCompileErrorNumber(cx, ts,
JSREPORT_TS | JSREPORT_ERROR,
JSMSG_BAD_PROP_ID);
return NULL;
}
tt = js_GetToken(cx, ts);
#if JS_HAS_GETTER_SETTER
if (tt == TOK_NAME) {
tt = CheckGetterOrSetter(cx, ts, TOK_COLON);
if (tt == TOK_ERROR)
/* else fall thru ... */
#endif
case TOK_STRING:
pn3 = NewParseNode(cx, ts, PN_NULLARY, tc);
if (pn3)
pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
break;
case TOK_RC:
if (afterComma &&
!js_ReportCompileErrorNumber(cx, ts,
JSREPORT_TS |
JSREPORT_WARNING |
JSREPORT_STRICT,
JSMSG_TRAILING_COMMA)) {
return NULL;
}
#endif
if (tt != TOK_COLON) {
js_ReportCompileErrorNumber(cx, ts,
JSREPORT_TS | JSREPORT_ERROR,
JSMSG_COLON_AFTER_ID);
return NULL;
}
op = CURRENT_TOKEN(ts).t_op;
pn2 = NewBinary(cx, TOK_COLON, op, pn3, AssignExpr(cx, ts, tc),
tc);
#if JS_HAS_GETTER_SETTER
skip:
#endif
if (!pn2)
return NULL;
PN_APPEND(pn, pn2);
} while (js_MatchToken(cx, ts, TOK_COMMA));
goto end_obj_init;
default:
js_ReportCompileErrorNumber(cx, ts,
JSREPORT_TS | JSREPORT_ERROR,
JSMSG_BAD_PROP_ID);
return NULL;
}
MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LIST);
tt = js_GetToken(cx, ts);
#if JS_HAS_GETTER_SETTER
if (tt == TOK_NAME) {
tt = CheckGetterOrSetter(cx, ts, TOK_COLON);
if (tt == TOK_ERROR)
return NULL;
}
#endif
if (tt != TOK_COLON) {
js_ReportCompileErrorNumber(cx, ts,
JSREPORT_TS | JSREPORT_ERROR,
JSMSG_COLON_AFTER_ID);
return NULL;
}
op = CURRENT_TOKEN(ts).t_op;
pn2 = NewBinary(cx, TOK_COLON, op, pn3, AssignExpr(cx, ts, tc),
tc);
#if JS_HAS_GETTER_SETTER
skip:
#endif
if (!pn2)
return NULL;
PN_APPEND(pn, pn2);
tt = js_GetToken(cx, ts);
if (tt == TOK_RC)
goto end_obj_init;
if (tt != TOK_COMMA) {
js_ReportCompileErrorNumber(cx, ts,
JSREPORT_TS | JSREPORT_ERROR,
JSMSG_CURLY_AFTER_LIST);
return NULL;
}
afterComma = JS_TRUE;
}
end_obj_init:
pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
@ -4017,6 +4045,26 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
#if JS_HAS_XML_SUPPORT
if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
if (afterDot) {
/*
* Here PrimaryExpr is called after '.' or '..' and we
* just scanned .name:: or ..name:: . This is the only
* case where a keyword after '.' or '..' is not
* treated as a property name.
*/
str = ATOM_TO_STRING(pn->pn_atom);
tt = js_CheckKeyword(JSSTRING_CHARS(str),
JSSTRING_LENGTH(str));
if (tt == TOK_FUNCTION) {
pn->pn_arity = PN_NULLARY;
pn->pn_type = TOK_FUNCTION;
} else if (tt != TOK_EOF) {
js_ReportCompileErrorNumber(
cx, ts, JSREPORT_TS | JSREPORT_ERROR,
JSMSG_KEYWORD_NOT_NS);
return NULL;
}
}
pn = QualifiedSuffix(cx, ts, pn, tc);
if (!pn)
return NULL;

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

@ -74,8 +74,6 @@
#include "jsxml.h"
#endif
#define MAX_KEYWORD_LENGTH 12
#define JS_KEYWORD(keyword, type, op, version) \
const char js_##keyword##_str[] = #keyword;
#include "jskeyword.tbl"
@ -134,11 +132,14 @@ FindKeyword(const jschar *s, size_t length)
return NULL;
}
JSBool
js_IsKeyword(const jschar *str, size_t length)
JSTokenType
js_CheckKeyword(const jschar *str, size_t length)
{
const struct keyword *kw;
JS_ASSERT(length != 0);
return FindKeyword(str, length) != NULL;
kw = FindKeyword(str, length);
return kw ? kw->tokentype : TOK_EOF;
}
JS_FRIEND_API(void)
@ -1282,23 +1283,20 @@ retry:
}
UngetChar(ts, c);
/*
* Check for keywords unless we saw Unicode escape or parser asks
* to ignore keywords.
*/
if (!hadUnicodeEscape &&
!(ts->flags & TSF_KEYWORD_IS_NAME) &&
(kw = FindKeyword(TOKENBUF_BASE(), TOKENBUF_LENGTH()))) {
if (kw->tokentype == TOK_RESERVED) {
char buf[MAX_KEYWORD_LENGTH + 1];
size_t buflen = sizeof(buf) - 1;
if (!js_DeflateStringToBuffer(cx,
TOKENBUF_BASE(),
TOKENBUF_LENGTH(),
buf, &buflen)) {
goto error;
}
buf [buflen] = 0;
if (!js_ReportCompileErrorNumber(cx, ts,
JSREPORT_TS |
JSREPORT_WARNING |
JSREPORT_STRICT,
JSMSG_RESERVED_ID, buf)) {
JSMSG_RESERVED_ID,
kw->chars)) {
goto error;
}
} else if (JS_VERSION_IS_ECMA(cx) ||

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

@ -272,6 +272,9 @@ struct JSTokenStream {
*/
#define TSF_IN_HTML_COMMENT 0x2000
/* Ignore keywords and return TOK_NAME instead to the parser. */
#define TSF_KEYWORD_IS_NAME 0x4000
/* Unicode separators that are treated as line terminators, in addition to \n, \r */
#define LINE_SEPARATOR 0x2028
#define PARA_SEPARATOR 0x2029
@ -301,10 +304,14 @@ extern JS_FRIEND_API(int)
js_fgets(char *buf, int size, FILE *file);
/*
* Return true if the given char array forms JavaScript keyword.
* If the given char array forms JavaScript keyword, return corresponding
* token. Otherwise return TOK_EOF.
*/
extern JSBool
js_IsKeyword(const jschar *str, size_t length);
extern JSTokenType
js_CheckKeyword(const jschar *chars, size_t length);
#define js_IsKeyword(chars, length) \
(js_CheckKeyword(chars, length) != TOK_EOF)
/*
* Friend-exported API entry point to call a mapping function on each reserved