зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1317375 - Implement "Template Literals Revision / Lifting Template Literal Restriction" ECMAScript proposal r=arai
MozReview-Commit-ID: 4OBI6kCe7Lf
This commit is contained in:
Родитель
7acd123c94
Коммит
8049403822
|
@ -3044,7 +3044,12 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
|
|||
MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos));
|
||||
|
||||
RootedValue expr(cx);
|
||||
expr.setString(next->pn_atom);
|
||||
if (next->isKind(PNK_RAW_UNDEFINED)) {
|
||||
expr.setUndefined();
|
||||
} else {
|
||||
MOZ_ASSERT(next->isKind(PNK_TEMPLATE_STRING));
|
||||
expr.setString(next->pn_atom);
|
||||
}
|
||||
cooked.infallibleAppend(expr);
|
||||
}
|
||||
|
||||
|
@ -3136,6 +3141,7 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
|
|||
case PNK_TRUE:
|
||||
case PNK_FALSE:
|
||||
case PNK_NULL:
|
||||
case PNK_RAW_UNDEFINED:
|
||||
return literal(pn, dst);
|
||||
|
||||
case PNK_YIELD_STAR:
|
||||
|
@ -3276,6 +3282,10 @@ ASTSerializer::literal(ParseNode* pn, MutableHandleValue dst)
|
|||
val.setNull();
|
||||
break;
|
||||
|
||||
case PNK_RAW_UNDEFINED:
|
||||
val.setUndefined();
|
||||
break;
|
||||
|
||||
case PNK_TRUE:
|
||||
val.setBoolean(true);
|
||||
break;
|
||||
|
|
|
@ -2660,6 +2660,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
|||
case PNK_TRUE:
|
||||
case PNK_FALSE:
|
||||
case PNK_NULL:
|
||||
case PNK_RAW_UNDEFINED:
|
||||
case PNK_ELISION:
|
||||
case PNK_GENERATOR:
|
||||
case PNK_NUMBER:
|
||||
|
@ -5825,6 +5826,9 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje
|
|||
case PNK_NULL:
|
||||
vp.setNull();
|
||||
return true;
|
||||
case PNK_RAW_UNDEFINED:
|
||||
vp.setUndefined();
|
||||
return true;
|
||||
case PNK_CALLSITEOBJ:
|
||||
case PNK_ARRAY: {
|
||||
unsigned count;
|
||||
|
@ -10210,6 +10214,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote)
|
|||
case PNK_TRUE:
|
||||
case PNK_FALSE:
|
||||
case PNK_NULL:
|
||||
case PNK_RAW_UNDEFINED:
|
||||
if (!emit1(pn->getOp()))
|
||||
return false;
|
||||
break;
|
||||
|
|
|
@ -378,6 +378,7 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
|
|||
case PNK_TRUE:
|
||||
case PNK_FALSE:
|
||||
case PNK_NULL:
|
||||
case PNK_RAW_UNDEFINED:
|
||||
case PNK_THIS:
|
||||
case PNK_ELISION:
|
||||
case PNK_NUMBER:
|
||||
|
@ -468,6 +469,7 @@ IsEffectless(ParseNode* node)
|
|||
node->isKind(PNK_TEMPLATE_STRING) ||
|
||||
node->isKind(PNK_NUMBER) ||
|
||||
node->isKind(PNK_NULL) ||
|
||||
node->isKind(PNK_RAW_UNDEFINED) ||
|
||||
node->isKind(PNK_FUNCTION) ||
|
||||
node->isKind(PNK_GENEXP);
|
||||
}
|
||||
|
@ -492,6 +494,7 @@ Boolish(ParseNode* pn)
|
|||
|
||||
case PNK_FALSE:
|
||||
case PNK_NULL:
|
||||
case PNK_RAW_UNDEFINED:
|
||||
return Falsy;
|
||||
|
||||
case PNK_VOID: {
|
||||
|
@ -1643,6 +1646,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
|
|||
case PNK_TRUE:
|
||||
case PNK_FALSE:
|
||||
case PNK_NULL:
|
||||
case PNK_RAW_UNDEFINED:
|
||||
case PNK_ELISION:
|
||||
case PNK_NUMBER:
|
||||
case PNK_DEBUGGER:
|
||||
|
|
|
@ -183,6 +183,10 @@ class FullParseHandler
|
|||
return new_<NullLiteral>(pos);
|
||||
}
|
||||
|
||||
ParseNode* newRawUndefinedLiteral(const TokenPos& pos) {
|
||||
return new_<RawUndefinedLiteral>(pos);
|
||||
}
|
||||
|
||||
// The Boxer object here is any object that can allocate ObjectBoxes.
|
||||
// Specifically, a Boxer has a .newObjectBox(T) method that accepts a
|
||||
// Rooted<RegExpObject*> argument and returns an ObjectBox*.
|
||||
|
|
|
@ -316,7 +316,8 @@ class NameResolver
|
|||
return false;
|
||||
|
||||
// Next is the callsite object node. This node only contains
|
||||
// internal strings and an array -- no user-controlled expressions.
|
||||
// internal strings or undefined and an array -- no user-controlled
|
||||
// expressions.
|
||||
element = element->pn_next;
|
||||
#ifdef DEBUG
|
||||
{
|
||||
|
@ -326,7 +327,7 @@ class NameResolver
|
|||
for (ParseNode* kid = array->pn_head; kid; kid = kid->pn_next)
|
||||
MOZ_ASSERT(kid->isKind(PNK_TEMPLATE_STRING));
|
||||
for (ParseNode* next = array->pn_next; next; next = next->pn_next)
|
||||
MOZ_ASSERT(next->isKind(PNK_TEMPLATE_STRING));
|
||||
MOZ_ASSERT(next->isKind(PNK_TEMPLATE_STRING) || next->isKind(PNK_RAW_UNDEFINED));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -382,6 +383,7 @@ class NameResolver
|
|||
case PNK_TRUE:
|
||||
case PNK_FALSE:
|
||||
case PNK_NULL:
|
||||
case PNK_RAW_UNDEFINED:
|
||||
case PNK_ELISION:
|
||||
case PNK_GENERATOR:
|
||||
case PNK_NUMBER:
|
||||
|
|
|
@ -190,6 +190,7 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
|
|||
case PNK_TRUE:
|
||||
case PNK_FALSE:
|
||||
case PNK_NULL:
|
||||
case PNK_RAW_UNDEFINED:
|
||||
case PNK_ELISION:
|
||||
case PNK_GENERATOR:
|
||||
case PNK_NUMBER:
|
||||
|
@ -685,6 +686,7 @@ NullaryNode::dump()
|
|||
case PNK_TRUE: fprintf(stderr, "#true"); break;
|
||||
case PNK_FALSE: fprintf(stderr, "#false"); break;
|
||||
case PNK_NULL: fprintf(stderr, "#null"); break;
|
||||
case PNK_RAW_UNDEFINED: fprintf(stderr, "#undefined"); break;
|
||||
|
||||
case PNK_NUMBER: {
|
||||
ToCStringBuf cbuf;
|
||||
|
|
|
@ -54,6 +54,7 @@ class ObjectBox;
|
|||
F(TRUE) \
|
||||
F(FALSE) \
|
||||
F(NULL) \
|
||||
F(RAW_UNDEFINED) \
|
||||
F(THIS) \
|
||||
F(FUNCTION) \
|
||||
F(MODULE) \
|
||||
|
@ -406,7 +407,8 @@ IsTypeofKind(ParseNodeKind kind)
|
|||
* PNK_NUMBER dval pn_dval: double value of numeric literal
|
||||
* PNK_TRUE, nullary pn_op: JSOp bytecode
|
||||
* PNK_FALSE,
|
||||
* PNK_NULL
|
||||
* PNK_NULL,
|
||||
* PNK_RAW_UNDEFINED
|
||||
*
|
||||
* PNK_THIS, unary pn_kid: '.this' Name if function `this`, else nullptr
|
||||
* PNK_SUPERBASE unary pn_kid: '.this' Name
|
||||
|
@ -686,7 +688,8 @@ class ParseNode
|
|||
isKind(PNK_STRING) ||
|
||||
isKind(PNK_TRUE) ||
|
||||
isKind(PNK_FALSE) ||
|
||||
isKind(PNK_NULL);
|
||||
isKind(PNK_NULL) ||
|
||||
isKind(PNK_RAW_UNDEFINED);
|
||||
}
|
||||
|
||||
/* Return true if this node appears in a Directive Prologue. */
|
||||
|
@ -1141,6 +1144,16 @@ class NullLiteral : public ParseNode
|
|||
explicit NullLiteral(const TokenPos& pos) : ParseNode(PNK_NULL, JSOP_NULL, PN_NULLARY, pos) { }
|
||||
};
|
||||
|
||||
// This is only used internally, currently just for tagged templates.
|
||||
// It represents the value 'undefined' (aka `void 0`), like NullLiteral
|
||||
// represents the value 'null'.
|
||||
class RawUndefinedLiteral : public ParseNode
|
||||
{
|
||||
public:
|
||||
explicit RawUndefinedLiteral(const TokenPos& pos)
|
||||
: ParseNode(PNK_RAW_UNDEFINED, JSOP_UNDEFINED, PN_NULLARY, pos) { }
|
||||
};
|
||||
|
||||
class BooleanLiteral : public ParseNode
|
||||
{
|
||||
public:
|
||||
|
@ -1361,6 +1374,7 @@ ParseNode::isConstant()
|
|||
case PNK_STRING:
|
||||
case PNK_TEMPLATE_STRING:
|
||||
case PNK_NULL:
|
||||
case PNK_RAW_UNDEFINED:
|
||||
case PNK_FALSE:
|
||||
case PNK_TRUE:
|
||||
return true;
|
||||
|
|
|
@ -3085,7 +3085,7 @@ template <typename ParseHandler>
|
|||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::templateLiteral(YieldHandling yieldHandling)
|
||||
{
|
||||
Node pn = noSubstitutionTemplate();
|
||||
Node pn = noSubstitutionUntaggedTemplate();
|
||||
if (!pn)
|
||||
return null();
|
||||
|
||||
|
@ -3098,7 +3098,7 @@ Parser<ParseHandler>::templateLiteral(YieldHandling yieldHandling)
|
|||
if (!addExprAndGetNextTemplStrToken(yieldHandling, nodeList, &tt))
|
||||
return null();
|
||||
|
||||
pn = noSubstitutionTemplate();
|
||||
pn = noSubstitutionUntaggedTemplate();
|
||||
if (!pn)
|
||||
return null();
|
||||
|
||||
|
@ -3321,7 +3321,7 @@ template <typename ParseHandler>
|
|||
bool
|
||||
Parser<ParseHandler>::appendToCallSiteObj(Node callSiteObj)
|
||||
{
|
||||
Node cookedNode = noSubstitutionTemplate();
|
||||
Node cookedNode = noSubstitutionTaggedTemplate();
|
||||
if (!cookedNode)
|
||||
return false;
|
||||
|
||||
|
@ -8711,8 +8711,23 @@ Parser<ParseHandler>::stringLiteral()
|
|||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::noSubstitutionTemplate()
|
||||
Parser<ParseHandler>::noSubstitutionTaggedTemplate()
|
||||
{
|
||||
if (tokenStream.hasInvalidTemplateEscape()) {
|
||||
tokenStream.clearInvalidTemplateEscape();
|
||||
return handler.newRawUndefinedLiteral(pos());
|
||||
}
|
||||
|
||||
return handler.newTemplateStringLiteral(stopStringCompression(), pos());
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::noSubstitutionUntaggedTemplate()
|
||||
{
|
||||
if (!tokenStream.checkForInvalidTemplateEscapeError())
|
||||
return null();
|
||||
|
||||
return handler.newTemplateStringLiteral(stopStringCompression(), pos());
|
||||
}
|
||||
|
||||
|
@ -9425,7 +9440,7 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
|
|||
return templateLiteral(yieldHandling);
|
||||
|
||||
case TOK_NO_SUBS_TEMPLATE:
|
||||
return noSubstitutionTemplate();
|
||||
return noSubstitutionUntaggedTemplate();
|
||||
|
||||
case TOK_STRING:
|
||||
return stringLiteral();
|
||||
|
|
|
@ -1053,7 +1053,8 @@ class Parser final : public ParserBase, private JS::AutoGCRooter
|
|||
JSAtom* stopStringCompression();
|
||||
|
||||
Node stringLiteral();
|
||||
Node noSubstitutionTemplate();
|
||||
Node noSubstitutionTaggedTemplate();
|
||||
Node noSubstitutionUntaggedTemplate();
|
||||
Node templateLiteral(YieldHandling yieldHandling);
|
||||
bool taggedTemplate(YieldHandling yieldHandling, Node nodeList, TokenKind tt);
|
||||
bool appendToCallSiteObj(Node callSiteObj);
|
||||
|
|
|
@ -224,6 +224,7 @@ class SyntaxParseHandler
|
|||
|
||||
Node newThisLiteral(const TokenPos& pos, Node thisName) { return NodeGeneric; }
|
||||
Node newNullLiteral(const TokenPos& pos) { return NodeGeneric; }
|
||||
Node newRawUndefinedLiteral(const TokenPos& pos) { return NodeGeneric; }
|
||||
|
||||
template <class Boxer>
|
||||
Node newRegExp(RegExpObject* reobj, const TokenPos& pos, Boxer& boxer) { return NodeGeneric; }
|
||||
|
|
|
@ -1908,56 +1908,6 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
TokenStream::matchBracedUnicode(bool* matched, uint32_t* cp)
|
||||
{
|
||||
int32_t c;
|
||||
if (!peekChar(&c))
|
||||
return false;
|
||||
if (c != '{') {
|
||||
*matched = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
consumeKnownChar('{');
|
||||
|
||||
uint32_t start = userbuf.offset();
|
||||
|
||||
bool first = true;
|
||||
uint32_t code = 0;
|
||||
do {
|
||||
int32_t c = getCharIgnoreEOL();
|
||||
if (c == EOF) {
|
||||
error(JSMSG_MALFORMED_ESCAPE, "Unicode");
|
||||
return false;
|
||||
}
|
||||
if (c == '}') {
|
||||
if (first) {
|
||||
error(JSMSG_MALFORMED_ESCAPE, "Unicode");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!JS7_ISHEX(c)) {
|
||||
error(JSMSG_MALFORMED_ESCAPE, "Unicode");
|
||||
return false;
|
||||
}
|
||||
|
||||
code = (code << 4) | JS7_UNHEX(c);
|
||||
if (code > unicode::NonBMPMax) {
|
||||
errorAt(start, JSMSG_UNICODE_OVERFLOW, "escape sequence");
|
||||
return false;
|
||||
}
|
||||
|
||||
first = false;
|
||||
} while (true);
|
||||
|
||||
*matched = true;
|
||||
*cp = code;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TokenStream::getStringOrTemplateToken(int untilChar, Token** tp)
|
||||
{
|
||||
|
@ -1980,6 +1930,10 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp)
|
|||
}
|
||||
|
||||
if (c == '\\') {
|
||||
// When parsing templates, we don't immediately report errors for
|
||||
// invalid escapes; these are handled by the parser.
|
||||
// In those cases we don't append to tokenbuf, since it won't be
|
||||
// read.
|
||||
switch (c = getChar()) {
|
||||
case 'b': c = '\b'; break;
|
||||
case 'f': c = '\f'; break;
|
||||
|
@ -1995,11 +1949,74 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp)
|
|||
|
||||
// Unicode character specification.
|
||||
case 'u': {
|
||||
bool matched;
|
||||
uint32_t code;
|
||||
if (!matchBracedUnicode(&matched, &code))
|
||||
uint32_t code = 0;
|
||||
|
||||
int32_t c2;
|
||||
if (!peekChar(&c2))
|
||||
return false;
|
||||
if (matched) {
|
||||
|
||||
uint32_t start = userbuf.offset() - 2;
|
||||
|
||||
if (c2 == '{') {
|
||||
consumeKnownChar('{');
|
||||
|
||||
bool first = true;
|
||||
bool valid = true;
|
||||
do {
|
||||
int32_t c = getCharIgnoreEOL();
|
||||
if (c == EOF) {
|
||||
if (parsingTemplate) {
|
||||
setInvalidTemplateEscape(start, InvalidEscapeType::Unicode);
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
reportInvalidEscapeError(start, InvalidEscapeType::Unicode);
|
||||
return false;
|
||||
}
|
||||
if (c == '}') {
|
||||
if (first) {
|
||||
if (parsingTemplate) {
|
||||
setInvalidTemplateEscape(start, InvalidEscapeType::Unicode);
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
reportInvalidEscapeError(start, InvalidEscapeType::Unicode);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!JS7_ISHEX(c)) {
|
||||
if (parsingTemplate) {
|
||||
// We put the character back so that we read
|
||||
// it on the next pass, which matters if it
|
||||
// was '`' or '\'.
|
||||
ungetCharIgnoreEOL(c);
|
||||
setInvalidTemplateEscape(start, InvalidEscapeType::Unicode);
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
reportInvalidEscapeError(start, InvalidEscapeType::Unicode);
|
||||
return false;
|
||||
}
|
||||
|
||||
code = (code << 4) | JS7_UNHEX(c);
|
||||
if (code > unicode::NonBMPMax) {
|
||||
if (parsingTemplate) {
|
||||
setInvalidTemplateEscape(start + 3, InvalidEscapeType::UnicodeOverflow);
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
reportInvalidEscapeError(start + 3, InvalidEscapeType::UnicodeOverflow);
|
||||
return false;
|
||||
}
|
||||
|
||||
first = false;
|
||||
} while (true);
|
||||
|
||||
if (!valid)
|
||||
continue;
|
||||
|
||||
MOZ_ASSERT(code <= unicode::NonBMPMax);
|
||||
if (code < unicode::NonBMPMin) {
|
||||
c = code;
|
||||
|
@ -2021,7 +2038,11 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp)
|
|||
c = (c << 4) + JS7_UNHEX(cp[3]);
|
||||
skipChars(4);
|
||||
} else {
|
||||
error(JSMSG_MALFORMED_ESCAPE, "Unicode");
|
||||
if (parsingTemplate) {
|
||||
setInvalidTemplateEscape(start, InvalidEscapeType::Unicode);
|
||||
continue;
|
||||
}
|
||||
reportInvalidEscapeError(start, InvalidEscapeType::Unicode);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
@ -2034,7 +2055,12 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp)
|
|||
c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]);
|
||||
skipChars(2);
|
||||
} else {
|
||||
error(JSMSG_MALFORMED_ESCAPE, "hexadecimal");
|
||||
uint32_t start = userbuf.offset() - 2;
|
||||
if (parsingTemplate) {
|
||||
setInvalidTemplateEscape(start, InvalidEscapeType::Hexadecimal);
|
||||
continue;
|
||||
}
|
||||
reportInvalidEscapeError(start, InvalidEscapeType::Hexadecimal);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
@ -2051,8 +2077,8 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp)
|
|||
// Strict mode code allows only \0, then a non-digit.
|
||||
if (val != 0 || JS7_ISDEC(c)) {
|
||||
if (parsingTemplate) {
|
||||
error(JSMSG_DEPRECATED_OCTAL);
|
||||
return false;
|
||||
setInvalidTemplateEscape(userbuf.offset() - 2, InvalidEscapeType::Octal);
|
||||
continue;
|
||||
}
|
||||
if (!reportStrictModeError(JSMSG_DEPRECATED_OCTAL))
|
||||
return false;
|
||||
|
|
|
@ -80,6 +80,20 @@ struct TokenPos {
|
|||
|
||||
enum DecimalPoint { NoDecimal = false, HasDecimal = true };
|
||||
|
||||
enum class InvalidEscapeType {
|
||||
// No invalid character escapes.
|
||||
None,
|
||||
// A malformed \x escape.
|
||||
Hexadecimal,
|
||||
// A malformed \u escape.
|
||||
Unicode,
|
||||
// An otherwise well-formed \u escape which represents a
|
||||
// codepoint > 10FFFF.
|
||||
UnicodeOverflow,
|
||||
// An octal escape in a template token.
|
||||
Octal
|
||||
};
|
||||
|
||||
class TokenStream;
|
||||
|
||||
struct Token
|
||||
|
@ -361,6 +375,23 @@ class MOZ_STACK_CLASS TokenStream
|
|||
bool hadError() const { return flags.hadError; }
|
||||
void clearSawOctalEscape() { flags.sawOctalEscape = false; }
|
||||
|
||||
bool hasInvalidTemplateEscape() const {
|
||||
return invalidTemplateEscapeType != InvalidEscapeType::None;
|
||||
}
|
||||
void clearInvalidTemplateEscape() {
|
||||
invalidTemplateEscapeType = InvalidEscapeType::None;
|
||||
}
|
||||
|
||||
// If there is an invalid escape in a template, report it and return false,
|
||||
// otherwise return true.
|
||||
bool checkForInvalidTemplateEscapeError() {
|
||||
if (invalidTemplateEscapeType == InvalidEscapeType::None)
|
||||
return true;
|
||||
|
||||
reportInvalidEscapeError(invalidTemplateEscapeOffset, invalidTemplateEscapeType);
|
||||
return false;
|
||||
}
|
||||
|
||||
// TokenStream-specific error reporters.
|
||||
bool reportError(unsigned errorNumber, ...);
|
||||
bool reportErrorNoOffset(unsigned errorNumber, ...);
|
||||
|
@ -422,6 +453,33 @@ class MOZ_STACK_CLASS TokenStream
|
|||
bool reportStrictModeError(unsigned errorNumber, ...);
|
||||
bool strictMode() const { return strictModeGetter && strictModeGetter->strictMode(); }
|
||||
|
||||
void setInvalidTemplateEscape(uint32_t offset, InvalidEscapeType type) {
|
||||
MOZ_ASSERT(type != InvalidEscapeType::None);
|
||||
if (invalidTemplateEscapeType != InvalidEscapeType::None)
|
||||
return;
|
||||
invalidTemplateEscapeOffset = offset;
|
||||
invalidTemplateEscapeType = type;
|
||||
}
|
||||
void reportInvalidEscapeError(uint32_t offset, InvalidEscapeType type) {
|
||||
switch (type) {
|
||||
case InvalidEscapeType::None:
|
||||
MOZ_ASSERT_UNREACHABLE("unexpected InvalidEscapeType");
|
||||
return;
|
||||
case InvalidEscapeType::Hexadecimal:
|
||||
errorAt(offset, JSMSG_MALFORMED_ESCAPE, "hexadecimal");
|
||||
return;
|
||||
case InvalidEscapeType::Unicode:
|
||||
errorAt(offset, JSMSG_MALFORMED_ESCAPE, "Unicode");
|
||||
return;
|
||||
case InvalidEscapeType::UnicodeOverflow:
|
||||
errorAt(offset, JSMSG_UNICODE_OVERFLOW, "escape sequence");
|
||||
return;
|
||||
case InvalidEscapeType::Octal:
|
||||
errorAt(offset, JSMSG_DEPRECATED_OCTAL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static JSAtom* atomize(ExclusiveContext* cx, CharBuffer& cb);
|
||||
MOZ_MUST_USE bool putIdentInTokenbuf(const char16_t* identStart);
|
||||
|
||||
|
@ -442,6 +500,9 @@ class MOZ_STACK_CLASS TokenStream
|
|||
bool awaitIsKeyword = false;
|
||||
friend class AutoAwaitIsKeyword;
|
||||
|
||||
uint32_t invalidTemplateEscapeOffset = 0;
|
||||
InvalidEscapeType invalidTemplateEscapeType = InvalidEscapeType::None;
|
||||
|
||||
public:
|
||||
typedef Token::Modifier Modifier;
|
||||
static constexpr Modifier None = Token::None;
|
||||
|
@ -955,7 +1016,6 @@ class MOZ_STACK_CLASS TokenStream
|
|||
|
||||
MOZ_MUST_USE bool getTokenInternal(TokenKind* ttp, Modifier modifier);
|
||||
|
||||
MOZ_MUST_USE bool matchBracedUnicode(bool* matched, uint32_t* code);
|
||||
MOZ_MUST_USE bool getStringOrTemplateToken(int untilChar, Token** tp);
|
||||
|
||||
int32_t getChar();
|
||||
|
|
|
@ -287,5 +287,177 @@ assertEq(String.raw`h\r\ney${4}there\n`, "h\\r\\ney4there\\n");
|
|||
assertEq(String.raw`hey`, "hey");
|
||||
assertEq(String.raw``, "");
|
||||
|
||||
// Invalid escape sequences
|
||||
check(raw`\01`, ["\\01"]);
|
||||
check(raw`\01${0}right`, ["\\01","right"]);
|
||||
check(raw`left${0}\01`, ["left","\\01"]);
|
||||
check(raw`left${0}\01${1}right`, ["left","\\01","right"]);
|
||||
check(raw`\1`, ["\\1"]);
|
||||
check(raw`\1${0}right`, ["\\1","right"]);
|
||||
check(raw`left${0}\1`, ["left","\\1"]);
|
||||
check(raw`left${0}\1${1}right`, ["left","\\1","right"]);
|
||||
check(raw`\xg`, ["\\xg"]);
|
||||
check(raw`\xg${0}right`, ["\\xg","right"]);
|
||||
check(raw`left${0}\xg`, ["left","\\xg"]);
|
||||
check(raw`left${0}\xg${1}right`, ["left","\\xg","right"]);
|
||||
check(raw`\xAg`, ["\\xAg"]);
|
||||
check(raw`\xAg${0}right`, ["\\xAg","right"]);
|
||||
check(raw`left${0}\xAg`, ["left","\\xAg"]);
|
||||
check(raw`left${0}\xAg${1}right`, ["left","\\xAg","right"]);
|
||||
check(raw`\u0`, ["\\u0"]);
|
||||
check(raw`\u0${0}right`, ["\\u0","right"]);
|
||||
check(raw`left${0}\u0`, ["left","\\u0"]);
|
||||
check(raw`left${0}\u0${1}right`, ["left","\\u0","right"]);
|
||||
check(raw`\u0g`, ["\\u0g"]);
|
||||
check(raw`\u0g${0}right`, ["\\u0g","right"]);
|
||||
check(raw`left${0}\u0g`, ["left","\\u0g"]);
|
||||
check(raw`left${0}\u0g${1}right`, ["left","\\u0g","right"]);
|
||||
check(raw`\u00g`, ["\\u00g"]);
|
||||
check(raw`\u00g${0}right`, ["\\u00g","right"]);
|
||||
check(raw`left${0}\u00g`, ["left","\\u00g"]);
|
||||
check(raw`left${0}\u00g${1}right`, ["left","\\u00g","right"]);
|
||||
check(raw`\u000g`, ["\\u000g"]);
|
||||
check(raw`\u000g${0}right`, ["\\u000g","right"]);
|
||||
check(raw`left${0}\u000g`, ["left","\\u000g"]);
|
||||
check(raw`left${0}\u000g${1}right`, ["left","\\u000g","right"]);
|
||||
check(raw`\u{}`, ["\\u{}"]);
|
||||
check(raw`\u{}${0}right`, ["\\u{}","right"]);
|
||||
check(raw`left${0}\u{}`, ["left","\\u{}"]);
|
||||
check(raw`left${0}\u{}${1}right`, ["left","\\u{}","right"]);
|
||||
check(raw`\u{-0}`, ["\\u{-0}"]);
|
||||
check(raw`\u{-0}${0}right`, ["\\u{-0}","right"]);
|
||||
check(raw`left${0}\u{-0}`, ["left","\\u{-0}"]);
|
||||
check(raw`left${0}\u{-0}${1}right`, ["left","\\u{-0}","right"]);
|
||||
check(raw`\u{g}`, ["\\u{g}"]);
|
||||
check(raw`\u{g}${0}right`, ["\\u{g}","right"]);
|
||||
check(raw`left${0}\u{g}`, ["left","\\u{g}"]);
|
||||
check(raw`left${0}\u{g}${1}right`, ["left","\\u{g}","right"]);
|
||||
check(raw`\u{0`, ["\\u{0"]);
|
||||
check(raw`\u{0${0}right`, ["\\u{0","right"]);
|
||||
check(raw`left${0}\u{0`, ["left","\\u{0"]);
|
||||
check(raw`left${0}\u{0${1}right`, ["left","\\u{0","right"]);
|
||||
check(raw`\u{\u{0}`, ["\\u{\\u{0}"]);
|
||||
check(raw`\u{\u{0}${0}right`, ["\\u{\\u{0}","right"]);
|
||||
check(raw`left${0}\u{\u{0}`, ["left","\\u{\\u{0}"]);
|
||||
check(raw`left${0}\u{\u{0}${1}right`, ["left","\\u{\\u{0}","right"]);
|
||||
check(raw`\u{110000}`, ["\\u{110000}"]);
|
||||
check(raw`\u{110000}${0}right`, ["\\u{110000}","right"]);
|
||||
check(raw`left${0}\u{110000}`, ["left","\\u{110000}"]);
|
||||
check(raw`left${0}\u{110000}${1}right`, ["left","\\u{110000}","right"]);
|
||||
|
||||
check(cooked`\01`, [void 0]);
|
||||
check(cooked`\01${0}right`, [void 0,"right"]);
|
||||
check(cooked`left${0}\01`, ["left",void 0]);
|
||||
check(cooked`left${0}\01${1}right`, ["left",void 0,"right"]);
|
||||
check(cooked`\1`, [void 0]);
|
||||
check(cooked`\1${0}right`, [void 0,"right"]);
|
||||
check(cooked`left${0}\1`, ["left",void 0]);
|
||||
check(cooked`left${0}\1${1}right`, ["left",void 0,"right"]);
|
||||
check(cooked`\xg`, [void 0]);
|
||||
check(cooked`\xg${0}right`, [void 0,"right"]);
|
||||
check(cooked`left${0}\xg`, ["left",void 0]);
|
||||
check(cooked`left${0}\xg${1}right`, ["left",void 0,"right"]);
|
||||
check(cooked`\xAg`, [void 0]);
|
||||
check(cooked`\xAg${0}right`, [void 0,"right"]);
|
||||
check(cooked`left${0}\xAg`, ["left",void 0]);
|
||||
check(cooked`left${0}\xAg${1}right`, ["left",void 0,"right"]);
|
||||
check(cooked`\u0`, [void 0]);
|
||||
check(cooked`\u0${0}right`, [void 0,"right"]);
|
||||
check(cooked`left${0}\u0`, ["left",void 0]);
|
||||
check(cooked`left${0}\u0${1}right`, ["left",void 0,"right"]);
|
||||
check(cooked`\u0g`, [void 0]);
|
||||
check(cooked`\u0g${0}right`, [void 0,"right"]);
|
||||
check(cooked`left${0}\u0g`, ["left",void 0]);
|
||||
check(cooked`left${0}\u0g${1}right`, ["left",void 0,"right"]);
|
||||
check(cooked`\u00g`, [void 0]);
|
||||
check(cooked`\u00g${0}right`, [void 0,"right"]);
|
||||
check(cooked`left${0}\u00g`, ["left",void 0]);
|
||||
check(cooked`left${0}\u00g${1}right`, ["left",void 0,"right"]);
|
||||
check(cooked`\u000g`, [void 0]);
|
||||
check(cooked`\u000g${0}right`, [void 0,"right"]);
|
||||
check(cooked`left${0}\u000g`, ["left",void 0]);
|
||||
check(cooked`left${0}\u000g${1}right`, ["left",void 0,"right"]);
|
||||
check(cooked`\u{}`, [void 0]);
|
||||
check(cooked`\u{}${0}right`, [void 0,"right"]);
|
||||
check(cooked`left${0}\u{}`, ["left",void 0]);
|
||||
check(cooked`left${0}\u{}${1}right`, ["left",void 0,"right"]);
|
||||
check(cooked`\u{-0}`, [void 0]);
|
||||
check(cooked`\u{-0}${0}right`, [void 0,"right"]);
|
||||
check(cooked`left${0}\u{-0}`, ["left",void 0]);
|
||||
check(cooked`left${0}\u{-0}${1}right`, ["left",void 0,"right"]);
|
||||
check(cooked`\u{g}`, [void 0]);
|
||||
check(cooked`\u{g}${0}right`, [void 0,"right"]);
|
||||
check(cooked`left${0}\u{g}`, ["left",void 0]);
|
||||
check(cooked`left${0}\u{g}${1}right`, ["left",void 0,"right"]);
|
||||
check(cooked`\u{0`, [void 0]);
|
||||
check(cooked`\u{0${0}right`, [void 0,"right"]);
|
||||
check(cooked`left${0}\u{0`, ["left",void 0]);
|
||||
check(cooked`left${0}\u{0${1}right`, ["left",void 0,"right"]);
|
||||
check(cooked`\u{\u{0}`, [void 0]);
|
||||
check(cooked`\u{\u{0}${0}right`, [void 0,"right"]);
|
||||
check(cooked`left${0}\u{\u{0}`, ["left",void 0]);
|
||||
check(cooked`left${0}\u{\u{0}${1}right`, ["left",void 0,"right"]);
|
||||
check(cooked`\u{110000}`, [void 0]);
|
||||
check(cooked`\u{110000}${0}right`, [void 0,"right"]);
|
||||
check(cooked`left${0}\u{110000}`, ["left",void 0]);
|
||||
check(cooked`left${0}\u{110000}${1}right`, ["left",void 0,"right"]);
|
||||
|
||||
syntaxError("`\\01`");
|
||||
syntaxError("`\\01${0}right`");
|
||||
syntaxError("`left${0}\\01`");
|
||||
syntaxError("`left${0}\\01${1}right`");
|
||||
syntaxError("`\\1`");
|
||||
syntaxError("`\\1${0}right`");
|
||||
syntaxError("`left${0}\\1`");
|
||||
syntaxError("`left${0}\\1${1}right`");
|
||||
syntaxError("`\\xg`");
|
||||
syntaxError("`\\xg${0}right`");
|
||||
syntaxError("`left${0}\\xg`");
|
||||
syntaxError("`left${0}\\xg${1}right`");
|
||||
syntaxError("`\\xAg`");
|
||||
syntaxError("`\\xAg${0}right`");
|
||||
syntaxError("`left${0}\\xAg`");
|
||||
syntaxError("`left${0}\\xAg${1}right`");
|
||||
syntaxError("`\\u0`");
|
||||
syntaxError("`\\u0${0}right`");
|
||||
syntaxError("`left${0}\\u0`");
|
||||
syntaxError("`left${0}\\u0${1}right`");
|
||||
syntaxError("`\\u0g`");
|
||||
syntaxError("`\\u0g${0}right`");
|
||||
syntaxError("`left${0}\\u0g`");
|
||||
syntaxError("`left${0}\\u0g${1}right`");
|
||||
syntaxError("`\\u00g`");
|
||||
syntaxError("`\\u00g${0}right`");
|
||||
syntaxError("`left${0}\\u00g`");
|
||||
syntaxError("`left${0}\\u00g${1}right`");
|
||||
syntaxError("`\\u000g`");
|
||||
syntaxError("`\\u000g${0}right`");
|
||||
syntaxError("`left${0}\\u000g`");
|
||||
syntaxError("`left${0}\\u000g${1}right`");
|
||||
syntaxError("`\\u{}`");
|
||||
syntaxError("`\\u{}${0}right`");
|
||||
syntaxError("`left${0}\\u{}`");
|
||||
syntaxError("`left${0}\\u{}${1}right`");
|
||||
syntaxError("`\\u{-0}`");
|
||||
syntaxError("`\\u{-0}${0}right`");
|
||||
syntaxError("`left${0}\\u{-0}`");
|
||||
syntaxError("`left${0}\\u{-0}${1}right`");
|
||||
syntaxError("`\\u{g}`");
|
||||
syntaxError("`\\u{g}${0}right`");
|
||||
syntaxError("`left${0}\\u{g}`");
|
||||
syntaxError("`left${0}\\u{g}${1}right`");
|
||||
syntaxError("`\\u{0`");
|
||||
syntaxError("`\\u{0${0}right`");
|
||||
syntaxError("`left${0}\\u{0`");
|
||||
syntaxError("`left${0}\\u{0${1}right`");
|
||||
syntaxError("`\\u{\\u{0}`");
|
||||
syntaxError("`\\u{\\u{0}${0}right`");
|
||||
syntaxError("`left${0}\\u{\\u{0}`");
|
||||
syntaxError("`left${0}\\u{\\u{0}${1}right`");
|
||||
syntaxError("`\\u{110000}`");
|
||||
syntaxError("`\\u{110000}${0}right`");
|
||||
syntaxError("`left${0}\\u{110000}`");
|
||||
syntaxError("`left${0}\\u{110000}${1}right`");
|
||||
|
||||
|
||||
reportCompare(0, 0, "ok");
|
||||
|
|
|
@ -7,6 +7,8 @@ assertStringExpr("`hey\nthere`", literal("hey\nthere"));
|
|||
assertExpr("`hey${\"there\"}`", templateLit([lit("hey"), lit("there"), lit("")]));
|
||||
assertExpr("`hey${\"there\"}mine`", templateLit([lit("hey"), lit("there"), lit("mine")]));
|
||||
assertExpr("`hey${a == 5}mine`", templateLit([lit("hey"), binExpr("==", ident("a"), lit(5)), lit("mine")]));
|
||||
assertExpr("func`hey\\x`", taggedTemplate(ident("func"), template(["hey\\x"], [void 0])));
|
||||
assertExpr("func`hey${4}\\x`", taggedTemplate(ident("func"), template(["hey","\\x"], ["hey",void 0], lit(4))));
|
||||
assertExpr("`hey${`there${\"how\"}`}mine`", templateLit([lit("hey"),
|
||||
templateLit([lit("there"), lit("how"), lit("")]), lit("mine")]));
|
||||
assertExpr("func`hey`", taggedTemplate(ident("func"), template(["hey"], ["hey"])));
|
||||
|
|
Загрузка…
Ссылка в новой задаче