зеркало из https://github.com/mozilla/gecko-dev.git
Bug 666399 - New Harmony syntax for generators. r=Waldo
Add a GeneratorKind enumeration, and use it in the parser and runtime to indicate whether a function is a non-generator, a legacy generator, or a star generator. Always parse "yield" as TOK_YIELD, regardless of the version. Add TokenStream::currentName() to retrieve the current token's name, which works for TOK_NAME or TOK_YIELD. The parser should check the validity of "yield" as a name, if needed, using checkYieldNameValidity(). Parse "function*" as the StarGenerator GeneratorKind, and allow star generators to be lazily parsed. Separate parsing of return statements from yield expressions.
This commit is contained in:
Родитель
085d65c97f
Коммит
cb60a21f87
|
@ -69,7 +69,7 @@ CheckArgumentsWithinEval(JSContext *cx, Parser<FullParseHandler> &parser, Handle
|
|||
}
|
||||
|
||||
// It's an error to use |arguments| in a legacy generator expression.
|
||||
if (script->isGeneratorExp && script->isLegacyGenerator) {
|
||||
if (script->isGeneratorExp && script->isLegacyGenerator()) {
|
||||
parser.report(ParseError, false, NULL, JSMSG_BAD_GENEXP_BODY, js_arguments_str);
|
||||
return false;
|
||||
}
|
||||
|
@ -294,7 +294,8 @@ frontend::CompileScript(ExclusiveContext *cx, LifoAlloc *alloc, HandleObject sco
|
|||
*/
|
||||
JSFunction *fun = evalCaller->functionOrCallerFunction();
|
||||
Directives directives(/* strict = */ fun->strict());
|
||||
ObjectBox *funbox = parser.newFunctionBox(/* fn = */ NULL, fun, pc.addr(), directives);
|
||||
ObjectBox *funbox = parser.newFunctionBox(/* fn = */ NULL, fun, pc.addr(),
|
||||
directives, fun->generatorKind());
|
||||
if (!funbox)
|
||||
return NULL;
|
||||
bce.objectList.add(funbox);
|
||||
|
@ -414,7 +415,9 @@ frontend::CompileLazyFunction(JSContext *cx, LazyScript *lazy, const jschar *cha
|
|||
uint32_t staticLevel = lazy->staticLevel(cx);
|
||||
|
||||
Rooted<JSFunction*> fun(cx, lazy->function());
|
||||
ParseNode *pn = parser.standaloneLazyFunction(fun, staticLevel, lazy->strict());
|
||||
JS_ASSERT(!lazy->isLegacyGenerator());
|
||||
ParseNode *pn = parser.standaloneLazyFunction(fun, staticLevel, lazy->strict(),
|
||||
lazy->generatorKind());
|
||||
if (!pn)
|
||||
return false;
|
||||
|
||||
|
@ -518,7 +521,7 @@ frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileO
|
|||
ParseNode *fn;
|
||||
while (true) {
|
||||
Directives newDirectives = directives;
|
||||
fn = parser.standaloneFunctionBody(fun, formals, directives, &newDirectives);
|
||||
fn = parser.standaloneFunctionBody(fun, formals, NotGenerator, directives, &newDirectives);
|
||||
if (fn)
|
||||
break;
|
||||
|
||||
|
|
|
@ -384,9 +384,9 @@ Parser<FullParseHandler>::cloneParseTree(ParseNode *opn)
|
|||
if (pn->getKind() == PNK_MODULE) {
|
||||
MOZ_ASSUME_UNREACHABLE("module nodes cannot be cloned");
|
||||
}
|
||||
NULLCHECK(pn->pn_funbox =
|
||||
newFunctionBox(pn, opn->pn_funbox->function(), pc,
|
||||
Directives(/* strict = */ opn->pn_funbox->strict)));
|
||||
NULLCHECK(pn->pn_funbox = newFunctionBox(pn, opn->pn_funbox->function(), pc,
|
||||
Directives(/* strict = */ opn->pn_funbox->strict),
|
||||
opn->pn_funbox->generatorKind()));
|
||||
NULLCHECK(pn->pn_body = cloneParseTree(opn->pn_body));
|
||||
pn->pn_cookie = opn->pn_cookie;
|
||||
pn->pn_dflags = opn->pn_dflags;
|
||||
|
|
|
@ -56,7 +56,6 @@ namespace frontend {
|
|||
typedef Rooted<StaticBlockObject*> RootedStaticBlockObject;
|
||||
typedef Handle<StaticBlockObject*> HandleStaticBlockObject;
|
||||
|
||||
typedef MutableHandle<PropertyName*> MutableHandlePropertyName;
|
||||
|
||||
/*
|
||||
* Insist that the next token be of type tt, or report errno and return null.
|
||||
|
@ -466,13 +465,14 @@ Parser<ParseHandler>::newObjectBox(JSObject *obj)
|
|||
template <typename ParseHandler>
|
||||
FunctionBox::FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunction *fun,
|
||||
ParseContext<ParseHandler> *outerpc, Directives directives,
|
||||
bool extraWarnings)
|
||||
bool extraWarnings, GeneratorKind generatorKind)
|
||||
: ObjectBox(fun, traceListHead),
|
||||
SharedContext(cx, directives, extraWarnings),
|
||||
bindings(),
|
||||
bufStart(0),
|
||||
bufEnd(0),
|
||||
ndefaults(0),
|
||||
generatorKindBits_(GeneratorKindAsBits(generatorKind)),
|
||||
inWith(false), // initialized below
|
||||
inGenexpLambda(false),
|
||||
hasDestructuringArgs(false),
|
||||
|
@ -533,7 +533,7 @@ FunctionBox::FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunct
|
|||
template <typename ParseHandler>
|
||||
FunctionBox *
|
||||
Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction *fun, ParseContext<ParseHandler> *outerpc,
|
||||
Directives inheritedDirectives)
|
||||
Directives inheritedDirectives, GeneratorKind generatorKind)
|
||||
{
|
||||
JS_ASSERT(fun && !IsPoisonedPtr(fun));
|
||||
|
||||
|
@ -546,7 +546,8 @@ Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction *fun, ParseContext<Pars
|
|||
*/
|
||||
FunctionBox *funbox =
|
||||
alloc.new_<FunctionBox>(context, traceListHead, fun, outerpc,
|
||||
inheritedDirectives, options().extraWarningsOption);
|
||||
inheritedDirectives, options().extraWarningsOption,
|
||||
generatorKind);
|
||||
if (!funbox) {
|
||||
js_ReportOutOfMemory(context);
|
||||
return NULL;
|
||||
|
@ -863,6 +864,7 @@ Parser<ParseHandler>::checkStrictBinding(PropertyName *name, Node pn)
|
|||
template <>
|
||||
ParseNode *
|
||||
Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals,
|
||||
GeneratorKind generatorKind,
|
||||
Directives inheritedDirectives,
|
||||
Directives *newDirectives)
|
||||
{
|
||||
|
@ -877,7 +879,8 @@ Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun, const AutoN
|
|||
argsbody->makeEmpty();
|
||||
fn->pn_body = argsbody;
|
||||
|
||||
FunctionBox *funbox = newFunctionBox(fn, fun, /* outerpc = */ NULL, inheritedDirectives);
|
||||
FunctionBox *funbox = newFunctionBox(fn, fun, /* outerpc = */ NULL, inheritedDirectives,
|
||||
generatorKind);
|
||||
if (!funbox)
|
||||
return null();
|
||||
handler.setFunctionBox(fn, funbox);
|
||||
|
@ -1062,7 +1065,9 @@ Parser<ParseHandler>::functionBody(FunctionSyntaxKind kind, FunctionBodyType typ
|
|||
JS_ASSERT(pc->sc->isFunctionBox());
|
||||
JS_ASSERT(!pc->funHasReturnExpr && !pc->funHasReturnVoid);
|
||||
|
||||
#ifdef DEBUG
|
||||
uint32_t startYieldOffset = pc->lastYieldOffset;
|
||||
#endif
|
||||
|
||||
Node pn;
|
||||
if (type == StatementListBody) {
|
||||
|
@ -1082,8 +1087,14 @@ Parser<ParseHandler>::functionBody(FunctionSyntaxKind kind, FunctionBodyType typ
|
|||
return null();
|
||||
}
|
||||
|
||||
if (pc->lastYieldOffset != startYieldOffset) {
|
||||
JS_ASSERT(pc->isLegacyGenerator());
|
||||
switch (pc->generatorKind()) {
|
||||
case NotGenerator:
|
||||
JS_ASSERT(pc->lastYieldOffset == startYieldOffset);
|
||||
break;
|
||||
|
||||
case LegacyGenerator:
|
||||
// FIXME: Catch these errors eagerly, in yieldExpression().
|
||||
JS_ASSERT(pc->lastYieldOffset != startYieldOffset);
|
||||
if (kind == Arrow) {
|
||||
reportWithOffset(ParseError, false, pc->lastYieldOffset,
|
||||
JSMSG_YIELD_IN_ARROW, js_yield_str);
|
||||
|
@ -1095,9 +1106,12 @@ Parser<ParseHandler>::functionBody(FunctionSyntaxKind kind, FunctionBodyType typ
|
|||
JSMSG_BAD_ANON_GENERATOR_RETURN);
|
||||
return null();
|
||||
}
|
||||
pc->sc->asFunctionBox()->setIsLegacyGenerator();
|
||||
} else {
|
||||
JS_ASSERT(!pc->isLegacyGenerator());
|
||||
break;
|
||||
|
||||
case StarGenerator:
|
||||
JS_ASSERT(kind != Arrow);
|
||||
JS_ASSERT(type == StatementListBody);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check for falling off the end of a function that returns a value. */
|
||||
|
@ -1627,6 +1641,11 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
|
|||
}
|
||||
#endif /* JS_HAS_DESTRUCTURING */
|
||||
|
||||
case TOK_YIELD:
|
||||
if (!checkYieldNameValidity(JSMSG_MISSING_FORMAL))
|
||||
return false;
|
||||
goto TOK_NAME;
|
||||
|
||||
case TOK_TRIPLEDOT:
|
||||
{
|
||||
hasRest = true;
|
||||
|
@ -1636,15 +1655,16 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
|
|||
report(ParseError, false, null(), JSMSG_NO_REST_NAME);
|
||||
return false;
|
||||
}
|
||||
/* Fall through */
|
||||
goto TOK_NAME;
|
||||
}
|
||||
|
||||
TOK_NAME:
|
||||
case TOK_NAME:
|
||||
{
|
||||
if (parenFreeArrow)
|
||||
funbox->setStart(tokenStream);
|
||||
|
||||
RootedPropertyName name(context, tokenStream.currentToken().name());
|
||||
RootedPropertyName name(context, tokenStream.currentName());
|
||||
bool disallowDuplicateArgs = funbox->hasDestructuringArgs || hasDefaults;
|
||||
if (!defineArg(funcpn, name, disallowDuplicateArgs, &duplicatedArg))
|
||||
return false;
|
||||
|
@ -1695,6 +1715,20 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
|
|||
return true;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::checkFunctionName(HandlePropertyName funName)
|
||||
{
|
||||
if (pc->isStarGenerator() && funName == context->names().yield) {
|
||||
// The name of a named function expression is specified to be bound in
|
||||
// the outer context as if via "let". In an ES6 generator, "yield" is
|
||||
// not a valid name for a let-bound variable.
|
||||
report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
|
@ -1707,6 +1741,9 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
|||
/* Function statements add a binding to the enclosing scope. */
|
||||
bool bodyLevel = pc->atBodyLevel();
|
||||
|
||||
if (!checkFunctionName(funName))
|
||||
return false;
|
||||
|
||||
if (kind == Statement) {
|
||||
/*
|
||||
* Handle redeclaration and optimize cases where we can statically bind the
|
||||
|
@ -1820,7 +1857,9 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
|||
// so we can skip over them after accounting for their free variables.
|
||||
if (LazyScript *lazyOuter = handler.lazyOuterFunction()) {
|
||||
JSFunction *fun = handler.nextLazyInnerFunction();
|
||||
FunctionBox *funbox = newFunctionBox(pn, fun, pc, Directives(/* strict = */ false));
|
||||
JS_ASSERT(!fun->isLegacyGenerator());
|
||||
FunctionBox *funbox = newFunctionBox(pn, fun, pc, Directives(/* strict = */ false),
|
||||
fun->generatorKind());
|
||||
if (!funbox)
|
||||
return false;
|
||||
|
||||
|
@ -1896,6 +1935,9 @@ Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
|||
/* Function statements add a binding to the enclosing scope. */
|
||||
bool bodyLevel = pc->atBodyLevel();
|
||||
|
||||
if (!checkFunctionName(funName))
|
||||
return false;
|
||||
|
||||
if (kind == Statement) {
|
||||
/*
|
||||
* Handle redeclaration and optimize cases where we can statically bind the
|
||||
|
@ -1934,7 +1976,8 @@ Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
|||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream::Position &start,
|
||||
FunctionType type, FunctionSyntaxKind kind)
|
||||
FunctionType type, FunctionSyntaxKind kind,
|
||||
GeneratorKind generatorKind)
|
||||
{
|
||||
JS_ASSERT_IF(kind == Statement, funName);
|
||||
|
||||
|
@ -1962,7 +2005,7 @@ Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream:
|
|||
Directives newDirectives = directives;
|
||||
|
||||
while (true) {
|
||||
if (functionArgsAndBody(pn, fun, type, kind, directives, &newDirectives))
|
||||
if (functionArgsAndBody(pn, fun, type, kind, generatorKind, directives, &newDirectives))
|
||||
break;
|
||||
if (tokenStream.hadError() || directives == newDirectives)
|
||||
return null();
|
||||
|
@ -2066,6 +2109,7 @@ Parser<SyntaxParseHandler>::finishFunctionDefinition(Node pn, FunctionBox *funbo
|
|||
|
||||
if (pc->sc->strict)
|
||||
lazy->setStrict();
|
||||
lazy->setGeneratorKind(funbox->generatorKind());
|
||||
if (funbox->usesArguments && funbox->usesApply)
|
||||
lazy->setUsesArgumentsAndApply();
|
||||
PropagateTransitiveParseFlags(funbox, lazy);
|
||||
|
@ -2078,13 +2122,14 @@ template <>
|
|||
bool
|
||||
Parser<FullParseHandler>::functionArgsAndBody(ParseNode *pn, HandleFunction fun,
|
||||
FunctionType type, FunctionSyntaxKind kind,
|
||||
GeneratorKind generatorKind,
|
||||
Directives inheritedDirectives,
|
||||
Directives *newDirectives)
|
||||
{
|
||||
ParseContext<FullParseHandler> *outerpc = pc;
|
||||
|
||||
// Create box for fun->object early to protect against last-ditch GC.
|
||||
FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives);
|
||||
FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives, generatorKind);
|
||||
if (!funbox)
|
||||
return false;
|
||||
|
||||
|
@ -2160,13 +2205,14 @@ template <>
|
|||
bool
|
||||
Parser<SyntaxParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun,
|
||||
FunctionType type, FunctionSyntaxKind kind,
|
||||
GeneratorKind generatorKind,
|
||||
Directives inheritedDirectives,
|
||||
Directives *newDirectives)
|
||||
{
|
||||
ParseContext<SyntaxParseHandler> *outerpc = pc;
|
||||
|
||||
// Create box for fun->object early to protect against last-ditch GC.
|
||||
FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives);
|
||||
FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives, generatorKind);
|
||||
if (!funbox)
|
||||
return false;
|
||||
|
||||
|
@ -2192,14 +2238,15 @@ Parser<SyntaxParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun,
|
|||
template <>
|
||||
ParseNode *
|
||||
Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, unsigned staticLevel,
|
||||
bool strict)
|
||||
bool strict, GeneratorKind generatorKind)
|
||||
{
|
||||
Node pn = handler.newFunctionDefinition();
|
||||
if (!pn)
|
||||
return null();
|
||||
|
||||
Directives directives(/* strict = */ strict);
|
||||
FunctionBox *funbox = newFunctionBox(pn, fun, /* outerpc = */ NULL, directives);
|
||||
FunctionBox *funbox = newFunctionBox(pn, fun, /* outerpc = */ NULL, directives,
|
||||
generatorKind);
|
||||
if (!funbox)
|
||||
return null();
|
||||
|
||||
|
@ -2273,6 +2320,10 @@ Parser<ParseHandler>::functionArgsAndBodyGeneric(Node pn, HandleFunction fun, Fu
|
|||
// Parse the function body.
|
||||
FunctionBodyType bodyType = StatementListBody;
|
||||
if (tokenStream.getToken(TokenStream::Operand) != TOK_LC) {
|
||||
if (funbox->isStarGenerator()) {
|
||||
report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY);
|
||||
return false;
|
||||
}
|
||||
tokenStream.ungetToken();
|
||||
bodyType = ExpressionBody;
|
||||
fun->setIsExprClosure();
|
||||
|
@ -2310,7 +2361,7 @@ template <>
|
|||
ParseNode *
|
||||
Parser<FullParseHandler>::moduleDecl()
|
||||
{
|
||||
JS_ASSERT(tokenStream.currentToken().name() == context->names().module);
|
||||
JS_ASSERT(tokenStream.currentName() == context->names().module);
|
||||
if (!((pc->sc->isGlobalSharedContext() || pc->sc->isModuleBox()) && pc->atBodyLevel()))
|
||||
{
|
||||
report(ParseError, false, NULL, JSMSG_MODULE_STATEMENT);
|
||||
|
@ -2352,18 +2403,44 @@ Parser<SyntaxParseHandler>::moduleDecl()
|
|||
return SyntaxParseHandler::NodeFailure;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::checkYieldNameValidity(unsigned errorNumber)
|
||||
{
|
||||
// In star generators and in JS >= 1.7, yield is a keyword.
|
||||
if (pc->isStarGenerator() || versionNumber() >= JSVERSION_1_7) {
|
||||
report(ParseError, false, null(), errorNumber);
|
||||
return false;
|
||||
}
|
||||
// Otherwise in strict mode, yield is a future reserved word.
|
||||
if (pc->sc->strict) {
|
||||
report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::functionStmt()
|
||||
{
|
||||
JS_ASSERT(tokenStream.currentToken().type == TOK_FUNCTION);
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
|
||||
|
||||
TokenStream::Position start(keepAtoms);
|
||||
tokenStream.tell(&start);
|
||||
|
||||
RootedPropertyName name(context);
|
||||
if (tokenStream.getToken(TokenStream::KeywordIsName) == TOK_NAME) {
|
||||
name = tokenStream.currentToken().name();
|
||||
GeneratorKind generatorKind = NotGenerator;
|
||||
TokenKind tt = tokenStream.getToken(TokenStream::KeywordIsName);
|
||||
|
||||
if (tt == TOK_MUL) {
|
||||
tokenStream.tell(&start);
|
||||
tt = tokenStream.getToken(TokenStream::KeywordIsName);
|
||||
generatorKind = StarGenerator;
|
||||
}
|
||||
|
||||
if (tt == TOK_NAME) {
|
||||
name = tokenStream.currentName();
|
||||
} else {
|
||||
/* Unnamed function expressions are forbidden in statement context. */
|
||||
report(ParseError, false, null(), JSMSG_UNNAMED_FUNCTION_STMT);
|
||||
|
@ -2375,22 +2452,34 @@ Parser<ParseHandler>::functionStmt()
|
|||
!report(ParseStrictError, pc->sc->strict, null(), JSMSG_STRICT_FUNCTION_STATEMENT))
|
||||
return null();
|
||||
|
||||
return functionDef(name, start, Normal, Statement);
|
||||
return functionDef(name, start, Normal, Statement, generatorKind);
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::functionExpr()
|
||||
{
|
||||
RootedPropertyName name(context);
|
||||
JS_ASSERT(tokenStream.currentToken().type == TOK_FUNCTION);
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
|
||||
|
||||
TokenStream::Position start(keepAtoms);
|
||||
tokenStream.tell(&start);
|
||||
if (tokenStream.getToken(TokenStream::KeywordIsName) == TOK_NAME)
|
||||
name = tokenStream.currentToken().name();
|
||||
|
||||
RootedPropertyName name(context);
|
||||
GeneratorKind generatorKind = NotGenerator;
|
||||
TokenKind tt = tokenStream.getToken(TokenStream::KeywordIsName);
|
||||
|
||||
if (tt == TOK_MUL) {
|
||||
tokenStream.tell(&start);
|
||||
tt = tokenStream.getToken(TokenStream::KeywordIsName);
|
||||
generatorKind = StarGenerator;
|
||||
}
|
||||
|
||||
if (tt == TOK_NAME)
|
||||
name = tokenStream.currentName();
|
||||
else
|
||||
tokenStream.ungetToken();
|
||||
return functionDef(name, start, Normal, Expression);
|
||||
|
||||
return functionDef(name, start, Normal, Expression, generatorKind);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2524,7 +2613,7 @@ Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool *cont)
|
|||
}
|
||||
}
|
||||
} else if (directive == context->names().useAsm) {
|
||||
if (pc->sc->isFunctionBox())
|
||||
if (pc->sc->isFunctionBox() && !pc->isGenerator())
|
||||
return asmJS(list);
|
||||
return report(ParseWarning, false, pn, JSMSG_USE_ASM_DIRECTIVE_FAIL);
|
||||
}
|
||||
|
@ -2606,15 +2695,21 @@ Parser<ParseHandler>::condition()
|
|||
return pn;
|
||||
}
|
||||
|
||||
static bool
|
||||
MatchLabel(TokenStream &ts, MutableHandlePropertyName label)
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::matchLabel(MutableHandle<PropertyName*> label)
|
||||
{
|
||||
TokenKind tt = ts.peekTokenSameLine(TokenStream::Operand);
|
||||
TokenKind tt = tokenStream.peekTokenSameLine(TokenStream::Operand);
|
||||
if (tt == TOK_ERROR)
|
||||
return false;
|
||||
if (tt == TOK_NAME) {
|
||||
(void) ts.getToken();
|
||||
label.set(ts.currentToken().name());
|
||||
tokenStream.consumeKnownToken(TOK_NAME);
|
||||
label.set(tokenStream.currentName());
|
||||
} else if (tt == TOK_YIELD) {
|
||||
tokenStream.consumeKnownToken(TOK_YIELD);
|
||||
if (!checkYieldNameValidity())
|
||||
return false;
|
||||
label.set(tokenStream.currentName());
|
||||
} else {
|
||||
label.set(NULL);
|
||||
}
|
||||
|
@ -3240,7 +3335,7 @@ template <typename ParseHandler>
|
|||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::letBlock(LetContext letContext)
|
||||
{
|
||||
JS_ASSERT(tokenStream.currentToken().type == TOK_LET);
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LET));
|
||||
|
||||
RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context));
|
||||
if (!blockObj)
|
||||
|
@ -3330,7 +3425,7 @@ template <typename ParseHandler>
|
|||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::blockStatement()
|
||||
{
|
||||
JS_ASSERT(tokenStream.currentToken().type == TOK_LC);
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
|
||||
|
||||
StmtInfoPC stmtInfo(context);
|
||||
if (!PushBlocklikeStatement(&stmtInfo, STMT_BLOCK, pc))
|
||||
|
@ -3372,7 +3467,6 @@ Parser<ParseHandler>::newBindingNode(PropertyName *name, bool functionScope, Var
|
|||
}
|
||||
|
||||
/* Make a new node for this declarator name (or destructuring pattern). */
|
||||
JS_ASSERT(tokenStream.currentToken().type == TOK_NAME);
|
||||
return newName(name);
|
||||
}
|
||||
|
||||
|
@ -3461,12 +3555,17 @@ Parser<ParseHandler>::variables(ParseNodeKind kind, bool *psimple,
|
|||
#endif /* JS_HAS_DESTRUCTURING */
|
||||
|
||||
if (tt != TOK_NAME) {
|
||||
if (tt != TOK_ERROR)
|
||||
report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
|
||||
return null();
|
||||
if (tt == TOK_YIELD) {
|
||||
if (!checkYieldNameValidity(JSMSG_NO_VARIABLE_NAME))
|
||||
return null();
|
||||
} else {
|
||||
if (tt != TOK_ERROR)
|
||||
report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
|
||||
return null();
|
||||
}
|
||||
}
|
||||
|
||||
RootedPropertyName name(context, tokenStream.currentToken().name());
|
||||
RootedPropertyName name(context, tokenStream.currentName());
|
||||
pn2 = newBindingNode(name, kind == PNK_VAR || kind == PNK_CONST, varContext);
|
||||
if (!pn2)
|
||||
return null();
|
||||
|
@ -4123,9 +4222,14 @@ Parser<SyntaxParseHandler>::forStatement()
|
|||
PushStatementPC(pc, &forStmt, STMT_FOR_LOOP);
|
||||
|
||||
/* Don't parse 'for each' loops. */
|
||||
if (allowsForEachIn() && tokenStream.peekToken() == TOK_NAME) {
|
||||
JS_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||
return null();
|
||||
if (allowsForEachIn()) {
|
||||
TokenKind tt = tokenStream.peekToken();
|
||||
// Not all "yield" tokens are names, but the ones that aren't names are
|
||||
// invalid in this context anyway.
|
||||
if (tt == TOK_NAME || tt == TOK_YIELD) {
|
||||
JS_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||
return null();
|
||||
}
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
|
||||
|
@ -4327,7 +4431,7 @@ Parser<ParseHandler>::continueStatement()
|
|||
uint32_t begin = pos().begin;
|
||||
|
||||
RootedPropertyName label(context);
|
||||
if (!MatchLabel(tokenStream, &label))
|
||||
if (!matchLabel(&label))
|
||||
return null();
|
||||
|
||||
StmtInfoPC *stmt = pc->topStmt;
|
||||
|
@ -4374,7 +4478,7 @@ Parser<ParseHandler>::breakStatement()
|
|||
uint32_t begin = pos().begin;
|
||||
|
||||
RootedPropertyName label(context);
|
||||
if (!MatchLabel(tokenStream, &label))
|
||||
if (!matchLabel(&label))
|
||||
return null();
|
||||
StmtInfoPC *stmt = pc->topStmt;
|
||||
if (label) {
|
||||
|
@ -4405,98 +4509,45 @@ Parser<ParseHandler>::breakStatement()
|
|||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::returnStatementOrYieldExpression()
|
||||
Parser<ParseHandler>::returnStatement()
|
||||
{
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_RETURN) ||
|
||||
tokenStream.isCurrentTokenType(TOK_YIELD));
|
||||
bool isYield = tokenStream.isCurrentTokenType(TOK_YIELD);
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_RETURN));
|
||||
uint32_t begin = pos().begin;
|
||||
|
||||
if (!pc->sc->isFunctionBox()) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD,
|
||||
isYield ? js_yield_str : js_return_str);
|
||||
report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
|
||||
return null();
|
||||
}
|
||||
|
||||
// Legacy generators are identified by the presence of "yield" in their
|
||||
// bodies. We only see "yield" as TOK_YIELD in JS 1.7+.
|
||||
if (isYield) {
|
||||
JS_ASSERT(tokenStream.versionNumber() >= JSVERSION_1_7);
|
||||
if (!abortIfSyntaxParser())
|
||||
return null();
|
||||
|
||||
if (pc->isLegacyGenerator()) {
|
||||
// We are in a legacy generator: a function that has already seen a
|
||||
// yield.
|
||||
JS_ASSERT(pc->sc->isFunctionBox());
|
||||
JS_ASSERT(pc->lastYieldOffset != ParseContext<ParseHandler>::NoYieldOffset);
|
||||
} else {
|
||||
// We are in a code that has not seen a yield, and in JS 1.8 so
|
||||
// "yield" parsed as TOK_YIELD. Try to transition to being a legacy
|
||||
// generator.
|
||||
JS_ASSERT(tokenStream.currentToken().type == TOK_YIELD);
|
||||
JS_ASSERT(pc->lastYieldOffset == ParseContext<ParseHandler>::NoYieldOffset);
|
||||
|
||||
if (!pc->sc->isFunctionBox()) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_yield_str);
|
||||
return null();
|
||||
}
|
||||
|
||||
pc->generatorParseMode = ParseContext<ParseHandler>::LegacyGenerator;
|
||||
}
|
||||
|
||||
pc->lastYieldOffset = begin;
|
||||
}
|
||||
|
||||
// Parse an optional operand.
|
||||
//
|
||||
// Checking whether yield has an operand is especially wonky since
|
||||
// there is not a mandatory semicolon.
|
||||
//
|
||||
// ES6 does not permit yield without an operand. We will have to sunset
|
||||
// this extension in order to conform to the ES6 syntax, which treats
|
||||
// "yield \n expr;" as a single ExpressionStatement.
|
||||
// This is ugly, but we don't want to require a semicolon.
|
||||
Node exprNode;
|
||||
TokenKind next = tokenStream.peekTokenSameLine(TokenStream::Operand);
|
||||
if (next == TOK_ERROR)
|
||||
switch (tokenStream.peekTokenSameLine(TokenStream::Operand)) {
|
||||
case TOK_ERROR:
|
||||
return null();
|
||||
if (next == TOK_EOF || next == TOK_EOL || next == TOK_SEMI || next == TOK_RC ||
|
||||
(isYield && (next == TOK_RB || next == TOK_RP || next == TOK_COLON || next == TOK_COMMA)))
|
||||
{
|
||||
if (isYield) {
|
||||
if (!reportWithOffset(ParseWarning, false, pos().begin, JSMSG_YIELD_WITHOUT_OPERAND))
|
||||
return null();
|
||||
}
|
||||
|
||||
case TOK_EOF:
|
||||
case TOK_EOL:
|
||||
case TOK_SEMI:
|
||||
case TOK_RC:
|
||||
exprNode = null();
|
||||
if (!isYield)
|
||||
pc->funHasReturnVoid = true;
|
||||
} else {
|
||||
exprNode = isYield ? assignExpr() : expr();
|
||||
pc->funHasReturnVoid = true;
|
||||
break;
|
||||
default: {
|
||||
exprNode = expr();
|
||||
if (!exprNode)
|
||||
return null();
|
||||
if (!isYield)
|
||||
pc->funHasReturnExpr = true;
|
||||
pc->funHasReturnExpr = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isYield) {
|
||||
if (!MatchOrInsertSemicolon(tokenStream))
|
||||
return null();
|
||||
}
|
||||
if (!MatchOrInsertSemicolon(tokenStream))
|
||||
return null();
|
||||
|
||||
Node pn = isYield
|
||||
? handler.newUnary(PNK_YIELD, JSOP_YIELD, begin, exprNode)
|
||||
: handler.newReturnStatement(exprNode, TokenPos(begin, pos().end));
|
||||
Node pn = handler.newReturnStatement(exprNode, TokenPos(begin, pos().end));
|
||||
if (!pn)
|
||||
return null();
|
||||
|
||||
if (pc->funHasReturnExpr && pc->isLegacyGenerator()) {
|
||||
/* As in Python (see PEP-255), disallow return v; in generators. */
|
||||
reportBadReturn(pn, ParseError, JSMSG_BAD_GENERATOR_RETURN,
|
||||
JSMSG_BAD_ANON_GENERATOR_RETURN);
|
||||
return null();
|
||||
}
|
||||
|
||||
if (options().extraWarningsOption && pc->funHasReturnExpr && pc->funHasReturnVoid &&
|
||||
!reportBadReturn(pn, ParseExtraWarning,
|
||||
JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE))
|
||||
|
@ -4504,9 +4555,108 @@ Parser<ParseHandler>::returnStatementOrYieldExpression()
|
|||
return null();
|
||||
}
|
||||
|
||||
if (pc->isLegacyGenerator() && exprNode) {
|
||||
/* Disallow "return v;" in legacy generators. */
|
||||
reportBadReturn(pn, ParseError, JSMSG_BAD_GENERATOR_RETURN,
|
||||
JSMSG_BAD_ANON_GENERATOR_RETURN);
|
||||
return null();
|
||||
}
|
||||
|
||||
return pn;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::yieldExpression()
|
||||
{
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_YIELD));
|
||||
uint32_t begin = pos().begin;
|
||||
|
||||
switch (pc->generatorKind()) {
|
||||
case StarGenerator:
|
||||
{
|
||||
JS_ASSERT(pc->sc->isFunctionBox());
|
||||
|
||||
pc->lastYieldOffset = begin;
|
||||
|
||||
bool isDelegatingYield = tokenStream.matchToken(TOK_MUL);
|
||||
|
||||
// ES6 generators require a value.
|
||||
Node exprNode = assignExpr();
|
||||
if (!exprNode)
|
||||
return null();
|
||||
|
||||
// FIXME: Plumb isDelegatingYield appropriately.
|
||||
(void) isDelegatingYield;
|
||||
return handler.newUnary(PNK_YIELD, JSOP_YIELD, begin, exprNode);
|
||||
}
|
||||
|
||||
case NotGenerator:
|
||||
// We are in code that has not seen a yield, but we are in JS 1.7 or
|
||||
// later. Try to transition to being a legacy generator.
|
||||
JS_ASSERT(tokenStream.versionNumber() >= JSVERSION_1_7);
|
||||
JS_ASSERT(pc->lastYieldOffset == ParseContext<ParseHandler>::NoYieldOffset);
|
||||
|
||||
if (!abortIfSyntaxParser())
|
||||
return null();
|
||||
|
||||
if (!pc->sc->isFunctionBox()) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_yield_str);
|
||||
return null();
|
||||
}
|
||||
|
||||
pc->sc->asFunctionBox()->setGeneratorKind(LegacyGenerator);
|
||||
|
||||
if (pc->funHasReturnExpr) {
|
||||
/* As in Python (see PEP-255), disallow return v; in generators. */
|
||||
reportBadReturn(null(), ParseError, JSMSG_BAD_GENERATOR_RETURN,
|
||||
JSMSG_BAD_ANON_GENERATOR_RETURN);
|
||||
return null();
|
||||
}
|
||||
// Fall through.
|
||||
|
||||
case LegacyGenerator:
|
||||
{
|
||||
// We are in a legacy generator: a function that has already seen a
|
||||
// yield, or in a legacy generator comprehension.
|
||||
JS_ASSERT(pc->sc->isFunctionBox());
|
||||
|
||||
pc->lastYieldOffset = begin;
|
||||
|
||||
// Legacy generators do not require a value.
|
||||
Node exprNode;
|
||||
switch (tokenStream.peekTokenSameLine(TokenStream::Operand)) {
|
||||
case TOK_ERROR:
|
||||
return null();
|
||||
case TOK_EOF:
|
||||
case TOK_EOL:
|
||||
case TOK_SEMI:
|
||||
case TOK_RC:
|
||||
case TOK_RB:
|
||||
case TOK_RP:
|
||||
case TOK_COLON:
|
||||
case TOK_COMMA:
|
||||
// No value.
|
||||
exprNode = null();
|
||||
// ES6 does not permit yield without an operand. We should
|
||||
// encourage users of yield expressions of this kind to pass an
|
||||
// operand, to bring users closer to standard syntax.
|
||||
if (!reportWithOffset(ParseWarning, false, pos().begin, JSMSG_YIELD_WITHOUT_OPERAND))
|
||||
return null();
|
||||
break;
|
||||
default:
|
||||
exprNode = assignExpr();
|
||||
if (!exprNode)
|
||||
return null();
|
||||
}
|
||||
|
||||
return handler.newUnary(PNK_YIELD, JSOP_YIELD, begin, exprNode);
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSUME_UNREACHABLE("yieldExpr");
|
||||
}
|
||||
|
||||
template <>
|
||||
ParseNode *
|
||||
Parser<FullParseHandler>::withStatement()
|
||||
|
@ -4576,7 +4726,7 @@ typename ParseHandler::Node
|
|||
Parser<ParseHandler>::labeledStatement()
|
||||
{
|
||||
uint32_t begin = pos().begin;
|
||||
RootedPropertyName label(context, tokenStream.currentToken().name());
|
||||
RootedPropertyName label(context, tokenStream.currentName());
|
||||
for (StmtInfoPC *stmt = pc->topStmt; stmt; stmt = stmt->down) {
|
||||
if (stmt->type == STMT_LABEL && stmt->label == label) {
|
||||
report(ParseError, false, null(), JSMSG_DUPLICATE_LABEL);
|
||||
|
@ -4717,9 +4867,13 @@ Parser<ParseHandler>::tryStatement()
|
|||
break;
|
||||
#endif
|
||||
|
||||
case TOK_YIELD:
|
||||
if (!checkYieldNameValidity(JSMSG_CATCH_IDENTIFIER))
|
||||
return null();
|
||||
// Fall through.
|
||||
case TOK_NAME:
|
||||
{
|
||||
RootedPropertyName label(context, tokenStream.currentToken().name());
|
||||
RootedPropertyName label(context, tokenStream.currentName());
|
||||
catchName = newBindingNode(label, false);
|
||||
if (!catchName)
|
||||
return null();
|
||||
|
@ -4855,7 +5009,7 @@ Parser<ParseHandler>::statement(bool canHaveDirectives)
|
|||
case TOK_BREAK:
|
||||
return breakStatement();
|
||||
case TOK_RETURN:
|
||||
return returnStatementOrYieldExpression();
|
||||
return returnStatement();
|
||||
case TOK_WITH:
|
||||
return withStatement();
|
||||
case TOK_THROW:
|
||||
|
@ -4886,10 +5040,18 @@ Parser<ParseHandler>::statement(bool canHaveDirectives)
|
|||
}
|
||||
return expressionStatement();
|
||||
|
||||
case TOK_YIELD:
|
||||
if (tokenStream.peekToken() == TOK_COLON) {
|
||||
if (!checkYieldNameValidity())
|
||||
return null();
|
||||
return labeledStatement();
|
||||
}
|
||||
return expressionStatement();
|
||||
|
||||
case TOK_NAME:
|
||||
if (tokenStream.peekToken() == TOK_COLON)
|
||||
return labeledStatement();
|
||||
if (tokenStream.currentToken().name() == context->names().module
|
||||
if (tokenStream.currentName() == context->names().module
|
||||
&& tokenStream.peekTokenSameLine() == TOK_STRING)
|
||||
{
|
||||
return moduleDecl();
|
||||
|
@ -5202,8 +5364,8 @@ Parser<ParseHandler>::assignExpr()
|
|||
if (tt == TOK_STRING && tokenStream.nextTokenEndsExpr())
|
||||
return stringLiteral();
|
||||
|
||||
if (tt == TOK_YIELD)
|
||||
return returnStatementOrYieldExpression();
|
||||
if (tt == TOK_YIELD && (versionNumber() >= JSVERSION_1_7 || pc->isGenerator()))
|
||||
return yieldExpression();
|
||||
|
||||
tokenStream.ungetToken();
|
||||
|
||||
|
@ -5241,7 +5403,7 @@ Parser<ParseHandler>::assignExpr()
|
|||
return null();
|
||||
tokenStream.ungetToken();
|
||||
|
||||
return functionDef(NullPtr(), start, Normal, Arrow);
|
||||
return functionDef(NullPtr(), start, Normal, Arrow, NotGenerator);
|
||||
}
|
||||
|
||||
default:
|
||||
|
@ -5662,7 +5824,7 @@ Parser<FullParseHandler>::comprehensionTail(ParseNode *kid, unsigned blockid, bo
|
|||
BindData<FullParseHandler> data(context);
|
||||
TokenKind tt;
|
||||
|
||||
JS_ASSERT(tokenStream.currentToken().type == TOK_FOR);
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
|
||||
|
||||
if (kind == PNK_SEMI) {
|
||||
/*
|
||||
|
@ -5747,7 +5909,7 @@ Parser<FullParseHandler>::comprehensionTail(ParseNode *kid, unsigned blockid, bo
|
|||
#endif
|
||||
|
||||
case TOK_NAME:
|
||||
name = tokenStream.currentToken().name();
|
||||
name = tokenStream.currentName();
|
||||
|
||||
/*
|
||||
* Create a name node with pn_op JSOP_NAME. We can't set pn_op to
|
||||
|
@ -5955,7 +6117,8 @@ Parser<FullParseHandler>::generatorExpr(ParseNode *kid)
|
|||
|
||||
/* Create box for fun->object early to protect against last-ditch GC. */
|
||||
Directives directives(/* strict = */ outerpc->sc->strict);
|
||||
FunctionBox *genFunbox = newFunctionBox(genfn, fun, outerpc, directives);
|
||||
FunctionBox *genFunbox = newFunctionBox(genfn, fun, outerpc, directives,
|
||||
LegacyGenerator);
|
||||
if (!genFunbox)
|
||||
return null();
|
||||
|
||||
|
@ -5975,7 +6138,7 @@ Parser<FullParseHandler>::generatorExpr(ParseNode *kid)
|
|||
if (outerpc->sc->isFunctionBox())
|
||||
genFunbox->funCxFlags = outerpc->sc->asFunctionBox()->funCxFlags;
|
||||
|
||||
genFunbox->setIsLegacyGenerator();
|
||||
JS_ASSERT(genFunbox->isLegacyGenerator());
|
||||
genFunbox->inGenexpLambda = true;
|
||||
genfn->pn_blockid = genpc.bodyid;
|
||||
|
||||
|
@ -6116,7 +6279,7 @@ Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax)
|
|||
if (tt == TOK_ERROR)
|
||||
return null();
|
||||
if (tt == TOK_NAME) {
|
||||
PropertyName *field = tokenStream.currentToken().name();
|
||||
PropertyName *field = tokenStream.currentName();
|
||||
nextMember = handler.newPropertyAccess(lhs, field, pos().end);
|
||||
if (!nextMember)
|
||||
return null();
|
||||
|
@ -6191,9 +6354,7 @@ template <typename ParseHandler>
|
|||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::identifierName()
|
||||
{
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME));
|
||||
|
||||
RootedPropertyName name(context, tokenStream.currentToken().name());
|
||||
RootedPropertyName name(context, tokenStream.currentName());
|
||||
Node pn = newName(name);
|
||||
if (!pn)
|
||||
return null();
|
||||
|
@ -6395,7 +6556,7 @@ Parser<ParseHandler>::objectLiteral()
|
|||
break;
|
||||
|
||||
case TOK_NAME: {
|
||||
atom = tokenStream.currentToken().name();
|
||||
atom = tokenStream.currentName();
|
||||
if (atom == context->names().get) {
|
||||
op = JSOP_INITPROP_GETTER;
|
||||
} else if (atom == context->names().set) {
|
||||
|
@ -6411,7 +6572,7 @@ Parser<ParseHandler>::objectLiteral()
|
|||
// name next.
|
||||
TokenKind tt = tokenStream.getToken(TokenStream::KeywordIsName);
|
||||
if (tt == TOK_NAME) {
|
||||
atom = tokenStream.currentToken().name();
|
||||
atom = tokenStream.currentName();
|
||||
propname = newName(atom->asPropertyName());
|
||||
if (!propname)
|
||||
return null();
|
||||
|
@ -6527,7 +6688,7 @@ Parser<ParseHandler>::objectLiteral()
|
|||
TokenStream::Position start(keepAtoms);
|
||||
tokenStream.tell(&start);
|
||||
Node accessor = functionDef(funName, start, op == JSOP_INITPROP_GETTER ? Getter : Setter,
|
||||
Expression);
|
||||
Expression, NotGenerator);
|
||||
if (!accessor)
|
||||
return null();
|
||||
if (!handler.addAccessorPropertyDefinition(literal, propname, accessor, op))
|
||||
|
@ -6628,6 +6789,10 @@ Parser<ParseHandler>::primaryExpr(TokenKind tt)
|
|||
case TOK_STRING:
|
||||
return stringLiteral();
|
||||
|
||||
case TOK_YIELD:
|
||||
if (!checkYieldNameValidity())
|
||||
return null();
|
||||
// Fall through.
|
||||
case TOK_NAME:
|
||||
return identifierName();
|
||||
|
||||
|
@ -6689,7 +6854,7 @@ template <typename ParseHandler>
|
|||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::parenExpr(bool *genexp)
|
||||
{
|
||||
JS_ASSERT(tokenStream.currentToken().type == TOK_LP);
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
|
||||
uint32_t begin = pos().begin;
|
||||
|
||||
if (genexp)
|
||||
|
|
|
@ -104,18 +104,20 @@ struct ParseContext : public GenericParseContext
|
|||
|
||||
const unsigned staticLevel; /* static compilation unit nesting level */
|
||||
|
||||
// Functions start off being parsed as NotGenerator.
|
||||
// NotGenerator transitions to LegacyGenerator on parsing "yield" in JS 1.7.
|
||||
enum GeneratorParseMode { NotGenerator, LegacyGenerator };
|
||||
GeneratorParseMode generatorParseMode;
|
||||
|
||||
// lastYieldOffset stores the offset of the last yield that was parsed.
|
||||
// NoYieldOffset is its initial value.
|
||||
static const uint32_t NoYieldOffset = UINT32_MAX;
|
||||
uint32_t lastYieldOffset;
|
||||
|
||||
bool isGenerator() const { return generatorParseMode != NotGenerator; }
|
||||
bool isLegacyGenerator() const { return generatorParseMode == LegacyGenerator; }
|
||||
// Most functions start off being parsed as non-generators.
|
||||
// Non-generators transition to LegacyGenerator on parsing "yield" in JS 1.7.
|
||||
// An ES6 generator is marked as a "star generator" before its body is parsed.
|
||||
GeneratorKind generatorKind() const {
|
||||
return sc->isFunctionBox() ? sc->asFunctionBox()->generatorKind() : NotGenerator;
|
||||
}
|
||||
bool isGenerator() const { return generatorKind() != NotGenerator; }
|
||||
bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; }
|
||||
bool isStarGenerator() const { return generatorKind() == StarGenerator; }
|
||||
|
||||
Node blockNode; /* parse node for a block with let declarations
|
||||
(block with its own lexical scope) */
|
||||
|
@ -249,7 +251,6 @@ struct ParseContext : public GenericParseContext
|
|||
blockChain(prs->context),
|
||||
maybeFunction(maybeFunction),
|
||||
staticLevel(staticLevel),
|
||||
generatorParseMode(NotGenerator),
|
||||
lastYieldOffset(NoYieldOffset),
|
||||
blockNode(ParseHandler::null()),
|
||||
decls_(prs->context, prs->alloc),
|
||||
|
@ -400,7 +401,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter
|
|||
ObjectBox *newObjectBox(JSObject *obj);
|
||||
ModuleBox *newModuleBox(Module *module, ParseContext<ParseHandler> *pc);
|
||||
FunctionBox *newFunctionBox(Node fn, JSFunction *fun, ParseContext<ParseHandler> *pc,
|
||||
Directives directives);
|
||||
Directives directives, GeneratorKind generatorKind);
|
||||
|
||||
/*
|
||||
* Create a new function object given parse context (pc) and a name (which
|
||||
|
@ -433,13 +434,16 @@ class Parser : private AutoGCRooter, public StrictModeGetter
|
|||
Node statement(bool canHaveDirectives = false);
|
||||
bool maybeParseDirective(Node list, Node pn, bool *cont);
|
||||
|
||||
// Parse a function, given only its body. Used for the Function constructor.
|
||||
// Parse a function, given only its body. Used for the Function and
|
||||
// Generator constructors.
|
||||
Node standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals,
|
||||
GeneratorKind generatorKind,
|
||||
Directives inheritedDirectives, Directives *newDirectives);
|
||||
|
||||
// Parse a function, given only its arguments and body. Used for lazily
|
||||
// parsed functions.
|
||||
Node standaloneLazyFunction(HandleFunction fun, unsigned staticLevel, bool strict);
|
||||
Node standaloneLazyFunction(HandleFunction fun, unsigned staticLevel, bool strict,
|
||||
GeneratorKind generatorKind);
|
||||
|
||||
/*
|
||||
* Parse a function body. Pass StatementListBody if the body is a list of
|
||||
|
@ -487,7 +491,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter
|
|||
Node switchStatement();
|
||||
Node continueStatement();
|
||||
Node breakStatement();
|
||||
Node returnStatementOrYieldExpression();
|
||||
Node returnStatement();
|
||||
Node withStatement();
|
||||
Node labeledStatement();
|
||||
Node throwStatement();
|
||||
|
@ -504,6 +508,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter
|
|||
Node expr();
|
||||
Node assignExpr();
|
||||
Node assignExprWithoutYield(unsigned err);
|
||||
Node yieldExpression();
|
||||
Node condExpr1();
|
||||
Node orExpr1();
|
||||
Node unaryExpr();
|
||||
|
@ -517,9 +522,10 @@ class Parser : private AutoGCRooter, public StrictModeGetter
|
|||
bool functionArguments(FunctionSyntaxKind kind, Node *list, Node funcpn, bool &hasRest);
|
||||
|
||||
Node functionDef(HandlePropertyName name, const TokenStream::Position &start,
|
||||
FunctionType type, FunctionSyntaxKind kind);
|
||||
FunctionType type, FunctionSyntaxKind kind, GeneratorKind generatorKind);
|
||||
bool functionArgsAndBody(Node pn, HandleFunction fun,
|
||||
FunctionType type, FunctionSyntaxKind kind,
|
||||
GeneratorKind generatorKind,
|
||||
Directives inheritedDirectives, Directives *newDirectives);
|
||||
|
||||
Node unaryOpExpr(ParseNodeKind kind, JSOp op, uint32_t begin);
|
||||
|
@ -536,6 +542,9 @@ class Parser : private AutoGCRooter, public StrictModeGetter
|
|||
|
||||
Node identifierName();
|
||||
|
||||
bool matchLabel(MutableHandle<PropertyName*> label);
|
||||
bool checkYieldNameValidity(unsigned errorNumber = JSMSG_SYNTAX_ERROR);
|
||||
|
||||
bool allowsForEachIn() {
|
||||
#if !JS_HAS_FOR_EACH_IN
|
||||
return false;
|
||||
|
@ -556,6 +565,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter
|
|||
|
||||
bool checkFunctionArguments();
|
||||
bool makeDefIntoUse(Definition *dn, Node pn, JSAtom *atom);
|
||||
bool checkFunctionName(HandlePropertyName funName);
|
||||
bool checkFunctionDefinition(HandlePropertyName funName, Node *pn, FunctionSyntaxKind kind,
|
||||
bool *pbodyProcessed);
|
||||
bool finishFunctionDefinition(Node pn, FunctionBox *funbox, Node prelude, Node body);
|
||||
|
|
|
@ -72,10 +72,6 @@ class FunctionContextFlags
|
|||
// This class's data is all private and so only visible to these friends.
|
||||
friend class FunctionBox;
|
||||
|
||||
// We parsed a yield statement in the function, which can happen in JS1.7+
|
||||
// mode.
|
||||
bool isLegacyGenerator:1;
|
||||
|
||||
// The function or a function that encloses it may define new local names
|
||||
// at runtime through means other than calling eval.
|
||||
bool mightAliasLocals:1;
|
||||
|
@ -129,8 +125,7 @@ class FunctionContextFlags
|
|||
|
||||
public:
|
||||
FunctionContextFlags()
|
||||
: isLegacyGenerator(false),
|
||||
mightAliasLocals(false),
|
||||
: mightAliasLocals(false),
|
||||
hasExtensibleScope(false),
|
||||
needsDeclEnvObject(false),
|
||||
argumentsHasLocalBinding(false),
|
||||
|
@ -264,6 +259,8 @@ class FunctionBox : public ObjectBox, public SharedContext
|
|||
uint32_t startLine;
|
||||
uint32_t startColumn;
|
||||
uint16_t ndefaults;
|
||||
|
||||
uint8_t generatorKindBits_; /* The GeneratorKind of this function. */
|
||||
bool inWith:1; /* some enclosing scope is a with-statement */
|
||||
bool inGenexpLambda:1; /* lambda from generator expression */
|
||||
bool hasDestructuringArgs:1; /* arguments list contains destructuring expression */
|
||||
|
@ -279,21 +276,30 @@ class FunctionBox : public ObjectBox, public SharedContext
|
|||
template <typename ParseHandler>
|
||||
FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunction *fun,
|
||||
ParseContext<ParseHandler> *pc, Directives directives,
|
||||
bool extraWarnings);
|
||||
bool extraWarnings, GeneratorKind generatorKind);
|
||||
|
||||
ObjectBox *toObjectBox() { return this; }
|
||||
JSFunction *function() const { return &object->as<JSFunction>(); }
|
||||
|
||||
// In the future, isGenerator will also return true for ES6 generators.
|
||||
bool isGenerator() const { return isLegacyGenerator(); }
|
||||
bool isLegacyGenerator() const { return funCxFlags.isLegacyGenerator; }
|
||||
GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); }
|
||||
bool isGenerator() const { return generatorKind() != NotGenerator; }
|
||||
bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; }
|
||||
bool isStarGenerator() const { return generatorKind() == StarGenerator; }
|
||||
|
||||
void setGeneratorKind(GeneratorKind kind) {
|
||||
// A generator kind can be set at initialization, or when "yield" is
|
||||
// first seen. In both cases the transition can only happen from
|
||||
// NotGenerator.
|
||||
JS_ASSERT(!isGenerator());
|
||||
generatorKindBits_ = GeneratorKindAsBits(kind);
|
||||
}
|
||||
|
||||
bool mightAliasLocals() const { return funCxFlags.mightAliasLocals; }
|
||||
bool hasExtensibleScope() const { return funCxFlags.hasExtensibleScope; }
|
||||
bool needsDeclEnvObject() const { return funCxFlags.needsDeclEnvObject; }
|
||||
bool argumentsHasLocalBinding() const { return funCxFlags.argumentsHasLocalBinding; }
|
||||
bool definitelyNeedsArgsObj() const { return funCxFlags.definitelyNeedsArgsObj; }
|
||||
|
||||
void setIsLegacyGenerator() { funCxFlags.isLegacyGenerator = true; }
|
||||
void setMightAliasLocals() { funCxFlags.mightAliasLocals = true; }
|
||||
void setHasExtensibleScope() { funCxFlags.hasExtensibleScope = true; }
|
||||
void setNeedsDeclEnvObject() { funCxFlags.needsDeclEnvObject = true; }
|
||||
|
|
|
@ -937,10 +937,10 @@ TokenStream::checkForKeyword(const jschar *s, size_t length, TokenKind *ttp)
|
|||
return reportError(JSMSG_RESERVED_ID, kw->chars);
|
||||
}
|
||||
|
||||
// The keyword is not in this version. Treat it as an identifier,
|
||||
// unless it is let or yield which we treat as TOK_STRICT_RESERVED by
|
||||
// falling through to the code below (ES5 forbids them in strict mode).
|
||||
if (kw->tokentype != TOK_LET && kw->tokentype != TOK_YIELD)
|
||||
// The keyword is not in this version. Treat it as an identifier, unless
|
||||
// it is let which we treat as TOK_STRICT_RESERVED by falling through to
|
||||
// the code below (ES5 forbids it in strict mode).
|
||||
if (kw->tokentype != TOK_LET)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -407,6 +407,13 @@ class MOZ_STACK_CLASS TokenStream
|
|||
JSVersion versionNumber() const { return VersionNumber(options().version); }
|
||||
JSVersion versionWithFlags() const { return options().version; }
|
||||
|
||||
PropertyName *currentName() const {
|
||||
if (isCurrentTokenType(TOK_YIELD))
|
||||
return cx->names().yield;
|
||||
JS_ASSERT(isCurrentTokenType(TOK_NAME));
|
||||
return currentToken().name();
|
||||
}
|
||||
|
||||
bool isCurrentTokenAssignment() const {
|
||||
return TokenKindIsAssignment(currentToken().type);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
// Test interactions between ES6 generators and not-yet-standard
|
||||
// features.
|
||||
|
||||
function assertSyntaxError(str) {
|
||||
var msg;
|
||||
var evil = eval;
|
||||
try {
|
||||
// Non-direct eval.
|
||||
evil(str);
|
||||
} catch (exc) {
|
||||
if (exc instanceof SyntaxError)
|
||||
return;
|
||||
msg = "Assertion failed: expected SyntaxError, got " + exc;
|
||||
}
|
||||
if (msg === undefined)
|
||||
msg = "Assertion failed: expected SyntaxError, but no exception thrown";
|
||||
throw new Error(msg + " - " + str);
|
||||
}
|
||||
|
||||
// Destructuring binding.
|
||||
assertSyntaxError("function* f(x = yield) {}");
|
||||
assertSyntaxError("function* f(x = yield 17) {}");
|
||||
assertSyntaxError("function* f([yield]) {}");
|
||||
assertSyntaxError("function* f({ yield }) {}");
|
||||
assertSyntaxError("function* f(...yield) {}");
|
||||
|
||||
// For each.
|
||||
assertSyntaxError("for yield");
|
||||
assertSyntaxError("for yield (;;) {}");
|
||||
assertSyntaxError("for yield (x of y) {}");
|
||||
assertSyntaxError("for yield (var i in o) {}");
|
||||
|
||||
// Expression bodies.
|
||||
assertSyntaxError("function* f() yield 7");
|
||||
|
||||
// Asm.js.
|
||||
load(libdir + "asm.js");
|
||||
assertAsmDirectiveFail("function* f() { 'use asm'; function g() { return 0; } return g; })()")
|
|
@ -4607,7 +4607,7 @@ ParseFunction(ModuleCompiler &m, ParseNode **fnOut)
|
|||
AsmJSParseContext *outerpc = m.parser().pc;
|
||||
|
||||
Directives directives(outerpc);
|
||||
FunctionBox *funbox = m.parser().newFunctionBox(fn, fun, outerpc, directives);
|
||||
FunctionBox *funbox = m.parser().newFunctionBox(fn, fun, outerpc, directives, NotGenerator);
|
||||
if (!funbox)
|
||||
return false;
|
||||
|
||||
|
|
|
@ -409,3 +409,4 @@ MSG_DEF(JSMSG_BINARYDATA_NOT_BINARYSTRUCT, 355, 1, JSEXN_TYPEERR, "{0} is not
|
|||
MSG_DEF(JSMSG_BINARYDATA_SUBARRAY_INTEGER_ARG, 356, 1, JSEXN_ERR, "argument {0} must be an integer")
|
||||
MSG_DEF(JSMSG_BINARYDATA_STRUCTTYPE_EMPTY_DESCRIPTOR, 357, 0, JSEXN_ERR, "field descriptor cannot be empty")
|
||||
MSG_DEF(JSMSG_BINARYDATA_STRUCTTYPE_BAD_FIELD, 358, 1, JSEXN_ERR, "field {0} is not a valid BinaryData Type descriptor")
|
||||
MSG_DEF(JSMSG_ES6_UNIMPLEMENTED, 359, 0, JSEXN_ERR, "ES6 functionality not yet implemented")
|
||||
|
|
|
@ -1749,7 +1749,7 @@ ScriptAnalysis::needsArgsObj(JSContext *cx)
|
|||
*
|
||||
* FIXME: Don't build arguments for ES6 generator expressions.
|
||||
*/
|
||||
if (cx->compartment()->debugMode() || script_->isGenerator)
|
||||
if (cx->compartment()->debugMode() || script_->isGenerator())
|
||||
return true;
|
||||
|
||||
/*
|
||||
|
|
|
@ -104,7 +104,6 @@ const char js_typeof_str[] = "typeof";
|
|||
const char js_void_str[] = "void";
|
||||
const char js_while_str[] = "while";
|
||||
const char js_with_str[] = "with";
|
||||
const char js_yield_str[] = "yield";
|
||||
|
||||
/*
|
||||
* For a browser build from 2007-08-09 after the browser starts up there are
|
||||
|
|
|
@ -153,7 +153,6 @@ extern const char js_typeof_str[];
|
|||
extern const char js_void_str[];
|
||||
extern const char js_while_str[];
|
||||
extern const char js_with_str[];
|
||||
extern const char js_yield_str[];
|
||||
|
||||
namespace js {
|
||||
|
||||
|
|
|
@ -526,6 +526,7 @@ FindBody(JSContext *cx, HandleFunction fun, StableCharPtr chars, size_t length,
|
|||
do {
|
||||
switch (ts.getToken()) {
|
||||
case TOK_NAME:
|
||||
case TOK_YIELD:
|
||||
if (nest == 0)
|
||||
onward = false;
|
||||
break;
|
||||
|
@ -1228,14 +1229,7 @@ fun_isGenerator(JSContext *cx, unsigned argc, Value *vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
if (fun->hasScript()) {
|
||||
JSScript *script = fun->nonLazyScript();
|
||||
JS_ASSERT(script->length != 0);
|
||||
result = script->isGenerator;
|
||||
}
|
||||
|
||||
JS_SET_RVAL(cx, vp, BooleanValue(result));
|
||||
JS_SET_RVAL(cx, vp, BooleanValue(fun->isGenerator()));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1456,10 +1450,15 @@ js::Function(JSContext *cx, unsigned argc, Value *vp)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (tt == TOK_YIELD && ts.versionNumber() < JSVERSION_1_7)
|
||||
tt = TOK_NAME;
|
||||
|
||||
if (tt != TOK_NAME) {
|
||||
if (tt == TOK_TRIPLEDOT) {
|
||||
hasRest = true;
|
||||
tt = ts.getToken();
|
||||
if (tt == TOK_YIELD && ts.versionNumber() < JSVERSION_1_7)
|
||||
tt = TOK_NAME;
|
||||
if (tt != TOK_NAME) {
|
||||
if (tt != TOK_ERROR)
|
||||
ts.reportError(JSMSG_NO_REST_NAME);
|
||||
|
@ -1470,7 +1469,7 @@ js::Function(JSContext *cx, unsigned argc, Value *vp)
|
|||
}
|
||||
}
|
||||
|
||||
if (!formals.append(ts.currentToken().name()))
|
||||
if (!formals.append(ts.currentName()))
|
||||
return false;
|
||||
|
||||
/*
|
||||
|
|
|
@ -284,6 +284,18 @@ class JSFunction : public JSObject
|
|||
return u.i.s.lazy_;
|
||||
}
|
||||
|
||||
js::GeneratorKind generatorKind() const {
|
||||
if (!isInterpreted())
|
||||
return js::NotGenerator;
|
||||
return hasScript() ? nonLazyScript()->generatorKind() : lazyScript()->generatorKind();
|
||||
}
|
||||
|
||||
bool isGenerator() const { return generatorKind() != js::NotGenerator; }
|
||||
|
||||
bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; }
|
||||
|
||||
bool isStarGenerator() const { return generatorKind() == js::StarGenerator; }
|
||||
|
||||
inline void setScript(JSScript *script_);
|
||||
inline void initScript(JSScript *script_);
|
||||
void initLazyScript(js::LazyScript *lazy) {
|
||||
|
|
|
@ -1475,6 +1475,13 @@ js_NewGenerator(JSContext *cx, const FrameRegs &stackRegs)
|
|||
JS_ASSERT(stackRegs.stackDepth() == 0);
|
||||
StackFrame *stackfp = stackRegs.fp();
|
||||
|
||||
JS_ASSERT(stackfp->script()->isGenerator());
|
||||
|
||||
if (stackfp->script()->isStarGenerator()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ES6_UNIMPLEMENTED);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Rooted<GlobalObject*> global(cx, &stackfp->global());
|
||||
RootedObject obj(cx);
|
||||
{
|
||||
|
|
|
@ -2787,6 +2787,7 @@ ASTSerializer::function(ParseNode *pn, ASTType type, MutableHandleValue dst)
|
|||
{
|
||||
RootedFunction func(cx, pn->pn_funbox->function());
|
||||
|
||||
// FIXME: Provide more information (legacy generator vs star generator).
|
||||
bool isGenerator = pn->pn_funbox->isGenerator();
|
||||
|
||||
bool isExpression =
|
||||
|
|
|
@ -408,9 +408,9 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
|
|||
FunHasAnyAliasedFormal,
|
||||
ArgumentsHasVarBinding,
|
||||
NeedsArgsObj,
|
||||
IsGenerator,
|
||||
IsGeneratorExp,
|
||||
IsLegacyGenerator,
|
||||
IsStarGenerator,
|
||||
OwnSource,
|
||||
ExplicitUseStrict,
|
||||
SelfHosted
|
||||
|
@ -497,12 +497,12 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
|
|||
scriptBits |= (1 << NeedsArgsObj);
|
||||
if (!enclosingScript || enclosingScript->scriptSource() != script->scriptSource())
|
||||
scriptBits |= (1 << OwnSource);
|
||||
if (script->isGenerator)
|
||||
scriptBits |= (1 << IsGenerator);
|
||||
if (script->isGeneratorExp)
|
||||
scriptBits |= (1 << IsGeneratorExp);
|
||||
if (script->isLegacyGenerator)
|
||||
if (script->isLegacyGenerator())
|
||||
scriptBits |= (1 << IsLegacyGenerator);
|
||||
if (script->isStarGenerator())
|
||||
scriptBits |= (1 << IsStarGenerator);
|
||||
|
||||
JS_ASSERT(!script->compileAndGo);
|
||||
JS_ASSERT(!script->hasSingletons);
|
||||
|
@ -597,12 +597,14 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
|
|||
script->setArgumentsHasVarBinding();
|
||||
if (scriptBits & (1 << NeedsArgsObj))
|
||||
script->setNeedsArgsObj(true);
|
||||
if (scriptBits & (1 << IsGenerator))
|
||||
script->isGenerator = true;
|
||||
if (scriptBits & (1 << IsGeneratorExp))
|
||||
script->isGeneratorExp = true;
|
||||
if (scriptBits & (1 << IsLegacyGenerator))
|
||||
script->isLegacyGenerator = true;
|
||||
|
||||
if (scriptBits & (1 << IsLegacyGenerator)) {
|
||||
JS_ASSERT(!(scriptBits & (1 << IsStarGenerator)));
|
||||
script->setGeneratorKind(LegacyGenerator);
|
||||
} else if (scriptBits & (1 << IsStarGenerator))
|
||||
script->setGeneratorKind(StarGenerator);
|
||||
}
|
||||
|
||||
JS_STATIC_ASSERT(sizeof(jsbytecode) == 1);
|
||||
|
@ -1974,9 +1976,8 @@ JSScript::fullyInitFromEmitter(ExclusiveContext *cx, HandleScript script, Byteco
|
|||
RootedFunction fun(cx, NULL);
|
||||
if (funbox) {
|
||||
JS_ASSERT(!bce->script->noScriptRval);
|
||||
script->isGenerator = funbox->isGenerator();
|
||||
script->isGeneratorExp = funbox->inGenexpLambda;
|
||||
script->isLegacyGenerator = funbox->isLegacyGenerator();
|
||||
script->setGeneratorKind(funbox->generatorKind());
|
||||
script->setFunction(funbox->function());
|
||||
}
|
||||
|
||||
|
@ -2477,8 +2478,8 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun,
|
|||
dst->funHasAnyAliasedFormal = src->funHasAnyAliasedFormal;
|
||||
dst->hasSingletons = src->hasSingletons;
|
||||
dst->treatAsRunOnce = src->treatAsRunOnce;
|
||||
dst->isGenerator = src->isGenerator;
|
||||
dst->isGeneratorExp = src->isGeneratorExp;
|
||||
dst->setGeneratorKind(src->generatorKind());
|
||||
|
||||
/* Copy over hints. */
|
||||
dst->shouldInline = src->shouldInline;
|
||||
|
@ -2919,7 +2920,7 @@ JSScript::argumentsOptimizationFailed(JSContext *cx, HandleScript script)
|
|||
if (script->needsArgsObj())
|
||||
return true;
|
||||
|
||||
JS_ASSERT(!script->isGenerator);
|
||||
JS_ASSERT(!script->isGenerator());
|
||||
|
||||
script->needsArgsObj_ = true;
|
||||
|
||||
|
@ -3011,6 +3012,7 @@ LazyScript::LazyScript(JSFunction *fun, void *table, uint32_t numFreeVariables,
|
|||
version_(version),
|
||||
numFreeVariables_(numFreeVariables),
|
||||
numInnerFunctions_(numInnerFunctions),
|
||||
generatorKindBits_(GeneratorKindAsBits(NotGenerator)),
|
||||
strict_(false),
|
||||
bindingsAccessedDynamically_(false),
|
||||
hasDebuggerStatement_(false),
|
||||
|
|
|
@ -414,6 +414,19 @@ class ScriptSourceObject : public JSObject
|
|||
static const uint32_t SOURCE_SLOT = 0;
|
||||
};
|
||||
|
||||
enum GeneratorKind { NotGenerator, LegacyGenerator, StarGenerator };
|
||||
|
||||
static inline unsigned
|
||||
GeneratorKindAsBits(GeneratorKind generatorKind) {
|
||||
return static_cast<unsigned>(generatorKind);
|
||||
}
|
||||
|
||||
static inline GeneratorKind
|
||||
GeneratorKindFromBits(unsigned val) {
|
||||
JS_ASSERT(val <= StarGenerator);
|
||||
return static_cast<GeneratorKind>(val);
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
class JSScript : public js::gc::Cell
|
||||
|
@ -519,7 +532,7 @@ class JSScript : public js::gc::Cell
|
|||
uint16_t nslots; /* vars plus maximum stack depth */
|
||||
uint16_t staticLevel;/* static level for display maintenance */
|
||||
|
||||
// 8-bit fields.
|
||||
// 4-bit fields.
|
||||
|
||||
public:
|
||||
// The kinds of the optional arrays.
|
||||
|
@ -528,15 +541,16 @@ class JSScript : public js::gc::Cell
|
|||
OBJECTS,
|
||||
REGEXPS,
|
||||
TRYNOTES,
|
||||
LIMIT
|
||||
ARRAY_KIND_BITS
|
||||
};
|
||||
|
||||
typedef uint8_t ArrayBitsT;
|
||||
|
||||
private:
|
||||
// The bits in this field indicate the presence/non-presence of several
|
||||
// optional arrays in |data|. See the comments above Create() for details.
|
||||
ArrayBitsT hasArrayBits;
|
||||
uint8_t hasArrayBits:4;
|
||||
|
||||
// The GeneratorKind of the script.
|
||||
uint8_t generatorKindBits_:4;
|
||||
|
||||
// 1-bit fields.
|
||||
|
||||
|
@ -589,14 +603,9 @@ class JSScript : public js::gc::Cell
|
|||
#endif
|
||||
bool invalidatedIdempotentCache:1; /* idempotent cache has triggered invalidation */
|
||||
|
||||
// All generators have isGenerator set to true.
|
||||
bool isGenerator:1;
|
||||
// If the generator was created implicitly via a generator expression,
|
||||
// isGeneratorExp will be true.
|
||||
bool isGeneratorExp:1;
|
||||
// Generators are either legacy-style (JS 1.7+ starless generators with
|
||||
// StopIteration), or ES6-style (function* with boxed return values).
|
||||
bool isLegacyGenerator:1;
|
||||
|
||||
bool hasScriptCounts:1;/* script has an entry in
|
||||
JSCompartment::scriptCountsMap */
|
||||
|
@ -647,6 +656,19 @@ class JSScript : public js::gc::Cell
|
|||
jsbytecode *argumentsBytecode() const { JS_ASSERT(code[0] == JSOP_ARGUMENTS); return code; }
|
||||
void setArgumentsHasVarBinding();
|
||||
|
||||
js::GeneratorKind generatorKind() const {
|
||||
return js::GeneratorKindFromBits(generatorKindBits_);
|
||||
}
|
||||
bool isGenerator() const { return generatorKind() != js::NotGenerator; }
|
||||
bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; }
|
||||
bool isStarGenerator() const { return generatorKind() == js::StarGenerator; }
|
||||
void setGeneratorKind(js::GeneratorKind kind) {
|
||||
// A script only gets its generator kind set as part of initialization,
|
||||
// so it can only transition from not being a generator.
|
||||
JS_ASSERT(!isGenerator());
|
||||
generatorKindBits_ = GeneratorKindAsBits(kind);
|
||||
}
|
||||
|
||||
/*
|
||||
* As an optimization, even when argsHasLocalBinding, the function prologue
|
||||
* may not need to create an arguments object. This is determined by
|
||||
|
@ -1038,7 +1060,8 @@ class JSScript : public js::gc::Cell
|
|||
void markChildren(JSTracer *trc);
|
||||
};
|
||||
|
||||
JS_STATIC_ASSERT(sizeof(JSScript::ArrayBitsT) * 8 >= JSScript::LIMIT);
|
||||
/* The array kind flags are stored in a 4-bit field; make sure they fit. */
|
||||
JS_STATIC_ASSERT(JSScript::ARRAY_KIND_BITS <= 4);
|
||||
|
||||
/* If this fails, add/remove padding within JSScript. */
|
||||
JS_STATIC_ASSERT(sizeof(JSScript) % js::gc::CellSize == 0);
|
||||
|
@ -1145,7 +1168,9 @@ class LazyScript : public js::gc::Cell
|
|||
uint32_t version_ : 8;
|
||||
|
||||
uint32_t numFreeVariables_ : 24;
|
||||
uint32_t numInnerFunctions_ : 26;
|
||||
uint32_t numInnerFunctions_ : 24;
|
||||
|
||||
uint32_t generatorKindBits_:2;
|
||||
|
||||
// N.B. These are booleans but need to be uint32_t to pack correctly on MSVC.
|
||||
uint32_t strict_ : 1;
|
||||
|
@ -1211,6 +1236,23 @@ class LazyScript : public js::gc::Cell
|
|||
return (HeapPtrFunction *)&freeVariables()[numFreeVariables()];
|
||||
}
|
||||
|
||||
GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); }
|
||||
|
||||
bool isGenerator() const { return generatorKind() != NotGenerator; }
|
||||
|
||||
bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; }
|
||||
|
||||
bool isStarGenerator() const { return generatorKind() == StarGenerator; }
|
||||
|
||||
void setGeneratorKind(GeneratorKind kind) {
|
||||
// A script only gets its generator kind set as part of initialization,
|
||||
// so it can only transition from NotGenerator.
|
||||
JS_ASSERT(!isGenerator());
|
||||
// Legacy generators cannot currently be lazy.
|
||||
JS_ASSERT(kind != LegacyGenerator);
|
||||
generatorKindBits_ = GeneratorKindAsBits(kind);
|
||||
}
|
||||
|
||||
bool strict() const {
|
||||
return strict_;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
// This file was written by Andy Wingo <wingo@igalia.com> and originally
|
||||
// contributed to V8 as generators-parsing.js, available here:
|
||||
//
|
||||
// http://code.google.com/p/v8/source/browse/branches/bleeding_edge/test/mjsunit/harmony/generators-parsing.js
|
||||
|
||||
function assertSyntaxError(str) {
|
||||
var msg;
|
||||
var evil = eval;
|
||||
try {
|
||||
// Non-direct eval.
|
||||
evil(str);
|
||||
} catch (exc) {
|
||||
if (exc instanceof SyntaxError)
|
||||
return;
|
||||
msg = "Assertion failed: expected SyntaxError, got " + exc;
|
||||
}
|
||||
if (msg === undefined)
|
||||
msg = "Assertion failed: expected SyntaxError, but no exception thrown";
|
||||
throw new Error(msg + " - " + str);
|
||||
}
|
||||
|
||||
// Yield statements.
|
||||
function* g() { yield 3; yield 4; }
|
||||
|
||||
// Yield expressions.
|
||||
function* g() { (yield 3) + (yield 4); }
|
||||
|
||||
// You can have a generator in strict mode.
|
||||
function* g() { "use strict"; yield 3; yield 4; }
|
||||
|
||||
// Generators can have return statements also, which internally parse to a kind
|
||||
// of yield expression.
|
||||
function* g() { yield 1; return; }
|
||||
function* g() { yield 1; return 2; }
|
||||
function* g() { yield 1; return 2; yield "dead"; }
|
||||
|
||||
// Generator expression.
|
||||
(function* () { yield 3; });
|
||||
|
||||
// Named generator expression.
|
||||
(function* g() { yield 3; });
|
||||
|
||||
// Generators do not have to contain yield expressions.
|
||||
function* g() { }
|
||||
|
||||
// YieldExpressions can occur in the RHS of a YieldExpression.
|
||||
function* g() { yield yield 1; }
|
||||
function* g() { yield 3 + (yield 4); }
|
||||
|
||||
// Generator definitions with a name of "yield" are not specifically ruled out
|
||||
// by the spec, as the `yield' name is outside the generator itself. However,
|
||||
// in strict-mode, "yield" is an invalid identifier.
|
||||
function* yield() { (yield 3) + (yield 4); }
|
||||
assertSyntaxError("function* yield() { 'use strict'; (yield 3) + (yield 4); }");
|
||||
|
||||
// In classic mode, yield is a normal identifier, outside of generators.
|
||||
function yield(yield) { yield: yield (yield + yield (0)); }
|
||||
|
||||
// Yield is always valid as a key in an object literal.
|
||||
({ yield: 1 });
|
||||
function* g() { yield ({ yield: 1 }) }
|
||||
function* g() { yield ({ get yield() { return 1; }}) }
|
||||
|
||||
// Yield is a valid property name.
|
||||
function* g(obj) { yield obj.yield; }
|
||||
|
||||
// Checks that yield is a valid label in classic mode, but not valid in a strict
|
||||
// mode or in generators.
|
||||
function f() { yield: 1 }
|
||||
assertSyntaxError("function f() { 'use strict'; yield: 1 }")
|
||||
assertSyntaxError("function* g() { yield: 1 }")
|
||||
|
||||
// Yield is only a keyword in the body of the generator, not in nested
|
||||
// functions.
|
||||
function* g() { function f(yield) { yield (yield + yield (0)); } }
|
||||
|
||||
// Yield needs a RHS.
|
||||
assertSyntaxError("function* g() { yield; }");
|
||||
|
||||
// Yield in a generator is not an identifier.
|
||||
assertSyntaxError("function* g() { yield = 10; }");
|
||||
|
||||
// Yield binds very loosely, so this parses as "yield (3 + yield 4)", which is
|
||||
// invalid.
|
||||
assertSyntaxError("function* g() { yield 3 + yield 4; }");
|
||||
|
||||
// Yield is still a future-reserved-word in strict mode
|
||||
assertSyntaxError("function f() { 'use strict'; var yield = 13; }");
|
||||
|
||||
// The name of the NFE is let-bound in G, so is invalid.
|
||||
assertSyntaxError("function* g() { yield (function yield() {}); }");
|
||||
|
||||
// In generators, yield is invalid as a formal argument name.
|
||||
assertSyntaxError("function* g(yield) { yield (10); }");
|
||||
|
||||
if (typeof reportCompare == "function")
|
||||
reportCompare(true, true);
|
|
@ -152,6 +152,7 @@
|
|||
macro(void0, void0, "(void 0)") \
|
||||
macro(watch, watch, "watch") \
|
||||
macro(writable, writable, "writable") \
|
||||
macro(yield, yield, "yield") \
|
||||
/* Type names must be contiguous and ordered; see js::TypeName. */ \
|
||||
macro(undefined, undefined, "undefined") \
|
||||
macro(object, object, "object") \
|
||||
|
|
|
@ -72,8 +72,12 @@
|
|||
macro(protected, protected_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
|
||||
macro(public, public_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
|
||||
macro(static, static_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
|
||||
/* ES5 future reserved keyword in strict mode, keyword in JS1.7 even when not strict. */ \
|
||||
macro(yield, yield, TOK_YIELD, JSVERSION_1_7) \
|
||||
/* \
|
||||
* ES5 future reserved keyword in strict mode, keyword in JS1.7 even when \
|
||||
* not strict, keyword inside function* in all versions. Punt logic to \
|
||||
* parser. \
|
||||
*/ \
|
||||
macro(yield, yield, TOK_YIELD, JSVERSION_DEFAULT) \
|
||||
/* Various conditional keywords. */ \
|
||||
FOR_CONST_KEYWORD(macro) \
|
||||
FOR_LET_KEYWORD(macro)
|
||||
|
|
Загрузка…
Ссылка в новой задаче