mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1031397 - Implement Tagged Templates as described in ES6 draft section 12.3.7. r=jorendorff
This commit is contained in:
parent
cc829656d8
commit
7797397ded
@ -1038,6 +1038,17 @@ EmitObjectOp(ExclusiveContext *cx, ObjectBox *objbox, JSOp op, BytecodeEmitter *
|
||||
return EmitInternedObjectOp(cx, bce->objectList.add(objbox), op, bce);
|
||||
}
|
||||
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
static bool
|
||||
EmitObjectPairOp(ExclusiveContext *cx, ObjectBox *objbox1, ObjectBox *objbox2, JSOp op,
|
||||
BytecodeEmitter *bce)
|
||||
{
|
||||
uint32_t index = bce->objectList.add(objbox1);
|
||||
bce->objectList.add(objbox2);
|
||||
return EmitInternedObjectOp(cx, index, op, bce);
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool
|
||||
EmitRegExp(ExclusiveContext *cx, uint32_t index, BytecodeEmitter *bce)
|
||||
{
|
||||
@ -3845,12 +3856,15 @@ EmitAssignment(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *lhs, JSOp
|
||||
}
|
||||
|
||||
bool
|
||||
ParseNode::getConstantValue(ExclusiveContext *cx, bool strictChecks, MutableHandleValue vp)
|
||||
ParseNode::getConstantValue(ExclusiveContext *cx, MutableHandleValue vp)
|
||||
{
|
||||
switch (getKind()) {
|
||||
case PNK_NUMBER:
|
||||
vp.setNumber(pn_dval);
|
||||
return true;
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
case PNK_TEMPLATE_STRING:
|
||||
#endif
|
||||
case PNK_STRING:
|
||||
vp.setString(pn_atom);
|
||||
return true;
|
||||
@ -3865,25 +3879,43 @@ ParseNode::getConstantValue(ExclusiveContext *cx, bool strictChecks, MutableHand
|
||||
return true;
|
||||
case PNK_SPREAD:
|
||||
return false;
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
case PNK_CALLSITEOBJ:
|
||||
#endif
|
||||
case PNK_ARRAY: {
|
||||
JS_ASSERT(isOp(JSOP_NEWINIT) && !(pn_xflags & PNX_NONCONST));
|
||||
RootedValue value(cx);
|
||||
unsigned count;
|
||||
ParseNode *pn;
|
||||
|
||||
RootedObject obj(cx,
|
||||
NewDenseAllocatedArray(cx, pn_count, nullptr, MaybeSingletonObject));
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
bool isArray = (getKind() == PNK_ARRAY);
|
||||
|
||||
if (!isArray) {
|
||||
count = pn_count - 1;
|
||||
pn = pn_head->pn_next;
|
||||
} else {
|
||||
#endif
|
||||
JS_ASSERT(isOp(JSOP_NEWINIT) && !(pn_xflags & PNX_NONCONST));
|
||||
count = pn_count;
|
||||
pn = pn_head;
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
}
|
||||
#endif
|
||||
|
||||
RootedObject obj(cx, NewDenseAllocatedArray(cx, count, nullptr, MaybeSingletonObject));
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
unsigned idx = 0;
|
||||
RootedId id(cx);
|
||||
RootedValue value(cx);
|
||||
for (ParseNode *pn = pn_head; pn; idx++, pn = pn->pn_next) {
|
||||
if (!pn->getConstantValue(cx, strictChecks, &value))
|
||||
for (; pn; idx++, pn = pn->pn_next) {
|
||||
if (!pn->getConstantValue(cx, &value))
|
||||
return false;
|
||||
id = INT_TO_JSID(idx);
|
||||
if (!JSObject::defineGeneric(cx, obj, id, value, nullptr, nullptr, JSPROP_ENUMERATE))
|
||||
return false;
|
||||
}
|
||||
JS_ASSERT(idx == pn_count);
|
||||
JS_ASSERT(idx == count);
|
||||
|
||||
types::FixArrayType(cx, obj);
|
||||
vp.setObject(*obj);
|
||||
@ -3900,7 +3932,7 @@ ParseNode::getConstantValue(ExclusiveContext *cx, bool strictChecks, MutableHand
|
||||
|
||||
RootedValue value(cx), idvalue(cx);
|
||||
for (ParseNode *pn = pn_head; pn; pn = pn->pn_next) {
|
||||
if (!pn->pn_right->getConstantValue(cx, strictChecks, &value))
|
||||
if (!pn->pn_right->getConstantValue(cx, &value))
|
||||
return false;
|
||||
|
||||
ParseNode *pnid = pn->pn_left;
|
||||
@ -3954,7 +3986,7 @@ static bool
|
||||
EmitSingletonInitialiser(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
{
|
||||
RootedValue value(cx);
|
||||
if (!pn->getConstantValue(cx, bce->sc->needStrictChecks(), &value))
|
||||
if (!pn->getConstantValue(cx, &value))
|
||||
return false;
|
||||
|
||||
JS_ASSERT(value.isObject());
|
||||
@ -3965,6 +3997,33 @@ EmitSingletonInitialiser(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *
|
||||
return EmitObjectOp(cx, objbox, JSOP_OBJECT, bce);
|
||||
}
|
||||
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
static bool
|
||||
EmitCallSiteObject(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
{
|
||||
RootedValue value(cx);
|
||||
if (!pn->getConstantValue(cx, &value))
|
||||
return false;
|
||||
|
||||
JS_ASSERT(value.isObject());
|
||||
|
||||
ObjectBox *objbox1 = bce->parser->newObjectBox(&value.toObject());
|
||||
if (!objbox1)
|
||||
return false;
|
||||
|
||||
if (!pn->as<CallSiteNode>().getRawArrayValue(cx, &value))
|
||||
return false;
|
||||
|
||||
JS_ASSERT(value.isObject());
|
||||
|
||||
ObjectBox *objbox2 = bce->parser->newObjectBox(&value.toObject());
|
||||
if (!objbox2)
|
||||
return false;
|
||||
|
||||
return EmitObjectPairOp(cx, objbox1, objbox2, JSOP_CALLSITEOBJ, bce);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* See the SRC_FOR source note offsetBias comments later in this file. */
|
||||
JS_STATIC_ASSERT(JSOP_NOP_LENGTH == 1);
|
||||
JS_STATIC_ASSERT(JSOP_POP_LENGTH == 1);
|
||||
@ -5580,7 +5639,11 @@ EmitArray(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, uint32_t co
|
||||
static bool
|
||||
EmitCallOrNew(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
{
|
||||
bool callop = pn->isKind(PNK_CALL);
|
||||
bool callop = pn->isKind(PNK_CALL)
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
|| pn->isKind(PNK_TAGGED_TEMPLATE)
|
||||
#endif
|
||||
;
|
||||
|
||||
/*
|
||||
* Emit callable invocation or operator new (constructor call) code.
|
||||
@ -6604,6 +6667,9 @@ frontend::EmitTree(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
break;
|
||||
|
||||
case PNK_NEW:
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
case PNK_TAGGED_TEMPLATE:
|
||||
#endif
|
||||
case PNK_CALL:
|
||||
case PNK_GENEXP:
|
||||
ok = EmitCallOrNew(cx, bce, pn);
|
||||
@ -6640,7 +6706,11 @@ frontend::EmitTree(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
case PNK_CALLSITEOBJ:
|
||||
ok = EmitCallSiteObject(cx, bce, pn);
|
||||
break;
|
||||
#endif
|
||||
case PNK_ARRAY:
|
||||
if (!(pn->pn_xflags & PNX_NONCONST) && pn->pn_head && bce->checkSingletonContext())
|
||||
ok = EmitSingletonInitialiser(cx, bce, pn);
|
||||
|
@ -124,6 +124,38 @@ class FullParseHandler
|
||||
ParseNode *newTemplateStringLiteral(JSAtom *atom, const TokenPos &pos) {
|
||||
return new_<NullaryNode>(PNK_TEMPLATE_STRING, JSOP_NOP, pos, atom);
|
||||
}
|
||||
|
||||
ParseNode *newCallSiteObject(uint32_t begin, unsigned blockidGen) {
|
||||
ParseNode *callSite = new_<CallSiteNode>(begin);
|
||||
if (!callSite)
|
||||
return null();
|
||||
|
||||
Node propExpr = newArrayLiteral(getPosition(callSite).begin, blockidGen);
|
||||
if (!propExpr)
|
||||
return null();
|
||||
|
||||
if (!addArrayElement(callSite, propExpr))
|
||||
return null();
|
||||
|
||||
return callSite;
|
||||
}
|
||||
|
||||
bool addToCallSiteObject(ParseNode *callSiteObj, ParseNode *rawNode, ParseNode *cookedNode) {
|
||||
MOZ_ASSERT(callSiteObj->isKind(PNK_CALLSITEOBJ));
|
||||
|
||||
if (!addArrayElement(callSiteObj, cookedNode))
|
||||
return false;
|
||||
if (!addArrayElement(callSiteObj->pn_head, rawNode))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* We don't know when the last noSubstTemplate will come in, and we
|
||||
* don't want to deal with this outside this method
|
||||
*/
|
||||
setEndPosition(callSiteObj, callSiteObj->pn_head);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
ParseNode *newThisLiteral(const TokenPos &pos) {
|
||||
|
@ -93,6 +93,8 @@ class UpvarCookie
|
||||
F(STRING) \
|
||||
F(TEMPLATE_STRING_LIST) \
|
||||
F(TEMPLATE_STRING) \
|
||||
F(TAGGED_TEMPLATE) \
|
||||
F(CALLSITEOBJ) \
|
||||
F(REGEXP) \
|
||||
F(TRUE) \
|
||||
F(FALSE) \
|
||||
@ -397,6 +399,14 @@ enum ParseNodeKind
|
||||
* with pn_cookie telling (staticLevel, slot) (see
|
||||
* jsscript.h's UPVAR macros) and pn_dflags telling
|
||||
* const-ness and static analysis results
|
||||
* PNK_TEMPLATE_STRING_LIST pn_head: list of alternating expr and template strings
|
||||
* list
|
||||
* PNK_TEMPLATE_STRING pn_atom: template string atom
|
||||
nullary pn_op: JSOP_NOP
|
||||
* PNK_TAGGED_TEMPLATE pn_head: list of call, call site object, arg1, arg2, ... argN
|
||||
* list pn_count: 2 + N (N is the number of substitutions)
|
||||
* PNK_CALLSITEOBJ list pn_head: a PNK_ARRAY node followed by
|
||||
* list of pn_count - 1 PNK_TEMPLATE_STRING nodes
|
||||
* PNK_REGEXP nullary pn_objbox: RegExp model object
|
||||
* PNK_NAME name If pn_used, PNK_NAME uses the lexdef member instead
|
||||
* of the expr member it overlays
|
||||
@ -818,7 +828,7 @@ class ParseNode
|
||||
#endif
|
||||
;
|
||||
|
||||
bool getConstantValue(ExclusiveContext *cx, bool strictChecks, MutableHandleValue vp);
|
||||
bool getConstantValue(ExclusiveContext *cx, MutableHandleValue vp);
|
||||
inline bool isConstant();
|
||||
|
||||
template <class NodeType>
|
||||
@ -1250,6 +1260,23 @@ class PropertyByValue : public ParseNode
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
/*
|
||||
* A CallSiteNode represents the implicit call site object argument in a TaggedTemplate.
|
||||
*/
|
||||
struct CallSiteNode : public ListNode {
|
||||
explicit CallSiteNode(uint32_t begin): ListNode(PNK_CALLSITEOBJ, TokenPos(begin, begin + 1)) {}
|
||||
|
||||
static bool test(const ParseNode &node) {
|
||||
return node.isKind(PNK_CALLSITEOBJ);
|
||||
}
|
||||
|
||||
bool getRawArrayValue(ExclusiveContext *cx, MutableHandleValue vp) {
|
||||
return pn_head->getConstantValue(cx, vp);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
void DumpParseTree(ParseNode *pn, int indent = 0);
|
||||
#endif
|
||||
@ -1445,6 +1472,9 @@ ParseNode::isConstant()
|
||||
switch (pn_type) {
|
||||
case PNK_NUMBER:
|
||||
case PNK_STRING:
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
case PNK_TEMPLATE_STRING:
|
||||
#endif
|
||||
case PNK_NULL:
|
||||
case PNK_FALSE:
|
||||
case PNK_TRUE:
|
||||
|
@ -1964,45 +1964,67 @@ Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
}
|
||||
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::addExprAndGetNextTemplStrToken(Node nodeList, TokenKind &tt)
|
||||
{
|
||||
Node pn = expr();
|
||||
if (!pn)
|
||||
return false;
|
||||
handler.addList(nodeList, pn);
|
||||
|
||||
tt = tokenStream.getToken();
|
||||
if (tt != TOK_RC) {
|
||||
if (tt != TOK_ERROR)
|
||||
report(ParseError, false, null(), JSMSG_TEMPLSTR_UNTERM_EXPR);
|
||||
return false;
|
||||
}
|
||||
|
||||
tt = tokenStream.getToken(TokenStream::TemplateTail);
|
||||
if (tt == TOK_ERROR)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::taggedTemplate(Node nodeList, TokenKind tt)
|
||||
{
|
||||
Node callSiteObjNode = handler.newCallSiteObject(pos().begin, pc->blockidGen);
|
||||
if (!callSiteObjNode)
|
||||
return false;
|
||||
handler.addList(nodeList, callSiteObjNode);
|
||||
|
||||
while (true) {
|
||||
if (!appendToCallSiteObj(callSiteObjNode))
|
||||
return false;
|
||||
if (tt != TOK_TEMPLATE_HEAD)
|
||||
break;
|
||||
|
||||
if (!addExprAndGetNextTemplStrToken(nodeList, tt))
|
||||
return false;
|
||||
}
|
||||
handler.setEndPosition(nodeList, callSiteObjNode);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::templateLiteral()
|
||||
{
|
||||
Node pn = noSubstitutionTemplate();
|
||||
if (!pn) {
|
||||
report(ParseError, false, null(),
|
||||
JSMSG_SYNTAX_ERROR);
|
||||
if (!pn)
|
||||
return null();
|
||||
}
|
||||
Node nodeList = handler.newList(PNK_TEMPLATE_STRING_LIST, pn);
|
||||
|
||||
TokenKind tt;
|
||||
do {
|
||||
pn = expr();
|
||||
if (!pn) {
|
||||
report(ParseError, false, null(),
|
||||
JSMSG_SYNTAX_ERROR);
|
||||
if (!addExprAndGetNextTemplStrToken(nodeList, tt))
|
||||
return null();
|
||||
}
|
||||
handler.addList(nodeList, pn);
|
||||
tt = tokenStream.getToken();
|
||||
if (tt != TOK_RC) {
|
||||
report(ParseError, false, null(),
|
||||
JSMSG_SYNTAX_ERROR);
|
||||
return null();
|
||||
}
|
||||
tt = tokenStream.getToken(TokenStream::TemplateTail);
|
||||
if (tt == TOK_ERROR) {
|
||||
report(ParseError, false, null(),
|
||||
JSMSG_SYNTAX_ERROR);
|
||||
return null();
|
||||
}
|
||||
|
||||
pn = noSubstitutionTemplate();
|
||||
if (!pn) {
|
||||
report(ParseError, false, null(),
|
||||
JSMSG_SYNTAX_ERROR);
|
||||
if (!pn)
|
||||
return null();
|
||||
}
|
||||
|
||||
handler.addList(nodeList, pn);
|
||||
} while (tt == TOK_TEMPLATE_HEAD);
|
||||
@ -2289,6 +2311,26 @@ Parser<SyntaxParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun,
|
||||
return outerpc->innerFunctions.append(fun);
|
||||
}
|
||||
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::appendToCallSiteObj(Node callSiteObj)
|
||||
{
|
||||
Node cookedNode = noSubstitutionTemplate();
|
||||
if (!cookedNode)
|
||||
return false;
|
||||
|
||||
JSAtom *atom = tokenStream.getRawTemplateStringAtom();
|
||||
if (!atom)
|
||||
return false;
|
||||
Node rawNode = handler.newTemplateStringLiteral(atom, pos());
|
||||
if (!rawNode)
|
||||
return false;
|
||||
|
||||
return handler.addToCallSiteObject(callSiteObj, rawNode, cookedNode);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <>
|
||||
ParseNode *
|
||||
Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, unsigned staticLevel,
|
||||
@ -6922,14 +6964,32 @@ Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax)
|
||||
nextMember = handler.newPropertyByValue(lhs, propExpr, pos().end);
|
||||
if (!nextMember)
|
||||
return null();
|
||||
} else if (allowCallSyntax && tt == TOK_LP) {
|
||||
} else if ((allowCallSyntax &&
|
||||
tt == TOK_LP)
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
|| tt == TOK_TEMPLATE_HEAD
|
||||
|| tt == TOK_NO_SUBS_TEMPLATE
|
||||
#endif
|
||||
)
|
||||
{
|
||||
JSOp op = JSOP_CALL;
|
||||
nextMember = handler.newList(PNK_CALL, null(), JSOP_CALL);
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
if (tt == TOK_LP)
|
||||
#endif
|
||||
nextMember = handler.newList(PNK_CALL, null(), JSOP_CALL);
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
else
|
||||
nextMember = handler.newList(PNK_TAGGED_TEMPLATE, null(), JSOP_CALL);
|
||||
#endif
|
||||
if (!nextMember)
|
||||
return null();
|
||||
|
||||
if (JSAtom *atom = handler.isName(lhs)) {
|
||||
if (atom == context->names().eval) {
|
||||
if (
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
tt == TOK_LP &&
|
||||
#endif
|
||||
atom == context->names().eval) {
|
||||
/* Select JSOP_EVAL and flag pc as heavyweight. */
|
||||
op = JSOP_EVAL;
|
||||
pc->sc->setBindingsAccessedDynamically();
|
||||
@ -6955,11 +7015,20 @@ Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax)
|
||||
handler.setBeginPosition(nextMember, lhs);
|
||||
handler.addList(nextMember, lhs);
|
||||
|
||||
bool isSpread = false;
|
||||
if (!argumentList(nextMember, &isSpread))
|
||||
return null();
|
||||
if (isSpread)
|
||||
op = (op == JSOP_EVAL ? JSOP_SPREADEVAL : JSOP_SPREADCALL);
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
if (tt == TOK_LP) {
|
||||
#endif
|
||||
bool isSpread = false;
|
||||
if (!argumentList(nextMember, &isSpread))
|
||||
return null();
|
||||
if (isSpread)
|
||||
op = (op == JSOP_EVAL ? JSOP_SPREADEVAL : JSOP_SPREADCALL);
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
} else {
|
||||
if (!taggedTemplate(nextMember, tt))
|
||||
return null();
|
||||
}
|
||||
#endif
|
||||
handler.setOp(nextMember, op);
|
||||
} else {
|
||||
tokenStream.ungetToken();
|
||||
|
@ -437,6 +437,9 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
Node noSubstitutionTemplate();
|
||||
Node templateLiteral();
|
||||
bool taggedTemplate(Node nodeList, TokenKind tt);
|
||||
bool appendToCallSiteObj(Node callSiteObj);
|
||||
bool addExprAndGetNextTemplStrToken(Node nodeList, TokenKind &tt);
|
||||
#endif
|
||||
inline Node newName(PropertyName *name);
|
||||
|
||||
|
@ -79,6 +79,14 @@ class SyntaxParseHandler
|
||||
Node newTemplateStringLiteral(JSAtom *atom, const TokenPos &pos) {
|
||||
return NodeGeneric;
|
||||
}
|
||||
|
||||
Node newCallSiteObject(uint32_t begin, unsigned blockidGen) {
|
||||
return NodeGeneric;
|
||||
}
|
||||
|
||||
bool addToCallSiteObject(Node callSiteObj, Node rawNode, Node cookedNode) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
Node newThisLiteral(const TokenPos &pos) { return NodeGeneric; }
|
||||
|
@ -470,6 +470,36 @@ class MOZ_STACK_CLASS TokenStream
|
||||
// asm.js reporter
|
||||
void reportAsmJSError(uint32_t offset, unsigned errorNumber, ...);
|
||||
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
JSAtom *getRawTemplateStringAtom() {
|
||||
JS_ASSERT(currentToken().type == TOK_TEMPLATE_HEAD ||
|
||||
currentToken().type == TOK_NO_SUBS_TEMPLATE);
|
||||
const jschar *cur = userbuf.base() + currentToken().pos.begin + 1;
|
||||
const jschar *end;
|
||||
if (currentToken().type == TOK_TEMPLATE_HEAD) {
|
||||
// Of the form |`...${| or |}...${|
|
||||
end = userbuf.base() + currentToken().pos.end - 2;
|
||||
} else {
|
||||
// NO_SUBS_TEMPLATE is of the form |`...`| or |}...`|
|
||||
end = userbuf.base() + currentToken().pos.end - 1;
|
||||
}
|
||||
|
||||
CharBuffer charbuf(cx);
|
||||
while (cur < end) {
|
||||
int32_t ch = *cur;
|
||||
if (ch == '\r') {
|
||||
ch = '\n';
|
||||
if ((cur + 1 < end) && (*(cur + 1) == '\n'))
|
||||
cur++;
|
||||
}
|
||||
if (!charbuf.append(ch))
|
||||
return nullptr;
|
||||
cur++;
|
||||
}
|
||||
return AtomizeChars(cx, charbuf.begin(), charbuf.length());
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
// These are private because they should only be called by the tokenizer
|
||||
// while tokenizing not by, for example, BytecodeEmitter.
|
||||
|
@ -44,4 +44,10 @@ function evalWithCache(code, ctx) {
|
||||
assertEq(res0, res2);
|
||||
assertEq(res0, res3);
|
||||
}
|
||||
|
||||
if (ctx.checkFrozen) {
|
||||
assertEq(Object.isFrozen(res0), Object.isFrozen(res1));
|
||||
assertEq(Object.isFrozen(res0), Object.isFrozen(res2));
|
||||
assertEq(Object.isFrozen(res0), Object.isFrozen(res3));
|
||||
}
|
||||
}
|
||||
|
@ -36,3 +36,10 @@ evalWithCache(test, { assertEqBytecode: true, assertEqResult : true });
|
||||
// code a function which has an object literal.
|
||||
test = "function f() { return { x: 2 }; }; f();";
|
||||
evalWithCache(test, { assertEqBytecode: true });
|
||||
|
||||
// code call site object
|
||||
var hasTemplateStrings = false; try { eval("``"); hasTemplateStrings = true; } catch (exc) { }
|
||||
if (hasTemplateStrings == true) {
|
||||
test = "function f(a) { return a; }; f`a${4}b`;";
|
||||
evalWithCache(test, { assertEqBytecode: true, checkFrozen: true});
|
||||
}
|
||||
|
@ -442,3 +442,4 @@ MSG_DEF(JSMSG_PROXY_CONSTRUCT_OBJECT, 387, 0, JSEXN_TYPEERR, "proxy [[Construc
|
||||
MSG_DEF(JSMSG_PROXY_GETOWN_OBJORUNDEF, 388, 0, JSEXN_TYPEERR, "proxy [[GetOwnProperty]] must return an object or undefined")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_C_AS_NC, 389, 0, JSEXN_TYPEERR, "proxy can't report existing configurable property as non-configurable")
|
||||
MSG_DEF(JSMSG_PROXY_REVOKED, 390, 0, JSEXN_TYPEERR, "illegal operation attempted on a revoked proxy")
|
||||
MSG_DEF(JSMSG_TEMPLSTR_UNTERM_EXPR, 391, 0, JSEXN_SYNTAXERR, "missing } in template string")
|
||||
|
@ -71,5 +71,7 @@ ASTDEF(AST_ARRAY_PATT, "ArrayPattern", "arrayPatter
|
||||
ASTDEF(AST_OBJECT_PATT, "ObjectPattern", "objectPattern")
|
||||
ASTDEF(AST_PROP_PATT, "Property", "propertyPattern")
|
||||
ASTDEF(AST_TEMPLATE_LITERAL, "TemplateLiteral", "templateLiteral")
|
||||
ASTDEF(AST_TAGGED_TEMPLATE, "TaggedTemplate", "taggedTemplate")
|
||||
ASTDEF(AST_CALL_SITE_OBJ, "CallSiteObject", "callSiteObject")
|
||||
|
||||
/* AST_LIMIT = last + 1 */
|
||||
|
@ -2210,6 +2210,22 @@ js::XDRObjectLiteral(XDRState<mode> *xdr, MutableHandleObject obj)
|
||||
FixObjectType(cx, obj);
|
||||
}
|
||||
|
||||
{
|
||||
uint32_t frozen;
|
||||
bool extensible;
|
||||
if (mode == XDR_ENCODE) {
|
||||
if (!JSObject::isExtensible(cx, obj, &extensible))
|
||||
return false;
|
||||
frozen = extensible ? 0 : 1;
|
||||
}
|
||||
if (!xdr->codeUint32(&frozen))
|
||||
return false;
|
||||
if (mode == XDR_DECODE && frozen == 1) {
|
||||
if (!JSObject::freeze(cx, obj))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -629,8 +629,15 @@ class NodeBuilder
|
||||
|
||||
bool arrayExpression(NodeVector &elts, TokenPos *pos, MutableHandleValue dst);
|
||||
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
bool templateLiteral(NodeVector &elts, TokenPos *pos, MutableHandleValue dst);
|
||||
|
||||
bool taggedTemplate(HandleValue callee, NodeVector &args, TokenPos *pos,
|
||||
MutableHandleValue dst);
|
||||
|
||||
bool callSiteObj(NodeVector &raw, NodeVector &cooked, TokenPos *pos, MutableHandleValue dst);
|
||||
#endif
|
||||
|
||||
bool spreadExpression(HandleValue expr, TokenPos *pos, MutableHandleValue dst);
|
||||
|
||||
bool objectExpression(NodeVector &elts, TokenPos *pos, MutableHandleValue dst);
|
||||
@ -1212,6 +1219,37 @@ NodeBuilder::arrayExpression(NodeVector &elts, TokenPos *pos, MutableHandleValue
|
||||
}
|
||||
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
bool
|
||||
NodeBuilder::callSiteObj(NodeVector &raw, NodeVector &cooked, TokenPos *pos, MutableHandleValue dst)
|
||||
{
|
||||
RootedValue rawVal(cx);
|
||||
if (!newArray(raw, &rawVal))
|
||||
return false;
|
||||
|
||||
RootedValue cookedVal(cx);
|
||||
if (!newArray(cooked, &cookedVal))
|
||||
return false;
|
||||
|
||||
return newNode(AST_CALL_SITE_OBJ, pos,
|
||||
"raw", rawVal,
|
||||
"cooked", cookedVal,
|
||||
dst);
|
||||
}
|
||||
|
||||
bool
|
||||
NodeBuilder::taggedTemplate(HandleValue callee, NodeVector &args, TokenPos *pos,
|
||||
MutableHandleValue dst)
|
||||
{
|
||||
RootedValue array(cx);
|
||||
if (!newArray(args, &array))
|
||||
return false;
|
||||
|
||||
return newNode(AST_TAGGED_TEMPLATE, pos,
|
||||
"callee", callee,
|
||||
"arguments", array,
|
||||
dst);
|
||||
}
|
||||
|
||||
bool
|
||||
NodeBuilder::templateLiteral(NodeVector &elts, TokenPos *pos, MutableHandleValue dst)
|
||||
{
|
||||
@ -2728,6 +2766,9 @@ ASTSerializer::expression(ParseNode *pn, MutableHandleValue dst)
|
||||
#endif
|
||||
|
||||
case PNK_NEW:
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
case PNK_TAGGED_TEMPLATE:
|
||||
#endif
|
||||
case PNK_CALL:
|
||||
{
|
||||
ParseNode *next = pn->pn_head;
|
||||
@ -2750,6 +2791,10 @@ ASTSerializer::expression(ParseNode *pn, MutableHandleValue dst)
|
||||
args.infallibleAppend(arg);
|
||||
}
|
||||
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
if (pn->getKind() == PNK_TAGGED_TEMPLATE)
|
||||
return builder.taggedTemplate(callee, args, &pn->pn_pos, dst);
|
||||
#endif
|
||||
return pn->isKind(PNK_NEW)
|
||||
? builder.newExpression(callee, args, &pn->pn_pos, dst)
|
||||
|
||||
@ -2777,7 +2822,36 @@ ASTSerializer::expression(ParseNode *pn, MutableHandleValue dst)
|
||||
expression(pn->pn_right, &right) &&
|
||||
builder.memberExpression(true, left, right, &pn->pn_pos, dst);
|
||||
}
|
||||
#ifdef JS_HAS_TEMPLATE_STRINGS
|
||||
case PNK_CALLSITEOBJ:
|
||||
{
|
||||
NodeVector raw(cx);
|
||||
if (!raw.reserve(pn->pn_head->pn_count))
|
||||
return false;
|
||||
for (ParseNode *next = pn->pn_head->pn_head; next; next = next->pn_next) {
|
||||
JS_ASSERT(pn->pn_pos.encloses(next->pn_pos));
|
||||
|
||||
RootedValue expr(cx);
|
||||
expr.setString(next->pn_atom);
|
||||
raw.infallibleAppend(expr);
|
||||
}
|
||||
|
||||
NodeVector cooked(cx);
|
||||
if (!cooked.reserve(pn->pn_count - 1))
|
||||
return false;
|
||||
|
||||
for (ParseNode *next = pn->pn_head->pn_next; next; next = next->pn_next) {
|
||||
JS_ASSERT(pn->pn_pos.encloses(next->pn_pos));
|
||||
|
||||
RootedValue expr(cx);
|
||||
expr.setString(next->pn_atom);
|
||||
cooked.infallibleAppend(expr);
|
||||
}
|
||||
|
||||
return builder.callSiteObj(raw, cooked, &pn->pn_pos, dst);
|
||||
}
|
||||
|
||||
#endif
|
||||
case PNK_ARRAY:
|
||||
{
|
||||
NodeVector elts(cx);
|
||||
|
297
js/src/tests/ecma_6/TemplateStrings/tagTempl.js
Normal file
297
js/src/tests/ecma_6/TemplateStrings/tagTempl.js
Normal file
@ -0,0 +1,297 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// This test case is weird in the sense the actual work happens at the eval
|
||||
// at the end. If template strings are not enabled, the test cases would throw
|
||||
// a syntax error and we'd have failure reported. To avoid that, the entire
|
||||
// test case is commented out and is part of a function. We use toString to
|
||||
// get the string version, obtain the actual lines to run, and then use eval to
|
||||
// do the actual evaluation.
|
||||
|
||||
function testCaseFn() {
|
||||
/*
|
||||
function syntaxError (script) {
|
||||
try {
|
||||
Function(script);
|
||||
} catch (e) {
|
||||
if (e.name === "SyntaxError") {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new Error('Expected syntax error: ' + script);
|
||||
}
|
||||
|
||||
// function definitions
|
||||
function check(actual, expected) {
|
||||
assertEq(actual.length, expected.length);
|
||||
for (var i = 0; i < expected.length; i++)
|
||||
assertEq(actual[i], expected[i]);
|
||||
}
|
||||
|
||||
function cooked(cs) { return cs; }
|
||||
function raw(cs) { return cs.raw; }
|
||||
function args(cs, ...rest) { return rest; }
|
||||
|
||||
|
||||
// TEST BEGIN
|
||||
|
||||
// Literals
|
||||
check(raw``, [""]);
|
||||
check(raw`${4}a`, ["","a"]);
|
||||
check(raw`${4}`, ["",""]);
|
||||
check(raw`a${4}`, ["a",""]);
|
||||
check(raw`a${4}b`, ["a","b"]);
|
||||
check(raw`a${4}b${3}`, ["a","b",""]);
|
||||
check(raw`a${4}b${3}c`, ["a","b","c"]);
|
||||
check(raw`a${4}${3}c`, ["a","","c"]);
|
||||
check(raw`${4}${3}`, ["","",""]);
|
||||
check(raw`${4}\r\r${3}`, ["","\\r\\r",""]);
|
||||
check(raw`${4}${3}c`, ["","","c"]);
|
||||
check(raw`zyx${4}wvut${3}c`, ["zyx","wvut","c"]);
|
||||
check(raw`zyx${4}wvut${3}\r\n`, ["zyx","wvut","\\r\\n"]);
|
||||
|
||||
check(raw`hey`, ["hey"]);
|
||||
check(raw`he\r\ny`, ["he\\r\\ny"]);
|
||||
check(raw`he\ry`, ["he\\ry"]);
|
||||
check(raw`he\r\ry`, ["he\\r\\ry"]);
|
||||
check(raw`he\ny`, ["he\\ny"]);
|
||||
check(raw`he\n\ny`, ["he\\n\\ny"]);
|
||||
|
||||
check(cooked`hey`, ["hey"]);
|
||||
check(cooked`he\r\ny`, ["he\r\ny"]);
|
||||
check(cooked`he\ry`, ["he\ry"]);
|
||||
check(cooked`he\r\ry`, ["he\r\ry"]);
|
||||
check(cooked`he\ny`, ["he\ny"]);
|
||||
check(cooked`he\n\ny`, ["he\n\ny"]);
|
||||
|
||||
check(eval("raw`\r`"), ["\n"]);
|
||||
check(eval("raw`\r\n`"), ["\n"]);
|
||||
check(eval("raw`\r\r\n`"), ["\n\n"]);
|
||||
check(eval("raw`he\r\ny`"), ["he\ny"]);
|
||||
check(eval("raw`he\ry`"), ["he\ny"]);
|
||||
check(eval("raw`he\r\ry`"), ["he\n\ny"]);
|
||||
check(eval("raw`he\r\r\ny`"), ["he\n\ny"]);
|
||||
|
||||
|
||||
check(eval("cooked`\r`"), ["\n"]);
|
||||
check(eval("cooked`\r\n`"), ["\n"]);
|
||||
check(eval("cooked`\r\r\n`"), ["\n\n"]);
|
||||
check(eval("cooked`he\r\ny`"), ["he\ny"]);
|
||||
check(eval("cooked`he\ry`"), ["he\ny"]);
|
||||
check(eval("cooked`he\r\ry`"), ["he\n\ny"]);
|
||||
check(eval("cooked`he\r\r\ny`"), ["he\n\ny"]);
|
||||
|
||||
// Expressions
|
||||
check(args`hey${"there"}now`, ["there"]);
|
||||
check(args`hey${4}now`, [4]);
|
||||
check(args`hey${4}`, [4]);
|
||||
check(args`${4}`, [4]);
|
||||
check(args`${4}${5}`, [4,5]);
|
||||
check(args`a${4}${5}`, [4,5]);
|
||||
check(args`a${4}b${5}`, [4,5]);
|
||||
check(args`a${4}b${5}c`, [4,5]);
|
||||
check(args`${4}b${5}c`, [4,5]);
|
||||
check(args`${4}${5}c`, [4,5]);
|
||||
|
||||
var a = 10;
|
||||
var b = 15;
|
||||
check(args`${4 + a}${5 + b}c`, [14,20]);
|
||||
check(args`${4 + a}${a + b}c`, [14,25]);
|
||||
check(args`${b + a}${5 + b}c`, [25,20]);
|
||||
check(args`${4 + a}${a + b}c${"a"}`, [14,25,"a"]);
|
||||
check(args`a${"b"}${"c"}${"d"}`, ["b","c","d"]);
|
||||
check(args`a${"b"}${"c"}${a + b}`, ["b","c",25]);
|
||||
check(args`a${"b"}`, ["b"]);
|
||||
|
||||
// Expressions - complex substitutions
|
||||
check(args`${`hey ${b + a} there`}${5 + b}c`, ["hey 25 there",20]);
|
||||
check(args`${`hey ${`my ${b + a} good`} there`}${5 + b}c`, ["hey my 25 good there",20]);
|
||||
|
||||
syntaxError("args`${}`");
|
||||
syntaxError("args`${`");
|
||||
syntaxError("args`${\\n}`");
|
||||
syntaxError("args`${yield 0}`");
|
||||
syntaxError("args`");
|
||||
syntaxError("args`$");
|
||||
syntaxError("args`${");
|
||||
syntaxError("args.``");
|
||||
|
||||
// Template substitution tests in the context of tagged templates
|
||||
// Extra whitespace inside a template substitution is ignored.
|
||||
check(args`a${
|
||||
0
|
||||
}`, [0]);
|
||||
|
||||
// Extra whitespace between tag and template is ignored
|
||||
check(args
|
||||
`a
|
||||
${
|
||||
0
|
||||
}`, [0]);
|
||||
|
||||
|
||||
check(args`${5}${ // Comments work in template substitutions.
|
||||
// Even comments that look like code:
|
||||
// 0}`, "FAIL"); /* NOTE: This whole line is a comment.
|
||||
0}`, [5,0]);
|
||||
|
||||
check(args // Comments work in template substitutions.
|
||||
// Even comments that look like code:
|
||||
// 0}`, "FAIL"); /* NOTE: This whole line is a comment.
|
||||
`${5}${0}`, [5,0]);
|
||||
|
||||
|
||||
// Template substitutions are expressions, not statements.
|
||||
syntaxError("args`${0;}`");
|
||||
check(args`${
|
||||
function f() {
|
||||
return "ok";
|
||||
}()
|
||||
}`, ["ok"]);
|
||||
|
||||
// Template substitutions can have side effects.
|
||||
var x = 0;
|
||||
check(args`${x += 1}`, [1]);
|
||||
assertEq(x, 1);
|
||||
|
||||
// The production for a template substitution is Expression, not
|
||||
// AssignmentExpression.
|
||||
x = 0;
|
||||
check(args`${++x, "o"}k`, ["o"]);
|
||||
assertEq(x, 1);
|
||||
|
||||
|
||||
// --> is not a comment inside a template.
|
||||
check(cooked`
|
||||
--> this is text
|
||||
`, ["\n--> this is text\n"]);
|
||||
|
||||
// reentrancy
|
||||
function f(n) {
|
||||
if (n === 0)
|
||||
return "";
|
||||
var res = args`${n}${f(n - 1)}`;
|
||||
return res[0] + res[1] + "";
|
||||
}
|
||||
assertEq(f(9), "987654321");
|
||||
|
||||
// Template string substitutions in generator functions can yield.
|
||||
function* g() {
|
||||
var res = args`${yield 1} ${yield 2}`;
|
||||
return res[0] + res[1] + "";
|
||||
}
|
||||
|
||||
var it = g();
|
||||
var next = it.next();
|
||||
assertEq(next.done, false);
|
||||
assertEq(next.value, 1);
|
||||
next = it.next("hello");
|
||||
assertEq(next.done, false);
|
||||
assertEq(next.value, 2);
|
||||
next = it.next("world");
|
||||
assertEq(next.done, true);
|
||||
assertEq(next.value, "helloworld");
|
||||
|
||||
// undefined
|
||||
assertEq(args`${void 0}`[0] + "", "undefined");
|
||||
assertEq(args`${Object.doesNotHaveThisProperty}`[0] + "", "undefined");
|
||||
|
||||
var callSiteObj = [];
|
||||
callSiteObj[0] = cooked`aa${4}bb`;
|
||||
for (var i = 1; i < 3; i++)
|
||||
callSiteObj[i] = cooked`aa${4}bb`;
|
||||
|
||||
// Same call site object behavior
|
||||
assertEq(callSiteObj[1], callSiteObj[2]);
|
||||
assertEq(callSiteObj[0] !== callSiteObj[1], true);
|
||||
assertEq("raw" in callSiteObj[0], true);
|
||||
|
||||
// Array length
|
||||
assertEq(callSiteObj[0].raw.length, 2);
|
||||
assertEq(callSiteObj[0].length, 2);
|
||||
|
||||
// Frozen objects
|
||||
assertEq(Object.isFrozen(callSiteObj[0]), true);
|
||||
assertEq(Object.isFrozen(callSiteObj[0].raw), true);
|
||||
|
||||
// Raw not enumerable
|
||||
assertEq(callSiteObj[0].propertyIsEnumerable(callSiteObj[0].raw), false);
|
||||
|
||||
// Allow call syntax
|
||||
check(new ((cs, sub) => function(){ return sub }) `${[1, 2, 3]}`, [1,2,3]);
|
||||
|
||||
var a = [];
|
||||
function test() {
|
||||
var x = callSite => callSite;
|
||||
for (var i = 0; i < 2; i++)
|
||||
a[i] = eval("x``");
|
||||
}
|
||||
test();
|
||||
assertEq(a[0] !== a[1], true);
|
||||
|
||||
// Test that |obj.method`template`| works
|
||||
var newObj = {
|
||||
methodRetThis : function () {
|
||||
return this;
|
||||
},
|
||||
methodRetCooked : function (a) {
|
||||
return a;
|
||||
},
|
||||
methodRetRaw : function (a) {
|
||||
return a.raw;
|
||||
},
|
||||
methodRetArgs : function (a, ...args) {
|
||||
return args;
|
||||
}
|
||||
}
|
||||
|
||||
assertEq(newObj.methodRetThis`abc${4}`, newObj);
|
||||
check(newObj.methodRetCooked`abc${4}\r`, ["abc","\r"]);
|
||||
check(eval("newObj.methodRetCooked`abc${4}\r`"), ["abc","\n"]);
|
||||
check(newObj.methodRetRaw`abc${4}\r`, ["abc","\\r"]);
|
||||
check(eval("newObj.methodRetRaw`abc${4}\r`"), ["abc","\n"]);
|
||||
check(eval("newObj.methodRetArgs`abc${4}${5}\r${6}`"), [4,5,6]);
|
||||
|
||||
// Chained calls
|
||||
function func(a) {
|
||||
if (a[0] === "hey") {
|
||||
return function(a) {
|
||||
if (a[0] === "there") {
|
||||
return function(a) {
|
||||
if (a[0] === "mine")
|
||||
return "was mine";
|
||||
else
|
||||
return "was not mine";
|
||||
}
|
||||
} else {
|
||||
return function(a) {
|
||||
return "was not there";
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return function(a) {
|
||||
return function(a) {
|
||||
return "was not hey";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertEq(func`hey``there``mine`, "was mine");
|
||||
assertEq(func`hey``there``amine`, "was not mine");
|
||||
assertEq(func`hey``tshere``amine`, "was not there");
|
||||
assertEq(func`heys``there``mine`, "was not hey");
|
||||
|
||||
*/
|
||||
/*End func*/}
|
||||
|
||||
var str = testCaseFn.toString().replace("/*","").replace("*/","");
|
||||
str = str.replace("function testCaseFn() {\n", "").replace("/*End func*/}","");
|
||||
var hasTemplateStrings = false;
|
||||
try { eval("``"); hasTemplateStrings = true; } catch (exc) { }
|
||||
if (hasTemplateStrings)
|
||||
eval(str);
|
||||
|
||||
reportCompare(0, 0, "ok");
|
@ -19,7 +19,7 @@ function syntaxError (script) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw "Expected syntax error: " + script;
|
||||
throw new Error('Expected syntax error: ' + script);
|
||||
}
|
||||
|
||||
// TEST BEGIN
|
@ -93,6 +93,10 @@ function callExpr(callee, args) Pattern({ type: "CallExpression", callee: callee
|
||||
function arrExpr(elts) Pattern({ type: "ArrayExpression", elements: elts })
|
||||
function objExpr(elts) Pattern({ type: "ObjectExpression", properties: elts })
|
||||
function templateLit(elts) Pattern({ type: "TemplateLiteral", elements: elts })
|
||||
function taggedTemplate(tagPart, templatePart) Pattern({ type: "TaggedTemplate", callee: tagPart,
|
||||
arguments : templatePart })
|
||||
function template(raw, cooked, ...args) Pattern([{ type: "CallSiteObject", raw: raw, cooked:
|
||||
cooked}, ...args])
|
||||
function compExpr(body, blocks, filter) Pattern({ type: "ComprehensionExpression", body: body, blocks: blocks, filter: filter })
|
||||
function genExpr(body, blocks, filter) Pattern({ type: "GeneratorExpression", body: body, blocks: blocks, filter: filter })
|
||||
function graphExpr(idx, body) Pattern({ type: "GraphExpression", index: idx, expression: body })
|
||||
@ -410,7 +414,20 @@ if (hasTemplateStrings == true) {
|
||||
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("`hey${`there${\"how\"}`}mine`", templateLit([lit("hey"), templateLit([lit("there"), lit("how"), lit("")]), lit("mine")]));
|
||||
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"])));
|
||||
assertExpr("func`hey${\"4\"}there`", taggedTemplate(ident("func"),
|
||||
template(["hey", "there"], ["hey", "there"], lit("4"))));
|
||||
assertExpr("func`hey${\"4\"}there${5}`", taggedTemplate(ident("func"),
|
||||
template(["hey", "there", ""], ["hey", "there", ""],
|
||||
lit("4"), lit(5))));
|
||||
assertExpr("func`hey\r\n`", taggedTemplate(ident("func"), template(["hey\n"], ["hey\n"])));
|
||||
assertExpr("func`hey${4}``${5}there``mine`",
|
||||
taggedTemplate(taggedTemplate(taggedTemplate(
|
||||
ident("func"), template(["hey", ""], ["hey", ""], lit(4))),
|
||||
template(["", "there"], ["", "there"], lit(5))),
|
||||
template(["mine"], ["mine"])));
|
||||
}
|
||||
assertStringExpr("\"hey there\"", literal("hey there"));
|
||||
|
||||
|
@ -203,6 +203,7 @@
|
||||
macro(y, y, "y") \
|
||||
macro(yield, yield, "yield") \
|
||||
macro(z, z, "z") \
|
||||
macro(raw, raw, "raw") \
|
||||
/* Type names must be contiguous and ordered; see js::TypeName. */ \
|
||||
macro(undefined, undefined, "undefined") \
|
||||
macro(object, object, "object") \
|
||||
|
@ -521,6 +521,28 @@ InitArrayElemOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, uint32_t
|
||||
return true;
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
ProcessCallSiteObjOperation(JSContext *cx, RootedObject &cso, RootedObject &raw,
|
||||
RootedValue &rawValue)
|
||||
{
|
||||
bool extensible;
|
||||
if (!JSObject::isExtensible(cx, cso, &extensible))
|
||||
return false;
|
||||
if (extensible) {
|
||||
JSAtom *name = cx->names().raw;
|
||||
if (!JSObject::defineProperty(cx, cso, name->asPropertyName(), rawValue,
|
||||
nullptr, nullptr, 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!JSObject::freeze(cx, raw))
|
||||
return false;
|
||||
if (!JSObject::freeze(cx, cso))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#define RELATIONAL_OP(OP) \
|
||||
JS_BEGIN_MACRO \
|
||||
/* Optimize for two int-tagged operands (typical loop control). */ \
|
||||
|
@ -1589,7 +1589,6 @@ CASE(JSOP_UNUSED51)
|
||||
CASE(JSOP_UNUSED52)
|
||||
CASE(JSOP_UNUSED57)
|
||||
CASE(JSOP_UNUSED83)
|
||||
CASE(JSOP_UNUSED101)
|
||||
CASE(JSOP_UNUSED102)
|
||||
CASE(JSOP_UNUSED103)
|
||||
CASE(JSOP_UNUSED104)
|
||||
@ -2726,6 +2725,23 @@ CASE(JSOP_OBJECT)
|
||||
}
|
||||
END_CASE(JSOP_OBJECT)
|
||||
|
||||
CASE(JSOP_CALLSITEOBJ)
|
||||
{
|
||||
|
||||
RootedObject &cso = rootObject0;
|
||||
cso = script->getObject(REGS.pc);
|
||||
RootedObject &raw = rootObject1;
|
||||
raw = script->getObject(GET_UINT32_INDEX(REGS.pc) + 1);
|
||||
RootedValue &rawValue = rootValue0;
|
||||
rawValue.setObject(*raw);
|
||||
|
||||
if (!ProcessCallSiteObjOperation(cx, cso, raw, rawValue))
|
||||
goto error;
|
||||
|
||||
PUSH_OBJECT(*cso);
|
||||
}
|
||||
END_CASE(JSOP_CALLSITEOBJ)
|
||||
|
||||
CASE(JSOP_REGEXP)
|
||||
{
|
||||
/*
|
||||
|
@ -883,7 +883,17 @@
|
||||
*/ \
|
||||
macro(JSOP_INITELEM_SETTER, 100, "initelem_setter", NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_SET|JOF_DETECTING) \
|
||||
\
|
||||
macro(JSOP_UNUSED101, 101, "unused101", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
/*
|
||||
* Pushes the call site object specified by objectIndex onto the stack. Defines the raw
|
||||
* property specified by objectIndex + 1 on the call site object and freezes both the call site
|
||||
* object as well as its raw property.
|
||||
* Category: Literals
|
||||
* Type: Object
|
||||
* Operands: uint32_t objectIndex
|
||||
* Stack: => obj
|
||||
*/ \
|
||||
macro(JSOP_CALLSITEOBJ, 101, "callsiteobj", NULL, 5, 0, 1, JOF_OBJECT) \
|
||||
\
|
||||
macro(JSOP_UNUSED102, 102, "unused102", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
macro(JSOP_UNUSED103, 103, "unused103", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
macro(JSOP_UNUSED104, 104, "unused104", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
|
@ -28,7 +28,7 @@ namespace js {
|
||||
*
|
||||
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
|
||||
*/
|
||||
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 178);
|
||||
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 179);
|
||||
|
||||
class XDRBuffer {
|
||||
public:
|
||||
|
Loading…
Reference in New Issue
Block a user