1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/frontend/Parser.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,7499 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* 1.11 + * JS parser. 1.12 + * 1.13 + * This is a recursive-descent parser for the JavaScript language specified by 1.14 + * "The JavaScript 1.5 Language Specification". It uses lexical and semantic 1.15 + * feedback to disambiguate non-LL(1) structures. It generates trees of nodes 1.16 + * induced by the recursive parsing (not precise syntax trees, see Parser.h). 1.17 + * After tree construction, it rewrites trees to fold constants and evaluate 1.18 + * compile-time expressions. 1.19 + * 1.20 + * This parser attempts no error recovery. 1.21 + */ 1.22 + 1.23 +#include "frontend/Parser-inl.h" 1.24 + 1.25 +#include "jsapi.h" 1.26 +#include "jsatom.h" 1.27 +#include "jscntxt.h" 1.28 +#include "jsfun.h" 1.29 +#include "jsobj.h" 1.30 +#include "jsopcode.h" 1.31 +#include "jsscript.h" 1.32 +#include "jstypes.h" 1.33 + 1.34 +#include "frontend/BytecodeCompiler.h" 1.35 +#include "frontend/FoldConstants.h" 1.36 +#include "frontend/ParseMaps.h" 1.37 +#include "frontend/TokenStream.h" 1.38 +#include "jit/AsmJS.h" 1.39 +#include "vm/Shape.h" 1.40 + 1.41 +#include "jsatominlines.h" 1.42 +#include "jsscriptinlines.h" 1.43 + 1.44 +#include "frontend/ParseNode-inl.h" 1.45 + 1.46 +using namespace js; 1.47 +using namespace js::gc; 1.48 +using mozilla::Maybe; 1.49 + 1.50 +namespace js { 1.51 +namespace frontend { 1.52 + 1.53 +typedef Rooted<StaticBlockObject*> RootedStaticBlockObject; 1.54 +typedef Handle<StaticBlockObject*> HandleStaticBlockObject; 1.55 +typedef Rooted<NestedScopeObject*> RootedNestedScopeObject; 1.56 +typedef Handle<NestedScopeObject*> HandleNestedScopeObject; 1.57 + 1.58 + 1.59 +/* 1.60 + * Insist that the next token be of type tt, or report errno and return null. 1.61 + * NB: this macro uses cx and ts from its lexical environment. 1.62 + */ 1.63 +#define MUST_MATCH_TOKEN(tt, errno) \ 1.64 + JS_BEGIN_MACRO \ 1.65 + if (tokenStream.getToken() != tt) { \ 1.66 + report(ParseError, false, null(), errno); \ 1.67 + return null(); \ 1.68 + } \ 1.69 + JS_END_MACRO 1.70 + 1.71 +static const unsigned BlockIdLimit = 1 << ParseNode::NumBlockIdBits; 1.72 + 1.73 +template <typename ParseHandler> 1.74 +bool 1.75 +GenerateBlockId(TokenStream &ts, ParseContext<ParseHandler> *pc, uint32_t &blockid) 1.76 +{ 1.77 + if (pc->blockidGen == BlockIdLimit) { 1.78 + ts.reportError(JSMSG_NEED_DIET, "program"); 1.79 + return false; 1.80 + } 1.81 + JS_ASSERT(pc->blockidGen < BlockIdLimit); 1.82 + blockid = pc->blockidGen++; 1.83 + return true; 1.84 +} 1.85 + 1.86 +template bool 1.87 +GenerateBlockId(TokenStream &ts, ParseContext<SyntaxParseHandler> *pc, uint32_t &blockid); 1.88 + 1.89 +template bool 1.90 +GenerateBlockId(TokenStream &ts, ParseContext<FullParseHandler> *pc, uint32_t &blockid); 1.91 + 1.92 +template <typename ParseHandler> 1.93 +static void 1.94 +PushStatementPC(ParseContext<ParseHandler> *pc, StmtInfoPC *stmt, StmtType type) 1.95 +{ 1.96 + stmt->blockid = pc->blockid(); 1.97 + PushStatement(pc, stmt, type); 1.98 +} 1.99 + 1.100 +// See comment on member function declaration. 1.101 +template <> 1.102 +bool 1.103 +ParseContext<FullParseHandler>::define(TokenStream &ts, 1.104 + HandlePropertyName name, ParseNode *pn, Definition::Kind kind) 1.105 +{ 1.106 + JS_ASSERT(!pn->isUsed()); 1.107 + JS_ASSERT_IF(pn->isDefn(), pn->isPlaceholder()); 1.108 + 1.109 + Definition *prevDef = nullptr; 1.110 + if (kind == Definition::LET) 1.111 + prevDef = decls_.lookupFirst(name); 1.112 + else 1.113 + JS_ASSERT(!decls_.lookupFirst(name)); 1.114 + 1.115 + if (!prevDef) 1.116 + prevDef = lexdeps.lookupDefn<FullParseHandler>(name); 1.117 + 1.118 + if (prevDef) { 1.119 + ParseNode **pnup = &prevDef->dn_uses; 1.120 + ParseNode *pnu; 1.121 + unsigned start = (kind == Definition::LET) ? pn->pn_blockid : bodyid; 1.122 + 1.123 + while ((pnu = *pnup) != nullptr && pnu->pn_blockid >= start) { 1.124 + JS_ASSERT(pnu->pn_blockid >= bodyid); 1.125 + JS_ASSERT(pnu->isUsed()); 1.126 + pnu->pn_lexdef = (Definition *) pn; 1.127 + pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS; 1.128 + pnup = &pnu->pn_link; 1.129 + } 1.130 + 1.131 + if (!pnu || pnu != prevDef->dn_uses) { 1.132 + *pnup = pn->dn_uses; 1.133 + pn->dn_uses = prevDef->dn_uses; 1.134 + prevDef->dn_uses = pnu; 1.135 + 1.136 + if (!pnu && prevDef->isPlaceholder()) 1.137 + lexdeps->remove(name); 1.138 + } 1.139 + 1.140 + pn->pn_dflags |= prevDef->pn_dflags & PND_CLOSED; 1.141 + } 1.142 + 1.143 + JS_ASSERT_IF(kind != Definition::LET, !lexdeps->lookup(name)); 1.144 + pn->setDefn(true); 1.145 + pn->pn_dflags &= ~PND_PLACEHOLDER; 1.146 + if (kind == Definition::CONST) 1.147 + pn->pn_dflags |= PND_CONST; 1.148 + 1.149 + Definition *dn = (Definition *)pn; 1.150 + switch (kind) { 1.151 + case Definition::ARG: 1.152 + JS_ASSERT(sc->isFunctionBox()); 1.153 + dn->setOp(JSOP_GETARG); 1.154 + dn->pn_dflags |= PND_BOUND; 1.155 + if (!dn->pn_cookie.set(ts, staticLevel, args_.length())) 1.156 + return false; 1.157 + if (!args_.append(dn)) 1.158 + return false; 1.159 + if (args_.length() >= ARGNO_LIMIT) { 1.160 + ts.reportError(JSMSG_TOO_MANY_FUN_ARGS); 1.161 + return false; 1.162 + } 1.163 + if (name == ts.names().empty) 1.164 + break; 1.165 + if (!decls_.addUnique(name, dn)) 1.166 + return false; 1.167 + break; 1.168 + 1.169 + case Definition::CONST: 1.170 + case Definition::VAR: 1.171 + if (sc->isFunctionBox()) { 1.172 + dn->setOp(JSOP_GETLOCAL); 1.173 + dn->pn_dflags |= PND_BOUND; 1.174 + if (!dn->pn_cookie.set(ts, staticLevel, vars_.length())) 1.175 + return false; 1.176 + if (!vars_.append(dn)) 1.177 + return false; 1.178 + if (vars_.length() >= LOCALNO_LIMIT) { 1.179 + ts.reportError(JSMSG_TOO_MANY_LOCALS); 1.180 + return false; 1.181 + } 1.182 + } 1.183 + if (!decls_.addUnique(name, dn)) 1.184 + return false; 1.185 + break; 1.186 + 1.187 + case Definition::LET: 1.188 + dn->setOp(JSOP_GETLOCAL); 1.189 + dn->pn_dflags |= (PND_LET | PND_BOUND); 1.190 + JS_ASSERT(dn->pn_cookie.level() == staticLevel); /* see bindLet */ 1.191 + if (!decls_.addShadow(name, dn)) 1.192 + return false; 1.193 + break; 1.194 + 1.195 + default: 1.196 + MOZ_ASSUME_UNREACHABLE("unexpected kind"); 1.197 + } 1.198 + 1.199 + return true; 1.200 +} 1.201 + 1.202 +template <> 1.203 +bool 1.204 +ParseContext<SyntaxParseHandler>::define(TokenStream &ts, HandlePropertyName name, Node pn, 1.205 + Definition::Kind kind) 1.206 +{ 1.207 + JS_ASSERT(!decls_.lookupFirst(name)); 1.208 + 1.209 + if (lexdeps.lookupDefn<SyntaxParseHandler>(name)) 1.210 + lexdeps->remove(name); 1.211 + 1.212 + // Keep track of the number of arguments in args_, for fun->nargs. 1.213 + if (kind == Definition::ARG) { 1.214 + if (!args_.append((Definition *) nullptr)) 1.215 + return false; 1.216 + if (args_.length() >= ARGNO_LIMIT) { 1.217 + ts.reportError(JSMSG_TOO_MANY_FUN_ARGS); 1.218 + return false; 1.219 + } 1.220 + } 1.221 + 1.222 + return decls_.addUnique(name, kind); 1.223 +} 1.224 + 1.225 +template <typename ParseHandler> 1.226 +void 1.227 +ParseContext<ParseHandler>::prepareToAddDuplicateArg(HandlePropertyName name, DefinitionNode prevDecl) 1.228 +{ 1.229 + JS_ASSERT(decls_.lookupFirst(name) == prevDecl); 1.230 + decls_.remove(name); 1.231 +} 1.232 + 1.233 +template <typename ParseHandler> 1.234 +void 1.235 +ParseContext<ParseHandler>::updateDecl(JSAtom *atom, Node pn) 1.236 +{ 1.237 + Definition *oldDecl = decls_.lookupFirst(atom); 1.238 + 1.239 + pn->setDefn(true); 1.240 + Definition *newDecl = (Definition *)pn; 1.241 + decls_.updateFirst(atom, newDecl); 1.242 + 1.243 + if (!sc->isFunctionBox()) { 1.244 + JS_ASSERT(newDecl->isFreeVar()); 1.245 + return; 1.246 + } 1.247 + 1.248 + JS_ASSERT(oldDecl->isBound()); 1.249 + JS_ASSERT(!oldDecl->pn_cookie.isFree()); 1.250 + newDecl->pn_cookie = oldDecl->pn_cookie; 1.251 + newDecl->pn_dflags |= PND_BOUND; 1.252 + if (IsArgOp(oldDecl->getOp())) { 1.253 + newDecl->setOp(JSOP_GETARG); 1.254 + JS_ASSERT(args_[oldDecl->pn_cookie.slot()] == oldDecl); 1.255 + args_[oldDecl->pn_cookie.slot()] = newDecl; 1.256 + } else { 1.257 + JS_ASSERT(IsLocalOp(oldDecl->getOp())); 1.258 + newDecl->setOp(JSOP_GETLOCAL); 1.259 + JS_ASSERT(vars_[oldDecl->pn_cookie.slot()] == oldDecl); 1.260 + vars_[oldDecl->pn_cookie.slot()] = newDecl; 1.261 + } 1.262 +} 1.263 + 1.264 +template <typename ParseHandler> 1.265 +void 1.266 +ParseContext<ParseHandler>::popLetDecl(JSAtom *atom) 1.267 +{ 1.268 + JS_ASSERT(ParseHandler::getDefinitionKind(decls_.lookupFirst(atom)) == Definition::LET); 1.269 + decls_.remove(atom); 1.270 +} 1.271 + 1.272 +template <typename ParseHandler> 1.273 +static void 1.274 +AppendPackedBindings(const ParseContext<ParseHandler> *pc, const DeclVector &vec, Binding *dst) 1.275 +{ 1.276 + for (size_t i = 0; i < vec.length(); ++i, ++dst) { 1.277 + Definition *dn = vec[i]; 1.278 + PropertyName *name = dn->name(); 1.279 + 1.280 + Binding::Kind kind; 1.281 + switch (dn->kind()) { 1.282 + case Definition::VAR: 1.283 + kind = Binding::VARIABLE; 1.284 + break; 1.285 + case Definition::CONST: 1.286 + kind = Binding::CONSTANT; 1.287 + break; 1.288 + case Definition::ARG: 1.289 + kind = Binding::ARGUMENT; 1.290 + break; 1.291 + default: 1.292 + MOZ_ASSUME_UNREACHABLE("unexpected dn->kind"); 1.293 + } 1.294 + 1.295 + /* 1.296 + * Bindings::init does not check for duplicates so we must ensure that 1.297 + * only one binding with a given name is marked aliased. pc->decls 1.298 + * maintains the canonical definition for each name, so use that. 1.299 + */ 1.300 + JS_ASSERT_IF(dn->isClosed(), pc->decls().lookupFirst(name) == dn); 1.301 + bool aliased = dn->isClosed() || 1.302 + (pc->sc->allLocalsAliased() && 1.303 + pc->decls().lookupFirst(name) == dn); 1.304 + 1.305 + *dst = Binding(name, kind, aliased); 1.306 + } 1.307 +} 1.308 + 1.309 +template <typename ParseHandler> 1.310 +bool 1.311 +ParseContext<ParseHandler>::generateFunctionBindings(ExclusiveContext *cx, TokenStream &ts, 1.312 + LifoAlloc &alloc, 1.313 + InternalHandle<Bindings*> bindings) const 1.314 +{ 1.315 + JS_ASSERT(sc->isFunctionBox()); 1.316 + JS_ASSERT(args_.length() < ARGNO_LIMIT); 1.317 + JS_ASSERT(vars_.length() < LOCALNO_LIMIT); 1.318 + 1.319 + /* 1.320 + * Avoid pathological edge cases by explicitly limiting the total number of 1.321 + * bindings to what will fit in a uint32_t. 1.322 + */ 1.323 + if (UINT32_MAX - args_.length() <= vars_.length()) 1.324 + return ts.reportError(JSMSG_TOO_MANY_LOCALS); 1.325 + 1.326 + uint32_t count = args_.length() + vars_.length(); 1.327 + Binding *packedBindings = alloc.newArrayUninitialized<Binding>(count); 1.328 + if (!packedBindings) { 1.329 + js_ReportOutOfMemory(cx); 1.330 + return false; 1.331 + } 1.332 + 1.333 + AppendPackedBindings(this, args_, packedBindings); 1.334 + AppendPackedBindings(this, vars_, packedBindings + args_.length()); 1.335 + 1.336 + return Bindings::initWithTemporaryStorage(cx, bindings, args_.length(), vars_.length(), 1.337 + packedBindings, blockScopeDepth); 1.338 +} 1.339 + 1.340 +template <typename ParseHandler> 1.341 +bool 1.342 +Parser<ParseHandler>::reportHelper(ParseReportKind kind, bool strict, uint32_t offset, 1.343 + unsigned errorNumber, va_list args) 1.344 +{ 1.345 + bool result = false; 1.346 + switch (kind) { 1.347 + case ParseError: 1.348 + result = tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_ERROR, errorNumber, args); 1.349 + break; 1.350 + case ParseWarning: 1.351 + result = 1.352 + tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_WARNING, errorNumber, args); 1.353 + break; 1.354 + case ParseExtraWarning: 1.355 + result = tokenStream.reportStrictWarningErrorNumberVA(offset, errorNumber, args); 1.356 + break; 1.357 + case ParseStrictError: 1.358 + result = tokenStream.reportStrictModeErrorNumberVA(offset, strict, errorNumber, args); 1.359 + break; 1.360 + } 1.361 + return result; 1.362 +} 1.363 + 1.364 +template <typename ParseHandler> 1.365 +bool 1.366 +Parser<ParseHandler>::report(ParseReportKind kind, bool strict, Node pn, unsigned errorNumber, ...) 1.367 +{ 1.368 + uint32_t offset = (pn ? handler.getPosition(pn) : pos()).begin; 1.369 + 1.370 + va_list args; 1.371 + va_start(args, errorNumber); 1.372 + bool result = reportHelper(kind, strict, offset, errorNumber, args); 1.373 + va_end(args); 1.374 + return result; 1.375 +} 1.376 + 1.377 +template <typename ParseHandler> 1.378 +bool 1.379 +Parser<ParseHandler>::reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...) 1.380 +{ 1.381 + va_list args; 1.382 + va_start(args, errorNumber); 1.383 + bool result = reportHelper(kind, strict, TokenStream::NoOffset, errorNumber, args); 1.384 + va_end(args); 1.385 + return result; 1.386 +} 1.387 + 1.388 +template <typename ParseHandler> 1.389 +bool 1.390 +Parser<ParseHandler>::reportWithOffset(ParseReportKind kind, bool strict, uint32_t offset, 1.391 + unsigned errorNumber, ...) 1.392 +{ 1.393 + va_list args; 1.394 + va_start(args, errorNumber); 1.395 + bool result = reportHelper(kind, strict, offset, errorNumber, args); 1.396 + va_end(args); 1.397 + return result; 1.398 +} 1.399 + 1.400 +template <> 1.401 +bool 1.402 +Parser<FullParseHandler>::abortIfSyntaxParser() 1.403 +{ 1.404 + handler.disableSyntaxParser(); 1.405 + return true; 1.406 +} 1.407 + 1.408 +template <> 1.409 +bool 1.410 +Parser<SyntaxParseHandler>::abortIfSyntaxParser() 1.411 +{ 1.412 + abortedSyntaxParse = true; 1.413 + return false; 1.414 +} 1.415 + 1.416 +template <typename ParseHandler> 1.417 +Parser<ParseHandler>::Parser(ExclusiveContext *cx, LifoAlloc *alloc, 1.418 + const ReadOnlyCompileOptions &options, 1.419 + const jschar *chars, size_t length, bool foldConstants, 1.420 + Parser<SyntaxParseHandler> *syntaxParser, 1.421 + LazyScript *lazyOuterFunction) 1.422 + : AutoGCRooter(cx, PARSER), 1.423 + context(cx), 1.424 + alloc(*alloc), 1.425 + tokenStream(cx, options, chars, length, thisForCtor()), 1.426 + traceListHead(nullptr), 1.427 + pc(nullptr), 1.428 + sct(nullptr), 1.429 + ss(nullptr), 1.430 + keepAtoms(cx->perThreadData), 1.431 + foldConstants(foldConstants), 1.432 + abortedSyntaxParse(false), 1.433 + isUnexpectedEOF_(false), 1.434 + handler(cx, *alloc, tokenStream, foldConstants, syntaxParser, lazyOuterFunction) 1.435 +{ 1.436 + { 1.437 + AutoLockForExclusiveAccess lock(cx); 1.438 + cx->perThreadData->addActiveCompilation(); 1.439 + } 1.440 + 1.441 + // The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings 1.442 + // which are not generated if functions are parsed lazily. Note that the 1.443 + // standard "use strict" does not inhibit lazy parsing. 1.444 + if (options.extraWarningsOption) 1.445 + handler.disableSyntaxParser(); 1.446 + 1.447 + tempPoolMark = alloc->mark(); 1.448 +} 1.449 + 1.450 +template <typename ParseHandler> 1.451 +Parser<ParseHandler>::~Parser() 1.452 +{ 1.453 + alloc.release(tempPoolMark); 1.454 + 1.455 + /* 1.456 + * The parser can allocate enormous amounts of memory for large functions. 1.457 + * Eagerly free the memory now (which otherwise won't be freed until the 1.458 + * next GC) to avoid unnecessary OOMs. 1.459 + */ 1.460 + alloc.freeAllIfHugeAndUnused(); 1.461 + 1.462 + { 1.463 + AutoLockForExclusiveAccess lock(context); 1.464 + context->perThreadData->removeActiveCompilation(); 1.465 + } 1.466 +} 1.467 + 1.468 +template <typename ParseHandler> 1.469 +ObjectBox * 1.470 +Parser<ParseHandler>::newObjectBox(JSObject *obj) 1.471 +{ 1.472 + JS_ASSERT(obj && !IsPoisonedPtr(obj)); 1.473 + 1.474 + /* 1.475 + * We use JSContext.tempLifoAlloc to allocate parsed objects and place them 1.476 + * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc 1.477 + * arenas containing the entries must be alive until we are done with 1.478 + * scanning, parsing and code generation for the whole script or top-level 1.479 + * function. 1.480 + */ 1.481 + 1.482 + ObjectBox *objbox = alloc.new_<ObjectBox>(obj, traceListHead); 1.483 + if (!objbox) { 1.484 + js_ReportOutOfMemory(context); 1.485 + return nullptr; 1.486 + } 1.487 + 1.488 + traceListHead = objbox; 1.489 + 1.490 + return objbox; 1.491 +} 1.492 + 1.493 +template <typename ParseHandler> 1.494 +FunctionBox::FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunction *fun, 1.495 + ParseContext<ParseHandler> *outerpc, Directives directives, 1.496 + bool extraWarnings, GeneratorKind generatorKind) 1.497 + : ObjectBox(fun, traceListHead), 1.498 + SharedContext(cx, directives, extraWarnings), 1.499 + bindings(), 1.500 + bufStart(0), 1.501 + bufEnd(0), 1.502 + length(0), 1.503 + generatorKindBits_(GeneratorKindAsBits(generatorKind)), 1.504 + inWith(false), // initialized below 1.505 + inGenexpLambda(false), 1.506 + hasDestructuringArgs(false), 1.507 + useAsm(directives.asmJS()), 1.508 + insideUseAsm(outerpc && outerpc->useAsmOrInsideUseAsm()), 1.509 + usesArguments(false), 1.510 + usesApply(false), 1.511 + funCxFlags() 1.512 +{ 1.513 + // Functions created at parse time may be set singleton after parsing and 1.514 + // baked into JIT code, so they must be allocated tenured. They are held by 1.515 + // the JSScript so cannot be collected during a minor GC anyway. 1.516 + JS_ASSERT(fun->isTenured()); 1.517 + 1.518 + if (!outerpc) { 1.519 + inWith = false; 1.520 + 1.521 + } else if (outerpc->parsingWith) { 1.522 + // This covers cases that don't involve eval(). For example: 1.523 + // 1.524 + // with (o) { (function() { g(); })(); } 1.525 + // 1.526 + // In this case, |outerpc| corresponds to global code, and 1.527 + // outerpc->parsingWith is true. 1.528 + inWith = true; 1.529 + 1.530 + } else if (outerpc->sc->isGlobalSharedContext()) { 1.531 + // This covers the case where a function is nested within an eval() 1.532 + // within a |with| statement. 1.533 + // 1.534 + // with (o) { eval("(function() { g(); })();"); } 1.535 + // 1.536 + // In this case, |outerpc| corresponds to the eval(), 1.537 + // outerpc->parsingWith is false because the eval() breaks the 1.538 + // ParseContext chain, and |parent| is nullptr (again because of the 1.539 + // eval(), so we have to look at |outerpc|'s scopeChain. 1.540 + // 1.541 + JSObject *scope = outerpc->sc->asGlobalSharedContext()->scopeChain(); 1.542 + while (scope) { 1.543 + if (scope->is<DynamicWithObject>()) 1.544 + inWith = true; 1.545 + scope = scope->enclosingScope(); 1.546 + } 1.547 + } else if (outerpc->sc->isFunctionBox()) { 1.548 + // This is like the above case, but for more deeply nested functions. 1.549 + // For example: 1.550 + // 1.551 + // with (o) { eval("(function() { (function() { g(); })(); })();"); } } 1.552 + // 1.553 + // In this case, the inner anonymous function needs to inherit the 1.554 + // setting of |inWith| from the outer one. 1.555 + FunctionBox *parent = outerpc->sc->asFunctionBox(); 1.556 + if (parent && parent->inWith) 1.557 + inWith = true; 1.558 + } 1.559 +} 1.560 + 1.561 +template <typename ParseHandler> 1.562 +FunctionBox * 1.563 +Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction *fun, ParseContext<ParseHandler> *outerpc, 1.564 + Directives inheritedDirectives, GeneratorKind generatorKind) 1.565 +{ 1.566 + JS_ASSERT(fun && !IsPoisonedPtr(fun)); 1.567 + 1.568 + /* 1.569 + * We use JSContext.tempLifoAlloc to allocate parsed objects and place them 1.570 + * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc 1.571 + * arenas containing the entries must be alive until we are done with 1.572 + * scanning, parsing and code generation for the whole script or top-level 1.573 + * function. 1.574 + */ 1.575 + FunctionBox *funbox = 1.576 + alloc.new_<FunctionBox>(context, traceListHead, fun, outerpc, 1.577 + inheritedDirectives, options().extraWarningsOption, 1.578 + generatorKind); 1.579 + if (!funbox) { 1.580 + js_ReportOutOfMemory(context); 1.581 + return nullptr; 1.582 + } 1.583 + 1.584 + traceListHead = funbox; 1.585 + if (fn) 1.586 + handler.setFunctionBox(fn, funbox); 1.587 + 1.588 + return funbox; 1.589 +} 1.590 + 1.591 +template <typename ParseHandler> 1.592 +void 1.593 +Parser<ParseHandler>::trace(JSTracer *trc) 1.594 +{ 1.595 + traceListHead->trace(trc); 1.596 +} 1.597 + 1.598 +void 1.599 +MarkParser(JSTracer *trc, AutoGCRooter *parser) 1.600 +{ 1.601 + static_cast<Parser<FullParseHandler> *>(parser)->trace(trc); 1.602 +} 1.603 + 1.604 +/* 1.605 + * Parse a top-level JS script. 1.606 + */ 1.607 +template <typename ParseHandler> 1.608 +typename ParseHandler::Node 1.609 +Parser<ParseHandler>::parse(JSObject *chain) 1.610 +{ 1.611 + /* 1.612 + * Protect atoms from being collected by a GC activation, which might 1.613 + * - nest on this thread due to out of memory (the so-called "last ditch" 1.614 + * GC attempted within js_NewGCThing), or 1.615 + * - run for any reason on another thread if this thread is suspended on 1.616 + * an object lock before it finishes generating bytecode into a script 1.617 + * protected from the GC by a root or a stack frame reference. 1.618 + */ 1.619 + Directives directives(options().strictOption); 1.620 + GlobalSharedContext globalsc(context, chain, directives, options().extraWarningsOption); 1.621 + ParseContext<ParseHandler> globalpc(this, /* parent = */ nullptr, ParseHandler::null(), 1.622 + &globalsc, /* newDirectives = */ nullptr, 1.623 + /* staticLevel = */ 0, /* bodyid = */ 0, 1.624 + /* blockScopeDepth = */ 0); 1.625 + if (!globalpc.init(tokenStream)) 1.626 + return null(); 1.627 + 1.628 + Node pn = statements(); 1.629 + if (pn) { 1.630 + if (!tokenStream.matchToken(TOK_EOF)) { 1.631 + report(ParseError, false, null(), JSMSG_SYNTAX_ERROR); 1.632 + return null(); 1.633 + } 1.634 + if (foldConstants) { 1.635 + if (!FoldConstants(context, &pn, this)) 1.636 + return null(); 1.637 + } 1.638 + } 1.639 + return pn; 1.640 +} 1.641 + 1.642 +/* 1.643 + * Insist on a final return before control flows out of pn. Try to be a bit 1.644 + * smart about loops: do {...; return e2;} while(0) at the end of a function 1.645 + * that contains an early return e1 will get a strict warning. Similarly for 1.646 + * iloops: while (true){...} is treated as though ... returns. 1.647 + */ 1.648 +enum { 1.649 + ENDS_IN_OTHER = 0, 1.650 + ENDS_IN_RETURN = 1, 1.651 + ENDS_IN_BREAK = 2 1.652 +}; 1.653 + 1.654 +static int 1.655 +HasFinalReturn(ParseNode *pn) 1.656 +{ 1.657 + ParseNode *pn2, *pn3; 1.658 + unsigned rv, rv2, hasDefault; 1.659 + 1.660 + switch (pn->getKind()) { 1.661 + case PNK_STATEMENTLIST: 1.662 + if (!pn->pn_head) 1.663 + return ENDS_IN_OTHER; 1.664 + return HasFinalReturn(pn->last()); 1.665 + 1.666 + case PNK_IF: 1.667 + if (!pn->pn_kid3) 1.668 + return ENDS_IN_OTHER; 1.669 + return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3); 1.670 + 1.671 + case PNK_WHILE: 1.672 + pn2 = pn->pn_left; 1.673 + if (pn2->isKind(PNK_TRUE)) 1.674 + return ENDS_IN_RETURN; 1.675 + if (pn2->isKind(PNK_NUMBER) && pn2->pn_dval) 1.676 + return ENDS_IN_RETURN; 1.677 + return ENDS_IN_OTHER; 1.678 + 1.679 + case PNK_DOWHILE: 1.680 + pn2 = pn->pn_right; 1.681 + if (pn2->isKind(PNK_FALSE)) 1.682 + return HasFinalReturn(pn->pn_left); 1.683 + if (pn2->isKind(PNK_TRUE)) 1.684 + return ENDS_IN_RETURN; 1.685 + if (pn2->isKind(PNK_NUMBER)) { 1.686 + if (pn2->pn_dval == 0) 1.687 + return HasFinalReturn(pn->pn_left); 1.688 + return ENDS_IN_RETURN; 1.689 + } 1.690 + return ENDS_IN_OTHER; 1.691 + 1.692 + case PNK_FOR: 1.693 + pn2 = pn->pn_left; 1.694 + if (pn2->isArity(PN_TERNARY) && !pn2->pn_kid2) 1.695 + return ENDS_IN_RETURN; 1.696 + return ENDS_IN_OTHER; 1.697 + 1.698 + case PNK_SWITCH: 1.699 + rv = ENDS_IN_RETURN; 1.700 + hasDefault = ENDS_IN_OTHER; 1.701 + pn2 = pn->pn_right; 1.702 + if (pn2->isKind(PNK_LEXICALSCOPE)) 1.703 + pn2 = pn2->expr(); 1.704 + for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) { 1.705 + if (pn2->isKind(PNK_DEFAULT)) 1.706 + hasDefault = ENDS_IN_RETURN; 1.707 + pn3 = pn2->pn_right; 1.708 + JS_ASSERT(pn3->isKind(PNK_STATEMENTLIST)); 1.709 + if (pn3->pn_head) { 1.710 + rv2 = HasFinalReturn(pn3->last()); 1.711 + if (rv2 == ENDS_IN_OTHER && pn2->pn_next) 1.712 + /* Falling through to next case or default. */; 1.713 + else 1.714 + rv &= rv2; 1.715 + } 1.716 + } 1.717 + /* If a final switch has no default case, we judge it harshly. */ 1.718 + rv &= hasDefault; 1.719 + return rv; 1.720 + 1.721 + case PNK_BREAK: 1.722 + return ENDS_IN_BREAK; 1.723 + 1.724 + case PNK_WITH: 1.725 + return HasFinalReturn(pn->pn_right); 1.726 + 1.727 + case PNK_RETURN: 1.728 + return ENDS_IN_RETURN; 1.729 + 1.730 + case PNK_COLON: 1.731 + case PNK_LEXICALSCOPE: 1.732 + return HasFinalReturn(pn->expr()); 1.733 + 1.734 + case PNK_THROW: 1.735 + return ENDS_IN_RETURN; 1.736 + 1.737 + case PNK_TRY: 1.738 + /* If we have a finally block that returns, we are done. */ 1.739 + if (pn->pn_kid3) { 1.740 + rv = HasFinalReturn(pn->pn_kid3); 1.741 + if (rv == ENDS_IN_RETURN) 1.742 + return rv; 1.743 + } 1.744 + 1.745 + /* Else check the try block and any and all catch statements. */ 1.746 + rv = HasFinalReturn(pn->pn_kid1); 1.747 + if (pn->pn_kid2) { 1.748 + JS_ASSERT(pn->pn_kid2->isArity(PN_LIST)); 1.749 + for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next) 1.750 + rv &= HasFinalReturn(pn2); 1.751 + } 1.752 + return rv; 1.753 + 1.754 + case PNK_CATCH: 1.755 + /* Check this catch block's body. */ 1.756 + return HasFinalReturn(pn->pn_kid3); 1.757 + 1.758 + case PNK_LET: 1.759 + /* Non-binary let statements are let declarations. */ 1.760 + if (!pn->isArity(PN_BINARY)) 1.761 + return ENDS_IN_OTHER; 1.762 + return HasFinalReturn(pn->pn_right); 1.763 + 1.764 + default: 1.765 + return ENDS_IN_OTHER; 1.766 + } 1.767 +} 1.768 + 1.769 +static int 1.770 +HasFinalReturn(SyntaxParseHandler::Node pn) 1.771 +{ 1.772 + return ENDS_IN_RETURN; 1.773 +} 1.774 + 1.775 +template <typename ParseHandler> 1.776 +bool 1.777 +Parser<ParseHandler>::reportBadReturn(Node pn, ParseReportKind kind, 1.778 + unsigned errnum, unsigned anonerrnum) 1.779 +{ 1.780 + JSAutoByteString name; 1.781 + JSAtom *atom = pc->sc->asFunctionBox()->function()->atom(); 1.782 + if (atom) { 1.783 + if (!AtomToPrintableString(context, atom, &name)) 1.784 + return false; 1.785 + } else { 1.786 + errnum = anonerrnum; 1.787 + } 1.788 + return report(kind, pc->sc->strict, pn, errnum, name.ptr()); 1.789 +} 1.790 + 1.791 +template <typename ParseHandler> 1.792 +bool 1.793 +Parser<ParseHandler>::checkFinalReturn(Node pn) 1.794 +{ 1.795 + JS_ASSERT(pc->sc->isFunctionBox()); 1.796 + return HasFinalReturn(pn) == ENDS_IN_RETURN || 1.797 + reportBadReturn(pn, ParseExtraWarning, 1.798 + JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE); 1.799 +} 1.800 + 1.801 +/* 1.802 + * Check that assigning to lhs is permitted. Assigning to 'eval' or 1.803 + * 'arguments' is banned in strict mode and in destructuring assignment. 1.804 + */ 1.805 +template <typename ParseHandler> 1.806 +bool 1.807 +Parser<ParseHandler>::checkStrictAssignment(Node lhs, AssignmentFlavor flavor) 1.808 +{ 1.809 + if (!pc->sc->needStrictChecks() && flavor != KeyedDestructuringAssignment) 1.810 + return true; 1.811 + 1.812 + JSAtom *atom = handler.isName(lhs); 1.813 + if (!atom) 1.814 + return true; 1.815 + 1.816 + if (atom == context->names().eval || atom == context->names().arguments) { 1.817 + JSAutoByteString name; 1.818 + if (!AtomToPrintableString(context, atom, &name)) 1.819 + return false; 1.820 + 1.821 + ParseReportKind kind; 1.822 + unsigned errnum; 1.823 + if (pc->sc->strict || flavor != KeyedDestructuringAssignment) { 1.824 + kind = ParseStrictError; 1.825 + errnum = JSMSG_BAD_STRICT_ASSIGN; 1.826 + } else { 1.827 + kind = ParseError; 1.828 + errnum = JSMSG_BAD_DESTRUCT_ASSIGN; 1.829 + } 1.830 + if (!report(kind, pc->sc->strict, lhs, errnum, name.ptr())) 1.831 + return false; 1.832 + } 1.833 + return true; 1.834 +} 1.835 + 1.836 +/* 1.837 + * Check that it is permitted to introduce a binding for atom. Strict mode 1.838 + * forbids introducing new definitions for 'eval', 'arguments', or for any 1.839 + * strict mode reserved keyword. Use pn for reporting error locations, or use 1.840 + * pc's token stream if pn is nullptr. 1.841 + */ 1.842 +template <typename ParseHandler> 1.843 +bool 1.844 +Parser<ParseHandler>::checkStrictBinding(PropertyName *name, Node pn) 1.845 +{ 1.846 + if (!pc->sc->needStrictChecks()) 1.847 + return true; 1.848 + 1.849 + if (name == context->names().eval || name == context->names().arguments || IsKeyword(name)) { 1.850 + JSAutoByteString bytes; 1.851 + if (!AtomToPrintableString(context, name, &bytes)) 1.852 + return false; 1.853 + return report(ParseStrictError, pc->sc->strict, pn, 1.854 + JSMSG_BAD_BINDING, bytes.ptr()); 1.855 + } 1.856 + 1.857 + return true; 1.858 +} 1.859 + 1.860 +template <> 1.861 +ParseNode * 1.862 +Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals, 1.863 + GeneratorKind generatorKind, 1.864 + Directives inheritedDirectives, 1.865 + Directives *newDirectives) 1.866 +{ 1.867 + Node fn = handler.newFunctionDefinition(); 1.868 + if (!fn) 1.869 + return null(); 1.870 + 1.871 + ParseNode *argsbody = ListNode::create(PNK_ARGSBODY, &handler); 1.872 + if (!argsbody) 1.873 + return null(); 1.874 + argsbody->setOp(JSOP_NOP); 1.875 + argsbody->makeEmpty(); 1.876 + fn->pn_body = argsbody; 1.877 + 1.878 + FunctionBox *funbox = newFunctionBox(fn, fun, /* outerpc = */ nullptr, inheritedDirectives, 1.879 + generatorKind); 1.880 + if (!funbox) 1.881 + return null(); 1.882 + funbox->length = fun->nargs() - fun->hasRest(); 1.883 + handler.setFunctionBox(fn, funbox); 1.884 + 1.885 + ParseContext<FullParseHandler> funpc(this, pc, fn, funbox, newDirectives, 1.886 + /* staticLevel = */ 0, /* bodyid = */ 0, 1.887 + /* blockScopeDepth = */ 0); 1.888 + if (!funpc.init(tokenStream)) 1.889 + return null(); 1.890 + 1.891 + for (unsigned i = 0; i < formals.length(); i++) { 1.892 + if (!defineArg(fn, formals[i])) 1.893 + return null(); 1.894 + } 1.895 + 1.896 + ParseNode *pn = functionBody(Statement, StatementListBody); 1.897 + if (!pn) 1.898 + return null(); 1.899 + 1.900 + if (!tokenStream.matchToken(TOK_EOF)) { 1.901 + report(ParseError, false, null(), JSMSG_SYNTAX_ERROR); 1.902 + return null(); 1.903 + } 1.904 + 1.905 + if (!FoldConstants(context, &pn, this)) 1.906 + return null(); 1.907 + 1.908 + InternalHandle<Bindings*> funboxBindings = 1.909 + InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings); 1.910 + if (!funpc.generateFunctionBindings(context, tokenStream, alloc, funboxBindings)) 1.911 + return null(); 1.912 + 1.913 + JS_ASSERT(fn->pn_body->isKind(PNK_ARGSBODY)); 1.914 + fn->pn_body->append(pn); 1.915 + fn->pn_body->pn_pos = pn->pn_pos; 1.916 + return fn; 1.917 +} 1.918 + 1.919 +template <> 1.920 +bool 1.921 +Parser<FullParseHandler>::checkFunctionArguments() 1.922 +{ 1.923 + /* 1.924 + * Non-top-level functions use JSOP_DEFFUN which is a dynamic scope 1.925 + * operation which means it aliases any bindings with the same name. 1.926 + */ 1.927 + if (FuncStmtSet *set = pc->funcStmts) { 1.928 + for (FuncStmtSet::Range r = set->all(); !r.empty(); r.popFront()) { 1.929 + PropertyName *name = r.front()->asPropertyName(); 1.930 + if (Definition *dn = pc->decls().lookupFirst(name)) 1.931 + dn->pn_dflags |= PND_CLOSED; 1.932 + } 1.933 + } 1.934 + 1.935 + /* Time to implement the odd semantics of 'arguments'. */ 1.936 + HandlePropertyName arguments = context->names().arguments; 1.937 + 1.938 + /* 1.939 + * As explained by the ContextFlags::funArgumentsHasLocalBinding comment, 1.940 + * create a declaration for 'arguments' if there are any unbound uses in 1.941 + * the function body. 1.942 + */ 1.943 + for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) { 1.944 + if (r.front().key() == arguments) { 1.945 + Definition *dn = r.front().value().get<FullParseHandler>(); 1.946 + pc->lexdeps->remove(arguments); 1.947 + dn->pn_dflags |= PND_IMPLICITARGUMENTS; 1.948 + if (!pc->define(tokenStream, arguments, dn, Definition::VAR)) 1.949 + return false; 1.950 + pc->sc->asFunctionBox()->usesArguments = true; 1.951 + break; 1.952 + } 1.953 + } 1.954 + 1.955 + /* 1.956 + * Report error if both rest parameters and 'arguments' are used. Do this 1.957 + * check before adding artificial 'arguments' below. 1.958 + */ 1.959 + Definition *maybeArgDef = pc->decls().lookupFirst(arguments); 1.960 + bool argumentsHasBinding = !!maybeArgDef; 1.961 + bool argumentsHasLocalBinding = maybeArgDef && maybeArgDef->kind() != Definition::ARG; 1.962 + bool hasRest = pc->sc->asFunctionBox()->function()->hasRest(); 1.963 + if (hasRest && argumentsHasLocalBinding) { 1.964 + report(ParseError, false, nullptr, JSMSG_ARGUMENTS_AND_REST); 1.965 + return false; 1.966 + } 1.967 + 1.968 + /* 1.969 + * Even if 'arguments' isn't explicitly mentioned, dynamic name lookup 1.970 + * forces an 'arguments' binding. The exception is that functions with rest 1.971 + * parameters are free from 'arguments'. 1.972 + */ 1.973 + if (!argumentsHasBinding && pc->sc->bindingsAccessedDynamically() && !hasRest) { 1.974 + ParseNode *pn = newName(arguments); 1.975 + if (!pn) 1.976 + return false; 1.977 + if (!pc->define(tokenStream, arguments, pn, Definition::VAR)) 1.978 + return false; 1.979 + argumentsHasBinding = true; 1.980 + argumentsHasLocalBinding = true; 1.981 + } 1.982 + 1.983 + /* 1.984 + * Now that all possible 'arguments' bindings have been added, note whether 1.985 + * 'arguments' has a local binding and whether it unconditionally needs an 1.986 + * arguments object. (Also see the flags' comments in ContextFlags.) 1.987 + */ 1.988 + if (argumentsHasLocalBinding) { 1.989 + FunctionBox *funbox = pc->sc->asFunctionBox(); 1.990 + funbox->setArgumentsHasLocalBinding(); 1.991 + 1.992 + /* 1.993 + * If a script has both explicit mentions of 'arguments' and dynamic 1.994 + * name lookups which could access the arguments, an arguments object 1.995 + * must be created eagerly. The SSA analysis used for lazy arguments 1.996 + * cannot cope with dynamic name accesses, so any 'arguments' accessed 1.997 + * via a NAME opcode must force construction of the arguments object. 1.998 + */ 1.999 + if (pc->sc->bindingsAccessedDynamically() && maybeArgDef) 1.1000 + funbox->setDefinitelyNeedsArgsObj(); 1.1001 + 1.1002 + /* 1.1003 + * If a script contains the debugger statement either directly or 1.1004 + * within an inner function, the arguments object must be created 1.1005 + * eagerly. The debugger can walk the scope chain and observe any 1.1006 + * values along it. 1.1007 + */ 1.1008 + if (pc->sc->hasDebuggerStatement()) 1.1009 + funbox->setDefinitelyNeedsArgsObj(); 1.1010 + 1.1011 + /* 1.1012 + * Check whether any parameters have been assigned within this 1.1013 + * function. In strict mode parameters do not alias arguments[i], and 1.1014 + * to make the arguments object reflect initial parameter values prior 1.1015 + * to any mutation we create it eagerly whenever parameters are (or 1.1016 + * might, in the case of calls to eval) be assigned. 1.1017 + */ 1.1018 + if (pc->sc->needStrictChecks()) { 1.1019 + for (AtomDefnListMap::Range r = pc->decls().all(); !r.empty(); r.popFront()) { 1.1020 + DefinitionList &dlist = r.front().value(); 1.1021 + for (DefinitionList::Range dr = dlist.all(); !dr.empty(); dr.popFront()) { 1.1022 + Definition *dn = dr.front<FullParseHandler>(); 1.1023 + if (dn->kind() == Definition::ARG && dn->isAssigned()) 1.1024 + funbox->setDefinitelyNeedsArgsObj(); 1.1025 + } 1.1026 + } 1.1027 + /* Watch for mutation of arguments through e.g. eval(). */ 1.1028 + if (pc->sc->bindingsAccessedDynamically()) 1.1029 + funbox->setDefinitelyNeedsArgsObj(); 1.1030 + } 1.1031 + } 1.1032 + 1.1033 + return true; 1.1034 +} 1.1035 + 1.1036 +template <> 1.1037 +bool 1.1038 +Parser<SyntaxParseHandler>::checkFunctionArguments() 1.1039 +{ 1.1040 + bool hasRest = pc->sc->asFunctionBox()->function()->hasRest(); 1.1041 + 1.1042 + if (pc->lexdeps->lookup(context->names().arguments)) { 1.1043 + pc->sc->asFunctionBox()->usesArguments = true; 1.1044 + if (hasRest) { 1.1045 + report(ParseError, false, null(), JSMSG_ARGUMENTS_AND_REST); 1.1046 + return false; 1.1047 + } 1.1048 + } else if (hasRest) { 1.1049 + DefinitionNode maybeArgDef = pc->decls().lookupFirst(context->names().arguments); 1.1050 + if (maybeArgDef && handler.getDefinitionKind(maybeArgDef) != Definition::ARG) { 1.1051 + report(ParseError, false, null(), JSMSG_ARGUMENTS_AND_REST); 1.1052 + return false; 1.1053 + } 1.1054 + } 1.1055 + 1.1056 + return true; 1.1057 +} 1.1058 + 1.1059 +template <typename ParseHandler> 1.1060 +typename ParseHandler::Node 1.1061 +Parser<ParseHandler>::functionBody(FunctionSyntaxKind kind, FunctionBodyType type) 1.1062 +{ 1.1063 + JS_ASSERT(pc->sc->isFunctionBox()); 1.1064 + JS_ASSERT(!pc->funHasReturnExpr && !pc->funHasReturnVoid); 1.1065 + 1.1066 +#ifdef DEBUG 1.1067 + uint32_t startYieldOffset = pc->lastYieldOffset; 1.1068 +#endif 1.1069 + 1.1070 + Node pn; 1.1071 + if (type == StatementListBody) { 1.1072 + pn = statements(); 1.1073 + if (!pn) 1.1074 + return null(); 1.1075 + } else { 1.1076 + JS_ASSERT(type == ExpressionBody); 1.1077 + JS_ASSERT(JS_HAS_EXPR_CLOSURES); 1.1078 + 1.1079 + Node kid = assignExpr(); 1.1080 + if (!kid) 1.1081 + return null(); 1.1082 + 1.1083 + pn = handler.newReturnStatement(kid, handler.getPosition(kid)); 1.1084 + if (!pn) 1.1085 + return null(); 1.1086 + } 1.1087 + 1.1088 + switch (pc->generatorKind()) { 1.1089 + case NotGenerator: 1.1090 + JS_ASSERT(pc->lastYieldOffset == startYieldOffset); 1.1091 + break; 1.1092 + 1.1093 + case LegacyGenerator: 1.1094 + // FIXME: Catch these errors eagerly, in yieldExpression(). 1.1095 + JS_ASSERT(pc->lastYieldOffset != startYieldOffset); 1.1096 + if (kind == Arrow) { 1.1097 + reportWithOffset(ParseError, false, pc->lastYieldOffset, 1.1098 + JSMSG_YIELD_IN_ARROW, js_yield_str); 1.1099 + return null(); 1.1100 + } 1.1101 + if (type == ExpressionBody) { 1.1102 + reportBadReturn(pn, ParseError, 1.1103 + JSMSG_BAD_GENERATOR_RETURN, 1.1104 + JSMSG_BAD_ANON_GENERATOR_RETURN); 1.1105 + return null(); 1.1106 + } 1.1107 + break; 1.1108 + 1.1109 + case StarGenerator: 1.1110 + JS_ASSERT(kind != Arrow); 1.1111 + JS_ASSERT(type == StatementListBody); 1.1112 + break; 1.1113 + } 1.1114 + 1.1115 + /* Check for falling off the end of a function that returns a value. */ 1.1116 + if (options().extraWarningsOption && pc->funHasReturnExpr && !checkFinalReturn(pn)) 1.1117 + return null(); 1.1118 + 1.1119 + /* Define the 'arguments' binding if necessary. */ 1.1120 + if (!checkFunctionArguments()) 1.1121 + return null(); 1.1122 + 1.1123 + return pn; 1.1124 +} 1.1125 + 1.1126 +/* See comment for use in Parser::functionDef. */ 1.1127 +template <> 1.1128 +bool 1.1129 +Parser<FullParseHandler>::makeDefIntoUse(Definition *dn, ParseNode *pn, JSAtom *atom) 1.1130 +{ 1.1131 + /* Turn pn into a definition. */ 1.1132 + pc->updateDecl(atom, pn); 1.1133 + 1.1134 + /* Change all uses of dn to be uses of pn. */ 1.1135 + for (ParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) { 1.1136 + JS_ASSERT(pnu->isUsed()); 1.1137 + JS_ASSERT(!pnu->isDefn()); 1.1138 + pnu->pn_lexdef = (Definition *) pn; 1.1139 + pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS; 1.1140 + } 1.1141 + pn->pn_dflags |= dn->pn_dflags & PND_USE2DEF_FLAGS; 1.1142 + pn->dn_uses = dn; 1.1143 + 1.1144 + /* 1.1145 + * A PNK_FUNCTION node must be a definition, so convert shadowed function 1.1146 + * statements into nops. This is valid since all body-level function 1.1147 + * statement initialization happens at the beginning of the function 1.1148 + * (thus, only the last statement's effect is visible). E.g., in 1.1149 + * 1.1150 + * function outer() { 1.1151 + * function g() { return 1 } 1.1152 + * assertEq(g(), 2); 1.1153 + * function g() { return 2 } 1.1154 + * assertEq(g(), 2); 1.1155 + * } 1.1156 + * 1.1157 + * both asserts are valid. 1.1158 + */ 1.1159 + if (dn->getKind() == PNK_FUNCTION) { 1.1160 + JS_ASSERT(dn->functionIsHoisted()); 1.1161 + pn->dn_uses = dn->pn_link; 1.1162 + handler.prepareNodeForMutation(dn); 1.1163 + dn->setKind(PNK_NOP); 1.1164 + dn->setArity(PN_NULLARY); 1.1165 + return true; 1.1166 + } 1.1167 + 1.1168 + /* 1.1169 + * If dn is arg, or in [var, const, let] and has an initializer, then we 1.1170 + * must rewrite it to be an assignment node, whose freshly allocated 1.1171 + * left-hand side becomes a use of pn. 1.1172 + */ 1.1173 + if (dn->canHaveInitializer()) { 1.1174 + if (ParseNode *rhs = dn->expr()) { 1.1175 + ParseNode *lhs = handler.makeAssignment(dn, rhs); 1.1176 + if (!lhs) 1.1177 + return false; 1.1178 + pn->dn_uses = lhs; 1.1179 + dn->pn_link = nullptr; 1.1180 + dn = (Definition *) lhs; 1.1181 + } 1.1182 + } 1.1183 + 1.1184 + /* Turn dn into a use of pn. */ 1.1185 + JS_ASSERT(dn->isKind(PNK_NAME)); 1.1186 + JS_ASSERT(dn->isArity(PN_NAME)); 1.1187 + JS_ASSERT(dn->pn_atom == atom); 1.1188 + dn->setOp((js_CodeSpec[dn->getOp()].format & JOF_SET) ? JSOP_SETNAME : JSOP_NAME); 1.1189 + dn->setDefn(false); 1.1190 + dn->setUsed(true); 1.1191 + dn->pn_lexdef = (Definition *) pn; 1.1192 + dn->pn_cookie.makeFree(); 1.1193 + dn->pn_dflags &= ~PND_BOUND; 1.1194 + return true; 1.1195 +} 1.1196 + 1.1197 +/* 1.1198 + * Parameter block types for the several Binder functions. We use a common 1.1199 + * helper function signature in order to share code among destructuring and 1.1200 + * simple variable declaration parsers. In the destructuring case, the binder 1.1201 + * function is called indirectly from the variable declaration parser by way 1.1202 + * of CheckDestructuring and its friends. 1.1203 + */ 1.1204 + 1.1205 +template <typename ParseHandler> 1.1206 +struct BindData 1.1207 +{ 1.1208 + BindData(ExclusiveContext *cx) : let(cx) {} 1.1209 + 1.1210 + typedef bool 1.1211 + (*Binder)(BindData *data, HandlePropertyName name, Parser<ParseHandler> *parser); 1.1212 + 1.1213 + /* name node for definition processing and error source coordinates */ 1.1214 + typename ParseHandler::Node pn; 1.1215 + 1.1216 + JSOp op; /* prolog bytecode or nop */ 1.1217 + Binder binder; /* binder, discriminates u */ 1.1218 + 1.1219 + struct LetData { 1.1220 + LetData(ExclusiveContext *cx) : blockObj(cx) {} 1.1221 + VarContext varContext; 1.1222 + RootedStaticBlockObject blockObj; 1.1223 + unsigned overflow; 1.1224 + } let; 1.1225 + 1.1226 + void initLet(VarContext varContext, StaticBlockObject &blockObj, unsigned overflow) { 1.1227 + this->pn = ParseHandler::null(); 1.1228 + this->op = JSOP_NOP; 1.1229 + this->binder = Parser<ParseHandler>::bindLet; 1.1230 + this->let.varContext = varContext; 1.1231 + this->let.blockObj = &blockObj; 1.1232 + this->let.overflow = overflow; 1.1233 + } 1.1234 + 1.1235 + void initVarOrConst(JSOp op) { 1.1236 + this->op = op; 1.1237 + this->binder = Parser<ParseHandler>::bindVarOrConst; 1.1238 + } 1.1239 +}; 1.1240 + 1.1241 +template <typename ParseHandler> 1.1242 +JSFunction * 1.1243 +Parser<ParseHandler>::newFunction(GenericParseContext *pc, HandleAtom atom, 1.1244 + FunctionSyntaxKind kind, JSObject *proto) 1.1245 +{ 1.1246 + JS_ASSERT_IF(kind == Statement, atom != nullptr); 1.1247 + 1.1248 + /* 1.1249 + * Find the global compilation context in order to pre-set the newborn 1.1250 + * function's parent slot to pc->sc->as<GlobalObject>()->scopeChain. If the 1.1251 + * global context is a compile-and-go one, we leave the pre-set parent 1.1252 + * intact; otherwise we clear parent and proto. 1.1253 + */ 1.1254 + while (pc->parent) 1.1255 + pc = pc->parent; 1.1256 + 1.1257 + RootedFunction fun(context); 1.1258 + JSFunction::Flags flags = (kind == Expression) 1.1259 + ? JSFunction::INTERPRETED_LAMBDA 1.1260 + : (kind == Arrow) 1.1261 + ? JSFunction::INTERPRETED_LAMBDA_ARROW 1.1262 + : JSFunction::INTERPRETED; 1.1263 + gc::AllocKind allocKind = JSFunction::FinalizeKind; 1.1264 + if (kind == Arrow) 1.1265 + allocKind = JSFunction::ExtendedFinalizeKind; 1.1266 + fun = NewFunctionWithProto(context, NullPtr(), nullptr, 0, flags, NullPtr(), atom, proto, 1.1267 + allocKind, MaybeSingletonObject); 1.1268 + if (!fun) 1.1269 + return nullptr; 1.1270 + if (options().selfHostingMode) 1.1271 + fun->setIsSelfHostedBuiltin(); 1.1272 + return fun; 1.1273 +} 1.1274 + 1.1275 +static bool 1.1276 +MatchOrInsertSemicolon(TokenStream &ts) 1.1277 +{ 1.1278 + TokenKind tt = ts.peekTokenSameLine(TokenStream::Operand); 1.1279 + if (tt == TOK_ERROR) 1.1280 + return false; 1.1281 + if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) { 1.1282 + /* Advance the scanner for proper error location reporting. */ 1.1283 + ts.getToken(TokenStream::Operand); 1.1284 + ts.reportError(JSMSG_SEMI_BEFORE_STMNT); 1.1285 + return false; 1.1286 + } 1.1287 + (void) ts.matchToken(TOK_SEMI); 1.1288 + return true; 1.1289 +} 1.1290 + 1.1291 +template <typename ParseHandler> 1.1292 +typename ParseHandler::DefinitionNode 1.1293 +Parser<ParseHandler>::getOrCreateLexicalDependency(ParseContext<ParseHandler> *pc, JSAtom *atom) 1.1294 +{ 1.1295 + AtomDefnAddPtr p = pc->lexdeps->lookupForAdd(atom); 1.1296 + if (p) 1.1297 + return p.value().get<ParseHandler>(); 1.1298 + 1.1299 + DefinitionNode dn = handler.newPlaceholder(atom, pc->blockid(), pos()); 1.1300 + if (!dn) 1.1301 + return ParseHandler::nullDefinition(); 1.1302 + DefinitionSingle def = DefinitionSingle::new_<ParseHandler>(dn); 1.1303 + if (!pc->lexdeps->add(p, atom, def)) 1.1304 + return ParseHandler::nullDefinition(); 1.1305 + return dn; 1.1306 +} 1.1307 + 1.1308 +static bool 1.1309 +ConvertDefinitionToNamedLambdaUse(TokenStream &ts, ParseContext<FullParseHandler> *pc, 1.1310 + FunctionBox *funbox, Definition *dn) 1.1311 +{ 1.1312 + dn->setOp(JSOP_CALLEE); 1.1313 + if (!dn->pn_cookie.set(ts, pc->staticLevel, 0)) 1.1314 + return false; 1.1315 + dn->pn_dflags |= PND_BOUND; 1.1316 + JS_ASSERT(dn->kind() == Definition::NAMED_LAMBDA); 1.1317 + 1.1318 + /* 1.1319 + * Since 'dn' is a placeholder, it has not been defined in the 1.1320 + * ParseContext and hence we must manually flag a closed-over 1.1321 + * callee name as needing a dynamic scope (this is done for all 1.1322 + * definitions in the ParseContext by generateFunctionBindings). 1.1323 + * 1.1324 + * If 'dn' has been assigned to, then we also flag the function 1.1325 + * scope has needing a dynamic scope so that dynamic scope 1.1326 + * setter can either ignore the set (in non-strict mode) or 1.1327 + * produce an error (in strict mode). 1.1328 + */ 1.1329 + if (dn->isClosed() || dn->isAssigned()) 1.1330 + funbox->setNeedsDeclEnvObject(); 1.1331 + return true; 1.1332 +} 1.1333 + 1.1334 +/* 1.1335 + * Beware: this function is called for functions nested in other functions or 1.1336 + * global scripts but not for functions compiled through the Function 1.1337 + * constructor or JSAPI. To always execute code when a function has finished 1.1338 + * parsing, use Parser::functionBody. 1.1339 + */ 1.1340 +template <> 1.1341 +bool 1.1342 +Parser<FullParseHandler>::leaveFunction(ParseNode *fn, ParseContext<FullParseHandler> *outerpc, 1.1343 + FunctionSyntaxKind kind) 1.1344 +{ 1.1345 + outerpc->blockidGen = pc->blockidGen; 1.1346 + 1.1347 + FunctionBox *funbox = fn->pn_funbox; 1.1348 + JS_ASSERT(funbox == pc->sc->asFunctionBox()); 1.1349 + 1.1350 + /* Propagate unresolved lexical names up to outerpc->lexdeps. */ 1.1351 + if (pc->lexdeps->count()) { 1.1352 + for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) { 1.1353 + JSAtom *atom = r.front().key(); 1.1354 + Definition *dn = r.front().value().get<FullParseHandler>(); 1.1355 + JS_ASSERT(dn->isPlaceholder()); 1.1356 + 1.1357 + if (atom == funbox->function()->name() && kind == Expression) { 1.1358 + if (!ConvertDefinitionToNamedLambdaUse(tokenStream, pc, funbox, dn)) 1.1359 + return false; 1.1360 + continue; 1.1361 + } 1.1362 + 1.1363 + Definition *outer_dn = outerpc->decls().lookupFirst(atom); 1.1364 + 1.1365 + /* 1.1366 + * Make sure to deoptimize lexical dependencies that are polluted 1.1367 + * by eval and function statements (which both flag the function as 1.1368 + * having an extensible scope) or any enclosing 'with'. 1.1369 + */ 1.1370 + if (funbox->hasExtensibleScope() || outerpc->parsingWith) 1.1371 + handler.deoptimizeUsesWithin(dn, fn->pn_pos); 1.1372 + 1.1373 + if (!outer_dn) { 1.1374 + /* 1.1375 + * Create a new placeholder for our outer lexdep. We could 1.1376 + * simply re-use the inner placeholder, but that introduces 1.1377 + * subtleties in the case where we find a later definition 1.1378 + * that captures an existing lexdep. For example: 1.1379 + * 1.1380 + * function f() { function g() { x; } let x; } 1.1381 + * 1.1382 + * Here, g's TOK_UPVARS node lists the placeholder for x, 1.1383 + * which must be captured by the 'let' declaration later, 1.1384 + * since 'let's are hoisted. Taking g's placeholder as our 1.1385 + * own would work fine. But consider: 1.1386 + * 1.1387 + * function f() { x; { function g() { x; } let x; } } 1.1388 + * 1.1389 + * Here, the 'let' must not capture all the uses of f's 1.1390 + * lexdep entry for x, but it must capture the x node 1.1391 + * referred to from g's TOK_UPVARS node. Always turning 1.1392 + * inherited lexdeps into uses of a new outer definition 1.1393 + * allows us to handle both these cases in a natural way. 1.1394 + */ 1.1395 + outer_dn = getOrCreateLexicalDependency(outerpc, atom); 1.1396 + if (!outer_dn) 1.1397 + return false; 1.1398 + } 1.1399 + 1.1400 + /* 1.1401 + * Insert dn's uses list at the front of outer_dn's list. 1.1402 + * 1.1403 + * Without loss of generality or correctness, we allow a dn to 1.1404 + * be in inner and outer lexdeps, since the purpose of lexdeps 1.1405 + * is one-pass coordination of name use and definition across 1.1406 + * functions, and if different dn's are used we'll merge lists 1.1407 + * when leaving the inner function. 1.1408 + * 1.1409 + * The dn == outer_dn case arises with generator expressions 1.1410 + * (see LegacyCompExprTransplanter::transplant, the PN_CODE/PN_NAME 1.1411 + * case), and nowhere else, currently. 1.1412 + */ 1.1413 + if (dn != outer_dn) { 1.1414 + if (ParseNode *pnu = dn->dn_uses) { 1.1415 + while (true) { 1.1416 + pnu->pn_lexdef = outer_dn; 1.1417 + if (!pnu->pn_link) 1.1418 + break; 1.1419 + pnu = pnu->pn_link; 1.1420 + } 1.1421 + pnu->pn_link = outer_dn->dn_uses; 1.1422 + outer_dn->dn_uses = dn->dn_uses; 1.1423 + dn->dn_uses = nullptr; 1.1424 + } 1.1425 + 1.1426 + outer_dn->pn_dflags |= dn->pn_dflags & ~PND_PLACEHOLDER; 1.1427 + } 1.1428 + 1.1429 + /* Mark the outer dn as escaping. */ 1.1430 + outer_dn->pn_dflags |= PND_CLOSED; 1.1431 + } 1.1432 + } 1.1433 + 1.1434 + InternalHandle<Bindings*> bindings = 1.1435 + InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings); 1.1436 + return pc->generateFunctionBindings(context, tokenStream, alloc, bindings); 1.1437 +} 1.1438 + 1.1439 +template <> 1.1440 +bool 1.1441 +Parser<SyntaxParseHandler>::leaveFunction(Node fn, ParseContext<SyntaxParseHandler> *outerpc, 1.1442 + FunctionSyntaxKind kind) 1.1443 +{ 1.1444 + outerpc->blockidGen = pc->blockidGen; 1.1445 + 1.1446 + FunctionBox *funbox = pc->sc->asFunctionBox(); 1.1447 + return addFreeVariablesFromLazyFunction(funbox->function(), outerpc); 1.1448 +} 1.1449 + 1.1450 +/* 1.1451 + * defineArg is called for both the arguments of a regular function definition 1.1452 + * and the arguments specified by the Function constructor. 1.1453 + * 1.1454 + * The 'disallowDuplicateArgs' bool indicates whether the use of another 1.1455 + * feature (destructuring or default arguments) disables duplicate arguments. 1.1456 + * (ECMA-262 requires us to support duplicate parameter names, but, for newer 1.1457 + * features, we consider the code to have "opted in" to higher standards and 1.1458 + * forbid duplicates.) 1.1459 + * 1.1460 + * If 'duplicatedArg' is non-null, then DefineArg assigns to it any previous 1.1461 + * argument with the same name. The caller may use this to report an error when 1.1462 + * one of the abovementioned features occurs after a duplicate. 1.1463 + */ 1.1464 +template <typename ParseHandler> 1.1465 +bool 1.1466 +Parser<ParseHandler>::defineArg(Node funcpn, HandlePropertyName name, 1.1467 + bool disallowDuplicateArgs, Node *duplicatedArg) 1.1468 +{ 1.1469 + SharedContext *sc = pc->sc; 1.1470 + 1.1471 + /* Handle duplicate argument names. */ 1.1472 + if (DefinitionNode prevDecl = pc->decls().lookupFirst(name)) { 1.1473 + Node pn = handler.getDefinitionNode(prevDecl); 1.1474 + 1.1475 + /* 1.1476 + * Strict-mode disallows duplicate args. We may not know whether we are 1.1477 + * in strict mode or not (since the function body hasn't been parsed). 1.1478 + * In such cases, report will queue up the potential error and return 1.1479 + * 'true'. 1.1480 + */ 1.1481 + if (sc->needStrictChecks()) { 1.1482 + JSAutoByteString bytes; 1.1483 + if (!AtomToPrintableString(context, name, &bytes)) 1.1484 + return false; 1.1485 + if (!report(ParseStrictError, pc->sc->strict, pn, 1.1486 + JSMSG_DUPLICATE_FORMAL, bytes.ptr())) 1.1487 + { 1.1488 + return false; 1.1489 + } 1.1490 + } 1.1491 + 1.1492 + if (disallowDuplicateArgs) { 1.1493 + report(ParseError, false, pn, JSMSG_BAD_DUP_ARGS); 1.1494 + return false; 1.1495 + } 1.1496 + 1.1497 + if (duplicatedArg) 1.1498 + *duplicatedArg = pn; 1.1499 + 1.1500 + /* ParseContext::define assumes and asserts prevDecl is not in decls. */ 1.1501 + JS_ASSERT(handler.getDefinitionKind(prevDecl) == Definition::ARG); 1.1502 + pc->prepareToAddDuplicateArg(name, prevDecl); 1.1503 + } 1.1504 + 1.1505 + Node argpn = newName(name); 1.1506 + if (!argpn) 1.1507 + return false; 1.1508 + 1.1509 + if (!checkStrictBinding(name, argpn)) 1.1510 + return false; 1.1511 + 1.1512 + handler.addFunctionArgument(funcpn, argpn); 1.1513 + return pc->define(tokenStream, name, argpn, Definition::ARG); 1.1514 +} 1.1515 + 1.1516 +template <typename ParseHandler> 1.1517 +/* static */ bool 1.1518 +Parser<ParseHandler>::bindDestructuringArg(BindData<ParseHandler> *data, 1.1519 + HandlePropertyName name, Parser<ParseHandler> *parser) 1.1520 +{ 1.1521 + ParseContext<ParseHandler> *pc = parser->pc; 1.1522 + JS_ASSERT(pc->sc->isFunctionBox()); 1.1523 + 1.1524 + if (pc->decls().lookupFirst(name)) { 1.1525 + parser->report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS); 1.1526 + return false; 1.1527 + } 1.1528 + 1.1529 + if (!parser->checkStrictBinding(name, data->pn)) 1.1530 + return false; 1.1531 + 1.1532 + return pc->define(parser->tokenStream, name, data->pn, Definition::VAR); 1.1533 +} 1.1534 + 1.1535 +template <typename ParseHandler> 1.1536 +bool 1.1537 +Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, Node funcpn, 1.1538 + bool *hasRest) 1.1539 +{ 1.1540 + FunctionBox *funbox = pc->sc->asFunctionBox(); 1.1541 + 1.1542 + *hasRest = false; 1.1543 + 1.1544 + bool parenFreeArrow = false; 1.1545 + if (kind == Arrow && tokenStream.peekToken() == TOK_NAME) { 1.1546 + parenFreeArrow = true; 1.1547 + } else { 1.1548 + if (tokenStream.getToken() != TOK_LP) { 1.1549 + report(ParseError, false, null(), 1.1550 + kind == Arrow ? JSMSG_BAD_ARROW_ARGS : JSMSG_PAREN_BEFORE_FORMAL); 1.1551 + return false; 1.1552 + } 1.1553 + 1.1554 + // Record the start of function source (for FunctionToString). If we 1.1555 + // are parenFreeArrow, we will set this below, after consuming the NAME. 1.1556 + funbox->setStart(tokenStream); 1.1557 + } 1.1558 + 1.1559 + Node argsbody = handler.newList(PNK_ARGSBODY); 1.1560 + if (!argsbody) 1.1561 + return false; 1.1562 + handler.setFunctionBody(funcpn, argsbody); 1.1563 + 1.1564 + if (parenFreeArrow || !tokenStream.matchToken(TOK_RP)) { 1.1565 + bool hasDefaults = false; 1.1566 + Node duplicatedArg = null(); 1.1567 + Node list = null(); 1.1568 + 1.1569 + do { 1.1570 + if (*hasRest) { 1.1571 + report(ParseError, false, null(), JSMSG_PARAMETER_AFTER_REST); 1.1572 + return false; 1.1573 + } 1.1574 + 1.1575 + TokenKind tt = tokenStream.getToken(); 1.1576 + JS_ASSERT_IF(parenFreeArrow, tt == TOK_NAME); 1.1577 + switch (tt) { 1.1578 + case TOK_LB: 1.1579 + case TOK_LC: 1.1580 + { 1.1581 + /* See comment below in the TOK_NAME case. */ 1.1582 + if (duplicatedArg) { 1.1583 + report(ParseError, false, duplicatedArg, JSMSG_BAD_DUP_ARGS); 1.1584 + return false; 1.1585 + } 1.1586 + 1.1587 + if (hasDefaults) { 1.1588 + report(ParseError, false, null(), JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT); 1.1589 + return false; 1.1590 + } 1.1591 + 1.1592 + funbox->hasDestructuringArgs = true; 1.1593 + 1.1594 + /* 1.1595 + * A destructuring formal parameter turns into one or more 1.1596 + * local variables initialized from properties of a single 1.1597 + * anonymous positional parameter, so here we must tweak our 1.1598 + * binder and its data. 1.1599 + */ 1.1600 + BindData<ParseHandler> data(context); 1.1601 + data.pn = ParseHandler::null(); 1.1602 + data.op = JSOP_DEFVAR; 1.1603 + data.binder = bindDestructuringArg; 1.1604 + Node lhs = destructuringExpr(&data, tt); 1.1605 + if (!lhs) 1.1606 + return false; 1.1607 + 1.1608 + /* 1.1609 + * Synthesize a destructuring assignment from the single 1.1610 + * anonymous positional parameter into the destructuring 1.1611 + * left-hand-side expression and accumulate it in list. 1.1612 + */ 1.1613 + HandlePropertyName name = context->names().empty; 1.1614 + Node rhs = newName(name); 1.1615 + if (!rhs) 1.1616 + return false; 1.1617 + 1.1618 + if (!pc->define(tokenStream, name, rhs, Definition::ARG)) 1.1619 + return false; 1.1620 + 1.1621 + Node item = handler.newBinary(PNK_ASSIGN, lhs, rhs); 1.1622 + if (!item) 1.1623 + return false; 1.1624 + if (list) { 1.1625 + handler.addList(list, item); 1.1626 + } else { 1.1627 + list = handler.newList(PNK_VAR, item); 1.1628 + if (!list) 1.1629 + return false; 1.1630 + *listp = list; 1.1631 + } 1.1632 + break; 1.1633 + } 1.1634 + 1.1635 + case TOK_YIELD: 1.1636 + if (!checkYieldNameValidity()) 1.1637 + return false; 1.1638 + goto TOK_NAME; 1.1639 + 1.1640 + case TOK_TRIPLEDOT: 1.1641 + { 1.1642 + *hasRest = true; 1.1643 + tt = tokenStream.getToken(); 1.1644 + if (tt != TOK_NAME) { 1.1645 + if (tt != TOK_ERROR) 1.1646 + report(ParseError, false, null(), JSMSG_NO_REST_NAME); 1.1647 + return false; 1.1648 + } 1.1649 + goto TOK_NAME; 1.1650 + } 1.1651 + 1.1652 + TOK_NAME: 1.1653 + case TOK_NAME: 1.1654 + { 1.1655 + if (parenFreeArrow) 1.1656 + funbox->setStart(tokenStream); 1.1657 + 1.1658 + RootedPropertyName name(context, tokenStream.currentName()); 1.1659 + bool disallowDuplicateArgs = funbox->hasDestructuringArgs || hasDefaults; 1.1660 + if (!defineArg(funcpn, name, disallowDuplicateArgs, &duplicatedArg)) 1.1661 + return false; 1.1662 + 1.1663 + if (tokenStream.matchToken(TOK_ASSIGN)) { 1.1664 + // A default argument without parentheses would look like: 1.1665 + // a = expr => body, but both operators are right-associative, so 1.1666 + // that would have been parsed as a = (expr => body) instead. 1.1667 + // Therefore it's impossible to get here with parenFreeArrow. 1.1668 + JS_ASSERT(!parenFreeArrow); 1.1669 + 1.1670 + if (*hasRest) { 1.1671 + report(ParseError, false, null(), JSMSG_REST_WITH_DEFAULT); 1.1672 + return false; 1.1673 + } 1.1674 + if (duplicatedArg) { 1.1675 + report(ParseError, false, duplicatedArg, JSMSG_BAD_DUP_ARGS); 1.1676 + return false; 1.1677 + } 1.1678 + if (!hasDefaults) { 1.1679 + hasDefaults = true; 1.1680 + 1.1681 + // The Function.length property is the number of formals 1.1682 + // before the first default argument. 1.1683 + funbox->length = pc->numArgs() - 1; 1.1684 + } 1.1685 + Node def_expr = assignExprWithoutYield(JSMSG_YIELD_IN_DEFAULT); 1.1686 + if (!def_expr) 1.1687 + return false; 1.1688 + handler.setLastFunctionArgumentDefault(funcpn, def_expr); 1.1689 + } 1.1690 + 1.1691 + break; 1.1692 + } 1.1693 + 1.1694 + default: 1.1695 + report(ParseError, false, null(), JSMSG_MISSING_FORMAL); 1.1696 + /* FALL THROUGH */ 1.1697 + case TOK_ERROR: 1.1698 + return false; 1.1699 + } 1.1700 + } while (!parenFreeArrow && tokenStream.matchToken(TOK_COMMA)); 1.1701 + 1.1702 + if (!parenFreeArrow && tokenStream.getToken() != TOK_RP) { 1.1703 + report(ParseError, false, null(), JSMSG_PAREN_AFTER_FORMAL); 1.1704 + return false; 1.1705 + } 1.1706 + 1.1707 + if (!hasDefaults) 1.1708 + funbox->length = pc->numArgs() - *hasRest; 1.1709 + } 1.1710 + 1.1711 + return true; 1.1712 +} 1.1713 + 1.1714 +template <> 1.1715 +bool 1.1716 +Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName, 1.1717 + ParseNode **pn_, FunctionSyntaxKind kind, 1.1718 + bool *pbodyProcessed) 1.1719 +{ 1.1720 + ParseNode *&pn = *pn_; 1.1721 + *pbodyProcessed = false; 1.1722 + 1.1723 + /* Function statements add a binding to the enclosing scope. */ 1.1724 + bool bodyLevel = pc->atBodyLevel(); 1.1725 + 1.1726 + if (kind == Statement) { 1.1727 + /* 1.1728 + * Handle redeclaration and optimize cases where we can statically bind the 1.1729 + * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup). 1.1730 + */ 1.1731 + if (Definition *dn = pc->decls().lookupFirst(funName)) { 1.1732 + JS_ASSERT(!dn->isUsed()); 1.1733 + JS_ASSERT(dn->isDefn()); 1.1734 + 1.1735 + if (options().extraWarningsOption || dn->kind() == Definition::CONST) { 1.1736 + JSAutoByteString name; 1.1737 + ParseReportKind reporter = (dn->kind() != Definition::CONST) 1.1738 + ? ParseExtraWarning 1.1739 + : ParseError; 1.1740 + if (!AtomToPrintableString(context, funName, &name) || 1.1741 + !report(reporter, false, nullptr, JSMSG_REDECLARED_VAR, 1.1742 + Definition::kindString(dn->kind()), name.ptr())) 1.1743 + { 1.1744 + return false; 1.1745 + } 1.1746 + } 1.1747 + 1.1748 + /* 1.1749 + * Body-level function statements are effectively variable 1.1750 + * declarations where the initialization is hoisted to the 1.1751 + * beginning of the block. This means that any other variable 1.1752 + * declaration with the same name is really just an assignment to 1.1753 + * the function's binding (which is mutable), so turn any existing 1.1754 + * declaration into a use. 1.1755 + */ 1.1756 + if (bodyLevel && !makeDefIntoUse(dn, pn, funName)) 1.1757 + return false; 1.1758 + } else if (bodyLevel) { 1.1759 + /* 1.1760 + * If this function was used before it was defined, claim the 1.1761 + * pre-created definition node for this function that primaryExpr 1.1762 + * put in pc->lexdeps on first forward reference, and recycle pn. 1.1763 + */ 1.1764 + if (Definition *fn = pc->lexdeps.lookupDefn<FullParseHandler>(funName)) { 1.1765 + JS_ASSERT(fn->isDefn()); 1.1766 + fn->setKind(PNK_FUNCTION); 1.1767 + fn->setArity(PN_CODE); 1.1768 + fn->pn_pos.begin = pn->pn_pos.begin; 1.1769 + fn->pn_pos.end = pn->pn_pos.end; 1.1770 + 1.1771 + fn->pn_body = nullptr; 1.1772 + fn->pn_cookie.makeFree(); 1.1773 + 1.1774 + pc->lexdeps->remove(funName); 1.1775 + handler.freeTree(pn); 1.1776 + pn = fn; 1.1777 + } 1.1778 + 1.1779 + if (!pc->define(tokenStream, funName, pn, Definition::VAR)) 1.1780 + return false; 1.1781 + } 1.1782 + 1.1783 + if (bodyLevel) { 1.1784 + JS_ASSERT(pn->functionIsHoisted()); 1.1785 + JS_ASSERT_IF(pc->sc->isFunctionBox(), !pn->pn_cookie.isFree()); 1.1786 + JS_ASSERT_IF(!pc->sc->isFunctionBox(), pn->pn_cookie.isFree()); 1.1787 + } else { 1.1788 + /* 1.1789 + * As a SpiderMonkey-specific extension, non-body-level function 1.1790 + * statements (e.g., functions in an "if" or "while" block) are 1.1791 + * dynamically bound when control flow reaches the statement. 1.1792 + */ 1.1793 + JS_ASSERT(!pc->sc->strict); 1.1794 + JS_ASSERT(pn->pn_cookie.isFree()); 1.1795 + if (pc->sc->isFunctionBox()) { 1.1796 + FunctionBox *funbox = pc->sc->asFunctionBox(); 1.1797 + funbox->setMightAliasLocals(); 1.1798 + funbox->setHasExtensibleScope(); 1.1799 + } 1.1800 + pn->setOp(JSOP_DEFFUN); 1.1801 + 1.1802 + /* 1.1803 + * Instead of setting bindingsAccessedDynamically, which would be 1.1804 + * overly conservative, remember the names of all function 1.1805 + * statements and mark any bindings with the same as aliased at the 1.1806 + * end of functionBody. 1.1807 + */ 1.1808 + if (!pc->funcStmts) { 1.1809 + pc->funcStmts = context->new_<FuncStmtSet>(context); 1.1810 + if (!pc->funcStmts || !pc->funcStmts->init()) 1.1811 + return false; 1.1812 + } 1.1813 + if (!pc->funcStmts->put(funName)) 1.1814 + return false; 1.1815 + 1.1816 + /* 1.1817 + * Due to the implicit declaration mechanism, 'arguments' will not 1.1818 + * have decls and, even if it did, they will not be noted as closed 1.1819 + * in the emitter. Thus, in the corner case of function statements 1.1820 + * overridding arguments, flag the whole scope as dynamic. 1.1821 + */ 1.1822 + if (funName == context->names().arguments) 1.1823 + pc->sc->setBindingsAccessedDynamically(); 1.1824 + } 1.1825 + 1.1826 + /* No further binding (in BindNameToSlot) is needed for functions. */ 1.1827 + pn->pn_dflags |= PND_BOUND; 1.1828 + } else { 1.1829 + /* A function expression does not introduce any binding. */ 1.1830 + pn->setOp(kind == Arrow ? JSOP_LAMBDA_ARROW : JSOP_LAMBDA); 1.1831 + } 1.1832 + 1.1833 + // When a lazily-parsed function is called, we only fully parse (and emit) 1.1834 + // that function, not any of its nested children. The initial syntax-only 1.1835 + // parse recorded the free variables of nested functions and their extents, 1.1836 + // so we can skip over them after accounting for their free variables. 1.1837 + if (LazyScript *lazyOuter = handler.lazyOuterFunction()) { 1.1838 + JSFunction *fun = handler.nextLazyInnerFunction(); 1.1839 + JS_ASSERT(!fun->isLegacyGenerator()); 1.1840 + FunctionBox *funbox = newFunctionBox(pn, fun, pc, Directives(/* strict = */ false), 1.1841 + fun->generatorKind()); 1.1842 + if (!funbox) 1.1843 + return false; 1.1844 + 1.1845 + if (!addFreeVariablesFromLazyFunction(fun, pc)) 1.1846 + return false; 1.1847 + 1.1848 + // The position passed to tokenStream.advance() is relative to 1.1849 + // userbuf.base() while LazyScript::{begin,end} offsets are relative to 1.1850 + // the outermost script source. N.B: userbuf.base() is initialized 1.1851 + // (in TokenStream()) to begin() - column() so that column numbers in 1.1852 + // the lazily parsed script are correct. 1.1853 + uint32_t userbufBase = lazyOuter->begin() - lazyOuter->column(); 1.1854 + tokenStream.advance(fun->lazyScript()->end() - userbufBase); 1.1855 + 1.1856 + *pbodyProcessed = true; 1.1857 + return true; 1.1858 + } 1.1859 + 1.1860 + return true; 1.1861 +} 1.1862 + 1.1863 +template <class T, class U> 1.1864 +static inline void 1.1865 +PropagateTransitiveParseFlags(const T *inner, U *outer) 1.1866 +{ 1.1867 + if (inner->bindingsAccessedDynamically()) 1.1868 + outer->setBindingsAccessedDynamically(); 1.1869 + if (inner->hasDebuggerStatement()) 1.1870 + outer->setHasDebuggerStatement(); 1.1871 +} 1.1872 + 1.1873 +template <typename ParseHandler> 1.1874 +bool 1.1875 +Parser<ParseHandler>::addFreeVariablesFromLazyFunction(JSFunction *fun, 1.1876 + ParseContext<ParseHandler> *pc) 1.1877 +{ 1.1878 + // Update any definition nodes in this context according to free variables 1.1879 + // in a lazily parsed inner function. 1.1880 + 1.1881 + LazyScript *lazy = fun->lazyScript(); 1.1882 + HeapPtrAtom *freeVariables = lazy->freeVariables(); 1.1883 + for (size_t i = 0; i < lazy->numFreeVariables(); i++) { 1.1884 + JSAtom *atom = freeVariables[i]; 1.1885 + 1.1886 + // 'arguments' will be implicitly bound within the inner function. 1.1887 + if (atom == context->names().arguments) 1.1888 + continue; 1.1889 + 1.1890 + DefinitionNode dn = pc->decls().lookupFirst(atom); 1.1891 + 1.1892 + if (!dn) { 1.1893 + dn = getOrCreateLexicalDependency(pc, atom); 1.1894 + if (!dn) 1.1895 + return false; 1.1896 + } 1.1897 + 1.1898 + /* Mark the outer dn as escaping. */ 1.1899 + handler.setFlag(handler.getDefinitionNode(dn), PND_CLOSED); 1.1900 + } 1.1901 + 1.1902 + PropagateTransitiveParseFlags(lazy, pc->sc); 1.1903 + return true; 1.1904 +} 1.1905 + 1.1906 +template <> 1.1907 +bool 1.1908 +Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName, 1.1909 + Node *pn, FunctionSyntaxKind kind, 1.1910 + bool *pbodyProcessed) 1.1911 +{ 1.1912 + *pbodyProcessed = false; 1.1913 + 1.1914 + /* Function statements add a binding to the enclosing scope. */ 1.1915 + bool bodyLevel = pc->atBodyLevel(); 1.1916 + 1.1917 + if (kind == Statement) { 1.1918 + /* 1.1919 + * Handle redeclaration and optimize cases where we can statically bind the 1.1920 + * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup). 1.1921 + */ 1.1922 + if (DefinitionNode dn = pc->decls().lookupFirst(funName)) { 1.1923 + if (dn == Definition::CONST) { 1.1924 + JSAutoByteString name; 1.1925 + if (!AtomToPrintableString(context, funName, &name) || 1.1926 + !report(ParseError, false, null(), JSMSG_REDECLARED_VAR, 1.1927 + Definition::kindString(dn), name.ptr())) 1.1928 + { 1.1929 + return false; 1.1930 + } 1.1931 + } 1.1932 + } else if (bodyLevel) { 1.1933 + if (pc->lexdeps.lookupDefn<SyntaxParseHandler>(funName)) 1.1934 + pc->lexdeps->remove(funName); 1.1935 + 1.1936 + if (!pc->define(tokenStream, funName, *pn, Definition::VAR)) 1.1937 + return false; 1.1938 + } 1.1939 + 1.1940 + if (!bodyLevel && funName == context->names().arguments) 1.1941 + pc->sc->setBindingsAccessedDynamically(); 1.1942 + } 1.1943 + 1.1944 + if (kind == Arrow) { 1.1945 + /* Arrow functions cannot yet be parsed lazily. */ 1.1946 + return abortIfSyntaxParser(); 1.1947 + } 1.1948 + 1.1949 + return true; 1.1950 +} 1.1951 + 1.1952 +template <typename ParseHandler> 1.1953 +typename ParseHandler::Node 1.1954 +Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream::Position &start, 1.1955 + FunctionType type, FunctionSyntaxKind kind, 1.1956 + GeneratorKind generatorKind) 1.1957 +{ 1.1958 + JS_ASSERT_IF(kind == Statement, funName); 1.1959 + 1.1960 + /* Make a TOK_FUNCTION node. */ 1.1961 + Node pn = handler.newFunctionDefinition(); 1.1962 + if (!pn) 1.1963 + return null(); 1.1964 + 1.1965 + bool bodyProcessed; 1.1966 + if (!checkFunctionDefinition(funName, &pn, kind, &bodyProcessed)) 1.1967 + return null(); 1.1968 + 1.1969 + if (bodyProcessed) 1.1970 + return pn; 1.1971 + 1.1972 + RootedObject proto(context); 1.1973 + if (generatorKind == StarGenerator) { 1.1974 + // If we are off the main thread, the generator meta-objects have 1.1975 + // already been created by js::StartOffThreadParseScript, so cx will not 1.1976 + // be necessary. 1.1977 + JSContext *cx = context->maybeJSContext(); 1.1978 + proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global()); 1.1979 + if (!proto) 1.1980 + return null(); 1.1981 + } 1.1982 + RootedFunction fun(context, newFunction(pc, funName, kind, proto)); 1.1983 + if (!fun) 1.1984 + return null(); 1.1985 + 1.1986 + // Speculatively parse using the directives of the parent parsing context. 1.1987 + // If a directive is encountered (e.g., "use strict") that changes how the 1.1988 + // function should have been parsed, we backup and reparse with the new set 1.1989 + // of directives. 1.1990 + Directives directives(pc); 1.1991 + Directives newDirectives = directives; 1.1992 + 1.1993 + while (true) { 1.1994 + if (functionArgsAndBody(pn, fun, type, kind, generatorKind, directives, &newDirectives)) 1.1995 + break; 1.1996 + if (tokenStream.hadError() || directives == newDirectives) 1.1997 + return null(); 1.1998 + 1.1999 + // Assignment must be monotonic to prevent reparsing iloops 1.2000 + JS_ASSERT_IF(directives.strict(), newDirectives.strict()); 1.2001 + JS_ASSERT_IF(directives.asmJS(), newDirectives.asmJS()); 1.2002 + directives = newDirectives; 1.2003 + 1.2004 + tokenStream.seek(start); 1.2005 + if (funName && tokenStream.getToken() == TOK_ERROR) 1.2006 + return null(); 1.2007 + 1.2008 + // functionArgsAndBody may have already set pn->pn_body before failing. 1.2009 + handler.setFunctionBody(pn, null()); 1.2010 + } 1.2011 + 1.2012 + return pn; 1.2013 +} 1.2014 + 1.2015 +template <> 1.2016 +bool 1.2017 +Parser<FullParseHandler>::finishFunctionDefinition(ParseNode *pn, FunctionBox *funbox, 1.2018 + ParseNode *prelude, ParseNode *body) 1.2019 +{ 1.2020 + pn->pn_pos.end = pos().end; 1.2021 + 1.2022 + /* 1.2023 + * If there were destructuring formal parameters, prepend the initializing 1.2024 + * comma expression that we synthesized to body. If the body is a return 1.2025 + * node, we must make a special PNK_SEQ node, to prepend the destructuring 1.2026 + * code without bracing the decompilation of the function body. 1.2027 + */ 1.2028 + if (prelude) { 1.2029 + if (!body->isArity(PN_LIST)) { 1.2030 + ParseNode *block; 1.2031 + 1.2032 + block = ListNode::create(PNK_SEQ, &handler); 1.2033 + if (!block) 1.2034 + return false; 1.2035 + block->pn_pos = body->pn_pos; 1.2036 + block->initList(body); 1.2037 + 1.2038 + body = block; 1.2039 + } 1.2040 + 1.2041 + ParseNode *item = UnaryNode::create(PNK_SEMI, &handler); 1.2042 + if (!item) 1.2043 + return false; 1.2044 + 1.2045 + item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin; 1.2046 + item->pn_kid = prelude; 1.2047 + item->pn_next = body->pn_head; 1.2048 + body->pn_head = item; 1.2049 + if (body->pn_tail == &body->pn_head) 1.2050 + body->pn_tail = &item->pn_next; 1.2051 + ++body->pn_count; 1.2052 + body->pn_xflags |= PNX_DESTRUCT; 1.2053 + } 1.2054 + 1.2055 + JS_ASSERT(pn->pn_funbox == funbox); 1.2056 + JS_ASSERT(pn->pn_body->isKind(PNK_ARGSBODY)); 1.2057 + pn->pn_body->append(body); 1.2058 + pn->pn_body->pn_pos = body->pn_pos; 1.2059 + 1.2060 + return true; 1.2061 +} 1.2062 + 1.2063 +template <> 1.2064 +bool 1.2065 +Parser<SyntaxParseHandler>::finishFunctionDefinition(Node pn, FunctionBox *funbox, 1.2066 + Node prelude, Node body) 1.2067 +{ 1.2068 + // The LazyScript for a lazily parsed function needs to be constructed 1.2069 + // while its ParseContext and associated lexdeps and inner functions are 1.2070 + // still available. 1.2071 + 1.2072 + if (funbox->inWith) 1.2073 + return abortIfSyntaxParser(); 1.2074 + 1.2075 + size_t numFreeVariables = pc->lexdeps->count(); 1.2076 + size_t numInnerFunctions = pc->innerFunctions.length(); 1.2077 + 1.2078 + RootedFunction fun(context, funbox->function()); 1.2079 + LazyScript *lazy = LazyScript::CreateRaw(context, fun, numFreeVariables, numInnerFunctions, 1.2080 + versionNumber(), funbox->bufStart, funbox->bufEnd, 1.2081 + funbox->startLine, funbox->startColumn); 1.2082 + if (!lazy) 1.2083 + return false; 1.2084 + 1.2085 + HeapPtrAtom *freeVariables = lazy->freeVariables(); 1.2086 + size_t i = 0; 1.2087 + for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) 1.2088 + freeVariables[i++].init(r.front().key()); 1.2089 + JS_ASSERT(i == numFreeVariables); 1.2090 + 1.2091 + HeapPtrFunction *innerFunctions = lazy->innerFunctions(); 1.2092 + for (size_t i = 0; i < numInnerFunctions; i++) 1.2093 + innerFunctions[i].init(pc->innerFunctions[i]); 1.2094 + 1.2095 + if (pc->sc->strict) 1.2096 + lazy->setStrict(); 1.2097 + lazy->setGeneratorKind(funbox->generatorKind()); 1.2098 + if (funbox->usesArguments && funbox->usesApply) 1.2099 + lazy->setUsesArgumentsAndApply(); 1.2100 + PropagateTransitiveParseFlags(funbox, lazy); 1.2101 + 1.2102 + fun->initLazyScript(lazy); 1.2103 + return true; 1.2104 +} 1.2105 + 1.2106 +template <> 1.2107 +bool 1.2108 +Parser<FullParseHandler>::functionArgsAndBody(ParseNode *pn, HandleFunction fun, 1.2109 + FunctionType type, FunctionSyntaxKind kind, 1.2110 + GeneratorKind generatorKind, 1.2111 + Directives inheritedDirectives, 1.2112 + Directives *newDirectives) 1.2113 +{ 1.2114 + ParseContext<FullParseHandler> *outerpc = pc; 1.2115 + 1.2116 + // Create box for fun->object early to protect against last-ditch GC. 1.2117 + FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives, generatorKind); 1.2118 + if (!funbox) 1.2119 + return false; 1.2120 + 1.2121 + // Try a syntax parse for this inner function. 1.2122 + do { 1.2123 + Parser<SyntaxParseHandler> *parser = handler.syntaxParser; 1.2124 + if (!parser) 1.2125 + break; 1.2126 + 1.2127 + { 1.2128 + // Move the syntax parser to the current position in the stream. 1.2129 + TokenStream::Position position(keepAtoms); 1.2130 + tokenStream.tell(&position); 1.2131 + if (!parser->tokenStream.seek(position, tokenStream)) 1.2132 + return false; 1.2133 + 1.2134 + ParseContext<SyntaxParseHandler> funpc(parser, outerpc, SyntaxParseHandler::null(), funbox, 1.2135 + newDirectives, outerpc->staticLevel + 1, 1.2136 + outerpc->blockidGen, 1.2137 + /* blockScopeDepth = */ 0); 1.2138 + if (!funpc.init(tokenStream)) 1.2139 + return false; 1.2140 + 1.2141 + if (!parser->functionArgsAndBodyGeneric(SyntaxParseHandler::NodeGeneric, 1.2142 + fun, type, kind, newDirectives)) 1.2143 + { 1.2144 + if (parser->hadAbortedSyntaxParse()) { 1.2145 + // Try again with a full parse. 1.2146 + parser->clearAbortedSyntaxParse(); 1.2147 + break; 1.2148 + } 1.2149 + return false; 1.2150 + } 1.2151 + 1.2152 + outerpc->blockidGen = funpc.blockidGen; 1.2153 + 1.2154 + // Advance this parser over tokens processed by the syntax parser. 1.2155 + parser->tokenStream.tell(&position); 1.2156 + if (!tokenStream.seek(position, parser->tokenStream)) 1.2157 + return false; 1.2158 + 1.2159 + // Update the end position of the parse node. 1.2160 + pn->pn_pos.end = tokenStream.currentToken().pos.end; 1.2161 + } 1.2162 + 1.2163 + if (!addFreeVariablesFromLazyFunction(fun, pc)) 1.2164 + return false; 1.2165 + 1.2166 + pn->pn_blockid = outerpc->blockid(); 1.2167 + PropagateTransitiveParseFlags(funbox, outerpc->sc); 1.2168 + return true; 1.2169 + } while (false); 1.2170 + 1.2171 + // Continue doing a full parse for this inner function. 1.2172 + ParseContext<FullParseHandler> funpc(this, pc, pn, funbox, newDirectives, 1.2173 + outerpc->staticLevel + 1, outerpc->blockidGen, 1.2174 + /* blockScopeDepth = */ 0); 1.2175 + if (!funpc.init(tokenStream)) 1.2176 + return false; 1.2177 + 1.2178 + if (!functionArgsAndBodyGeneric(pn, fun, type, kind, newDirectives)) 1.2179 + return false; 1.2180 + 1.2181 + if (!leaveFunction(pn, outerpc, kind)) 1.2182 + return false; 1.2183 + 1.2184 + pn->pn_blockid = outerpc->blockid(); 1.2185 + 1.2186 + /* 1.2187 + * Fruit of the poisonous tree: if a closure contains a dynamic name access 1.2188 + * (eval, with, etc), we consider the parent to do the same. The reason is 1.2189 + * that the deoptimizing effects of dynamic name access apply equally to 1.2190 + * parents: any local can be read at runtime. 1.2191 + */ 1.2192 + PropagateTransitiveParseFlags(funbox, outerpc->sc); 1.2193 + return true; 1.2194 +} 1.2195 + 1.2196 +template <> 1.2197 +bool 1.2198 +Parser<SyntaxParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun, 1.2199 + FunctionType type, FunctionSyntaxKind kind, 1.2200 + GeneratorKind generatorKind, 1.2201 + Directives inheritedDirectives, 1.2202 + Directives *newDirectives) 1.2203 +{ 1.2204 + ParseContext<SyntaxParseHandler> *outerpc = pc; 1.2205 + 1.2206 + // Create box for fun->object early to protect against last-ditch GC. 1.2207 + FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives, generatorKind); 1.2208 + if (!funbox) 1.2209 + return false; 1.2210 + 1.2211 + // Initialize early for possible flags mutation via destructuringExpr. 1.2212 + ParseContext<SyntaxParseHandler> funpc(this, pc, handler.null(), funbox, newDirectives, 1.2213 + outerpc->staticLevel + 1, outerpc->blockidGen, 1.2214 + /* blockScopeDepth = */ 0); 1.2215 + if (!funpc.init(tokenStream)) 1.2216 + return false; 1.2217 + 1.2218 + if (!functionArgsAndBodyGeneric(pn, fun, type, kind, newDirectives)) 1.2219 + return false; 1.2220 + 1.2221 + if (!leaveFunction(pn, outerpc, kind)) 1.2222 + return false; 1.2223 + 1.2224 + // This is a lazy function inner to another lazy function. Remember the 1.2225 + // inner function so that if the outer function is eventually parsed we do 1.2226 + // not need any further parsing or processing of the inner function. 1.2227 + JS_ASSERT(fun->lazyScript()); 1.2228 + return outerpc->innerFunctions.append(fun); 1.2229 +} 1.2230 + 1.2231 +template <> 1.2232 +ParseNode * 1.2233 +Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, unsigned staticLevel, 1.2234 + bool strict, GeneratorKind generatorKind) 1.2235 +{ 1.2236 + Node pn = handler.newFunctionDefinition(); 1.2237 + if (!pn) 1.2238 + return null(); 1.2239 + 1.2240 + Directives directives(/* strict = */ strict); 1.2241 + FunctionBox *funbox = newFunctionBox(pn, fun, /* outerpc = */ nullptr, directives, 1.2242 + generatorKind); 1.2243 + if (!funbox) 1.2244 + return null(); 1.2245 + funbox->length = fun->nargs() - fun->hasRest(); 1.2246 + 1.2247 + Directives newDirectives = directives; 1.2248 + ParseContext<FullParseHandler> funpc(this, /* parent = */ nullptr, pn, funbox, 1.2249 + &newDirectives, staticLevel, /* bodyid = */ 0, 1.2250 + /* blockScopeDepth = */ 0); 1.2251 + if (!funpc.init(tokenStream)) 1.2252 + return null(); 1.2253 + 1.2254 + if (!functionArgsAndBodyGeneric(pn, fun, Normal, Statement, &newDirectives)) { 1.2255 + JS_ASSERT(directives == newDirectives); 1.2256 + return null(); 1.2257 + } 1.2258 + 1.2259 + if (fun->isNamedLambda()) { 1.2260 + if (AtomDefnPtr p = pc->lexdeps->lookup(fun->name())) { 1.2261 + Definition *dn = p.value().get<FullParseHandler>(); 1.2262 + if (!ConvertDefinitionToNamedLambdaUse(tokenStream, pc, funbox, dn)) 1.2263 + return nullptr; 1.2264 + } 1.2265 + } 1.2266 + 1.2267 + InternalHandle<Bindings*> bindings = 1.2268 + InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings); 1.2269 + if (!pc->generateFunctionBindings(context, tokenStream, alloc, bindings)) 1.2270 + return null(); 1.2271 + 1.2272 + if (!FoldConstants(context, &pn, this)) 1.2273 + return null(); 1.2274 + 1.2275 + return pn; 1.2276 +} 1.2277 + 1.2278 +template <typename ParseHandler> 1.2279 +bool 1.2280 +Parser<ParseHandler>::functionArgsAndBodyGeneric(Node pn, HandleFunction fun, FunctionType type, 1.2281 + FunctionSyntaxKind kind, 1.2282 + Directives *newDirectives) 1.2283 +{ 1.2284 + // Given a properly initialized parse context, try to parse an actual 1.2285 + // function without concern for conversion to strict mode, use of lazy 1.2286 + // parsing and such. 1.2287 + 1.2288 + Node prelude = null(); 1.2289 + bool hasRest; 1.2290 + if (!functionArguments(kind, &prelude, pn, &hasRest)) 1.2291 + return false; 1.2292 + 1.2293 + FunctionBox *funbox = pc->sc->asFunctionBox(); 1.2294 + 1.2295 + fun->setArgCount(pc->numArgs()); 1.2296 + if (hasRest) 1.2297 + fun->setHasRest(); 1.2298 + 1.2299 + if (type == Getter && fun->nargs() > 0) { 1.2300 + report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "getter", "no", "s"); 1.2301 + return false; 1.2302 + } 1.2303 + if (type == Setter && fun->nargs() != 1) { 1.2304 + report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", ""); 1.2305 + return false; 1.2306 + } 1.2307 + 1.2308 + if (kind == Arrow && !tokenStream.matchToken(TOK_ARROW)) { 1.2309 + report(ParseError, false, null(), JSMSG_BAD_ARROW_ARGS); 1.2310 + return false; 1.2311 + } 1.2312 + 1.2313 + // Parse the function body. 1.2314 + FunctionBodyType bodyType = StatementListBody; 1.2315 + if (tokenStream.getToken(TokenStream::Operand) != TOK_LC) { 1.2316 + if (funbox->isStarGenerator()) { 1.2317 + report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY); 1.2318 + return false; 1.2319 + } 1.2320 + tokenStream.ungetToken(); 1.2321 + bodyType = ExpressionBody; 1.2322 + fun->setIsExprClosure(); 1.2323 + } 1.2324 + 1.2325 + Node body = functionBody(kind, bodyType); 1.2326 + if (!body) 1.2327 + return false; 1.2328 + 1.2329 + if (fun->name() && !checkStrictBinding(fun->name(), pn)) 1.2330 + return false; 1.2331 + 1.2332 +#if JS_HAS_EXPR_CLOSURES 1.2333 + if (bodyType == StatementListBody) { 1.2334 +#endif 1.2335 + if (!tokenStream.matchToken(TOK_RC)) { 1.2336 + report(ParseError, false, null(), JSMSG_CURLY_AFTER_BODY); 1.2337 + return false; 1.2338 + } 1.2339 + funbox->bufEnd = pos().begin + 1; 1.2340 +#if JS_HAS_EXPR_CLOSURES 1.2341 + } else { 1.2342 + if (tokenStream.hadError()) 1.2343 + return false; 1.2344 + funbox->bufEnd = pos().end; 1.2345 + if (kind == Statement && !MatchOrInsertSemicolon(tokenStream)) 1.2346 + return false; 1.2347 + } 1.2348 +#endif 1.2349 + 1.2350 + return finishFunctionDefinition(pn, funbox, prelude, body); 1.2351 +} 1.2352 + 1.2353 +template <typename ParseHandler> 1.2354 +bool 1.2355 +Parser<ParseHandler>::checkYieldNameValidity() 1.2356 +{ 1.2357 + // In star generators and in JS >= 1.7, yield is a keyword. Otherwise in 1.2358 + // strict mode, yield is a future reserved word. 1.2359 + if (pc->isStarGenerator() || versionNumber() >= JSVERSION_1_7 || pc->sc->strict) { 1.2360 + report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield"); 1.2361 + return false; 1.2362 + } 1.2363 + return true; 1.2364 +} 1.2365 + 1.2366 +template <typename ParseHandler> 1.2367 +typename ParseHandler::Node 1.2368 +Parser<ParseHandler>::functionStmt() 1.2369 +{ 1.2370 + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); 1.2371 + 1.2372 + TokenStream::Position start(keepAtoms); 1.2373 + tokenStream.tell(&start); 1.2374 + 1.2375 + RootedPropertyName name(context); 1.2376 + GeneratorKind generatorKind = NotGenerator; 1.2377 + TokenKind tt = tokenStream.getToken(); 1.2378 + 1.2379 + if (tt == TOK_MUL) { 1.2380 + tokenStream.tell(&start); 1.2381 + tt = tokenStream.getToken(); 1.2382 + generatorKind = StarGenerator; 1.2383 + } 1.2384 + 1.2385 + if (tt == TOK_NAME) { 1.2386 + name = tokenStream.currentName(); 1.2387 + } else if (tt == TOK_YIELD) { 1.2388 + if (!checkYieldNameValidity()) 1.2389 + return null(); 1.2390 + name = tokenStream.currentName(); 1.2391 + } else { 1.2392 + /* Unnamed function expressions are forbidden in statement context. */ 1.2393 + report(ParseError, false, null(), JSMSG_UNNAMED_FUNCTION_STMT); 1.2394 + return null(); 1.2395 + } 1.2396 + 1.2397 + /* We forbid function statements in strict mode code. */ 1.2398 + if (!pc->atBodyLevel() && pc->sc->needStrictChecks() && 1.2399 + !report(ParseStrictError, pc->sc->strict, null(), JSMSG_STRICT_FUNCTION_STATEMENT)) 1.2400 + return null(); 1.2401 + 1.2402 + return functionDef(name, start, Normal, Statement, generatorKind); 1.2403 +} 1.2404 + 1.2405 +template <typename ParseHandler> 1.2406 +typename ParseHandler::Node 1.2407 +Parser<ParseHandler>::functionExpr() 1.2408 +{ 1.2409 + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); 1.2410 + 1.2411 + TokenStream::Position start(keepAtoms); 1.2412 + tokenStream.tell(&start); 1.2413 + 1.2414 + GeneratorKind generatorKind = NotGenerator; 1.2415 + TokenKind tt = tokenStream.getToken(); 1.2416 + 1.2417 + if (tt == TOK_MUL) { 1.2418 + tokenStream.tell(&start); 1.2419 + tt = tokenStream.getToken(); 1.2420 + generatorKind = StarGenerator; 1.2421 + } 1.2422 + 1.2423 + RootedPropertyName name(context); 1.2424 + if (tt == TOK_NAME) { 1.2425 + name = tokenStream.currentName(); 1.2426 + } else if (tt == TOK_YIELD) { 1.2427 + if (!checkYieldNameValidity()) 1.2428 + return null(); 1.2429 + name = tokenStream.currentName(); 1.2430 + } else { 1.2431 + tokenStream.ungetToken(); 1.2432 + } 1.2433 + 1.2434 + return functionDef(name, start, Normal, Expression, generatorKind); 1.2435 +} 1.2436 + 1.2437 +/* 1.2438 + * Return true if this node, known to be an unparenthesized string literal, 1.2439 + * could be the string of a directive in a Directive Prologue. Directive 1.2440 + * strings never contain escape sequences or line continuations. 1.2441 + * isEscapeFreeStringLiteral, below, checks whether the node itself could be 1.2442 + * a directive. 1.2443 + */ 1.2444 +static inline bool 1.2445 +IsEscapeFreeStringLiteral(const TokenPos &pos, JSAtom *str) 1.2446 +{ 1.2447 + /* 1.2448 + * If the string's length in the source code is its length as a value, 1.2449 + * accounting for the quotes, then it must not contain any escape 1.2450 + * sequences or line continuations. 1.2451 + */ 1.2452 + return pos.begin + str->length() + 2 == pos.end; 1.2453 +} 1.2454 + 1.2455 +template <> 1.2456 +bool 1.2457 +Parser<SyntaxParseHandler>::asmJS(Node list) 1.2458 +{ 1.2459 + // While asm.js could technically be validated and compiled during syntax 1.2460 + // parsing, we have no guarantee that some later JS wouldn't abort the 1.2461 + // syntax parse and cause us to re-parse (and re-compile) the asm.js module. 1.2462 + // For simplicity, unconditionally abort the syntax parse when "use asm" is 1.2463 + // encountered so that asm.js is always validated/compiled exactly once 1.2464 + // during a full parse. 1.2465 + JS_ALWAYS_FALSE(abortIfSyntaxParser()); 1.2466 + return false; 1.2467 +} 1.2468 + 1.2469 +template <> 1.2470 +bool 1.2471 +Parser<FullParseHandler>::asmJS(Node list) 1.2472 +{ 1.2473 + // If we are already inside "use asm" that means we are either actively 1.2474 + // compiling or we are reparsing after asm.js validation failure. In either 1.2475 + // case, nothing to do here. 1.2476 + if (pc->useAsmOrInsideUseAsm()) 1.2477 + return true; 1.2478 + 1.2479 + // If there is no ScriptSource, then we are doing a non-compiling parse and 1.2480 + // so we shouldn't (and can't, without a ScriptSource) compile. 1.2481 + if (ss == nullptr) 1.2482 + return true; 1.2483 + 1.2484 + pc->sc->asFunctionBox()->useAsm = true; 1.2485 + 1.2486 +#ifdef JS_ION 1.2487 + // Attempt to validate and compile this asm.js module. On success, the 1.2488 + // tokenStream has been advanced to the closing }. On failure, the 1.2489 + // tokenStream is in an indeterminate state and we must reparse the 1.2490 + // function from the beginning. Reparsing is triggered by marking that a 1.2491 + // new directive has been encountered and returning 'false'. 1.2492 + bool validated; 1.2493 + if (!CompileAsmJS(context, *this, list, &validated)) 1.2494 + return false; 1.2495 + if (!validated) { 1.2496 + pc->newDirectives->setAsmJS(); 1.2497 + return false; 1.2498 + } 1.2499 +#endif 1.2500 + 1.2501 + return true; 1.2502 +} 1.2503 + 1.2504 +/* 1.2505 + * Recognize Directive Prologue members and directives. Assuming |pn| is a 1.2506 + * candidate for membership in a directive prologue, recognize directives and 1.2507 + * set |pc|'s flags accordingly. If |pn| is indeed part of a prologue, set its 1.2508 + * |pn_prologue| flag. 1.2509 + * 1.2510 + * Note that the following is a strict mode function: 1.2511 + * 1.2512 + * function foo() { 1.2513 + * "blah" // inserted semi colon 1.2514 + * "blurgh" 1.2515 + * "use\x20loose" 1.2516 + * "use strict" 1.2517 + * } 1.2518 + * 1.2519 + * That is, even though "use\x20loose" can never be a directive, now or in the 1.2520 + * future (because of the hex escape), the Directive Prologue extends through it 1.2521 + * to the "use strict" statement, which is indeed a directive. 1.2522 + */ 1.2523 +template <typename ParseHandler> 1.2524 +bool 1.2525 +Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool *cont) 1.2526 +{ 1.2527 + TokenPos directivePos; 1.2528 + JSAtom *directive = handler.isStringExprStatement(pn, &directivePos); 1.2529 + 1.2530 + *cont = !!directive; 1.2531 + if (!*cont) 1.2532 + return true; 1.2533 + 1.2534 + if (IsEscapeFreeStringLiteral(directivePos, directive)) { 1.2535 + // Mark this statement as being a possibly legitimate part of a 1.2536 + // directive prologue, so the bytecode emitter won't warn about it being 1.2537 + // useless code. (We mustn't just omit the statement entirely yet, as it 1.2538 + // could be producing the value of an eval or JSScript execution.) 1.2539 + // 1.2540 + // Note that even if the string isn't one we recognize as a directive, 1.2541 + // the emitter still shouldn't flag it as useless, as it could become a 1.2542 + // directive in the future. We don't want to interfere with people 1.2543 + // taking advantage of directive-prologue-enabled features that appear 1.2544 + // in other browsers first. 1.2545 + handler.setPrologue(pn); 1.2546 + 1.2547 + if (directive == context->names().useStrict) { 1.2548 + // We're going to be in strict mode. Note that this scope explicitly 1.2549 + // had "use strict"; 1.2550 + pc->sc->setExplicitUseStrict(); 1.2551 + if (!pc->sc->strict) { 1.2552 + if (pc->sc->isFunctionBox()) { 1.2553 + // Request that this function be reparsed as strict. 1.2554 + pc->newDirectives->setStrict(); 1.2555 + return false; 1.2556 + } else { 1.2557 + // We don't reparse global scopes, so we keep track of the 1.2558 + // one possible strict violation that could occur in the 1.2559 + // directive prologue -- octal escapes -- and complain now. 1.2560 + if (tokenStream.sawOctalEscape()) { 1.2561 + report(ParseError, false, null(), JSMSG_DEPRECATED_OCTAL); 1.2562 + return false; 1.2563 + } 1.2564 + pc->sc->strict = true; 1.2565 + } 1.2566 + } 1.2567 + } else if (directive == context->names().useAsm) { 1.2568 + if (pc->sc->isFunctionBox()) 1.2569 + return asmJS(list); 1.2570 + return report(ParseWarning, false, pn, JSMSG_USE_ASM_DIRECTIVE_FAIL); 1.2571 + } 1.2572 + } 1.2573 + return true; 1.2574 +} 1.2575 + 1.2576 +/* 1.2577 + * Parse the statements in a block, creating a StatementList node that lists 1.2578 + * the statements. If called from block-parsing code, the caller must match 1.2579 + * '{' before and '}' after. 1.2580 + */ 1.2581 +template <typename ParseHandler> 1.2582 +typename ParseHandler::Node 1.2583 +Parser<ParseHandler>::statements() 1.2584 +{ 1.2585 + JS_CHECK_RECURSION(context, return null()); 1.2586 + 1.2587 + Node pn = handler.newStatementList(pc->blockid(), pos()); 1.2588 + if (!pn) 1.2589 + return null(); 1.2590 + 1.2591 + Node saveBlock = pc->blockNode; 1.2592 + pc->blockNode = pn; 1.2593 + 1.2594 + bool canHaveDirectives = pc->atBodyLevel(); 1.2595 + for (;;) { 1.2596 + TokenKind tt = tokenStream.peekToken(TokenStream::Operand); 1.2597 + if (tt <= TOK_EOF || tt == TOK_RC) { 1.2598 + if (tt == TOK_ERROR) { 1.2599 + if (tokenStream.isEOF()) 1.2600 + isUnexpectedEOF_ = true; 1.2601 + return null(); 1.2602 + } 1.2603 + break; 1.2604 + } 1.2605 + Node next = statement(canHaveDirectives); 1.2606 + if (!next) { 1.2607 + if (tokenStream.isEOF()) 1.2608 + isUnexpectedEOF_ = true; 1.2609 + return null(); 1.2610 + } 1.2611 + 1.2612 + if (canHaveDirectives) { 1.2613 + if (!maybeParseDirective(pn, next, &canHaveDirectives)) 1.2614 + return null(); 1.2615 + } 1.2616 + 1.2617 + handler.addStatementToList(pn, next, pc); 1.2618 + } 1.2619 + 1.2620 + /* 1.2621 + * Handle the case where there was a let declaration under this block. If 1.2622 + * it replaced pc->blockNode with a new block node then we must refresh pn 1.2623 + * and then restore pc->blockNode. 1.2624 + */ 1.2625 + if (pc->blockNode != pn) 1.2626 + pn = pc->blockNode; 1.2627 + pc->blockNode = saveBlock; 1.2628 + return pn; 1.2629 +} 1.2630 + 1.2631 +template <typename ParseHandler> 1.2632 +typename ParseHandler::Node 1.2633 +Parser<ParseHandler>::condition() 1.2634 +{ 1.2635 + MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND); 1.2636 + Node pn = exprInParens(); 1.2637 + if (!pn) 1.2638 + return null(); 1.2639 + MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND); 1.2640 + 1.2641 + /* Check for (a = b) and warn about possible (a == b) mistype. */ 1.2642 + if (handler.isOperationWithoutParens(pn, PNK_ASSIGN) && 1.2643 + !report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN)) 1.2644 + { 1.2645 + return null(); 1.2646 + } 1.2647 + return pn; 1.2648 +} 1.2649 + 1.2650 +template <typename ParseHandler> 1.2651 +bool 1.2652 +Parser<ParseHandler>::matchLabel(MutableHandle<PropertyName*> label) 1.2653 +{ 1.2654 + TokenKind tt = tokenStream.peekTokenSameLine(TokenStream::Operand); 1.2655 + if (tt == TOK_ERROR) 1.2656 + return false; 1.2657 + if (tt == TOK_NAME) { 1.2658 + tokenStream.consumeKnownToken(TOK_NAME); 1.2659 + label.set(tokenStream.currentName()); 1.2660 + } else if (tt == TOK_YIELD) { 1.2661 + tokenStream.consumeKnownToken(TOK_YIELD); 1.2662 + if (!checkYieldNameValidity()) 1.2663 + return false; 1.2664 + label.set(tokenStream.currentName()); 1.2665 + } else { 1.2666 + label.set(nullptr); 1.2667 + } 1.2668 + return true; 1.2669 +} 1.2670 + 1.2671 +template <typename ParseHandler> 1.2672 +bool 1.2673 +Parser<ParseHandler>::reportRedeclaration(Node pn, bool isConst, JSAtom *atom) 1.2674 +{ 1.2675 + JSAutoByteString name; 1.2676 + if (AtomToPrintableString(context, atom, &name)) 1.2677 + report(ParseError, false, pn, JSMSG_REDECLARED_VAR, isConst ? "const" : "variable", name.ptr()); 1.2678 + return false; 1.2679 +} 1.2680 + 1.2681 +/* 1.2682 + * Define a let-variable in a block, let-expression, or comprehension scope. pc 1.2683 + * must already be in such a scope. 1.2684 + * 1.2685 + * Throw a SyntaxError if 'atom' is an invalid name. Otherwise create a 1.2686 + * property for the new variable on the block object, pc->staticScope; 1.2687 + * populate data->pn->pn_{op,cookie,defn,dflags}; and stash a pointer to 1.2688 + * data->pn in a slot of the block object. 1.2689 + */ 1.2690 +template <> 1.2691 +/* static */ bool 1.2692 +Parser<FullParseHandler>::bindLet(BindData<FullParseHandler> *data, 1.2693 + HandlePropertyName name, Parser<FullParseHandler> *parser) 1.2694 +{ 1.2695 + ParseContext<FullParseHandler> *pc = parser->pc; 1.2696 + ParseNode *pn = data->pn; 1.2697 + if (!parser->checkStrictBinding(name, pn)) 1.2698 + return false; 1.2699 + 1.2700 + ExclusiveContext *cx = parser->context; 1.2701 + 1.2702 + Rooted<StaticBlockObject *> blockObj(cx, data->let.blockObj); 1.2703 + unsigned index = blockObj->numVariables(); 1.2704 + if (index >= StaticBlockObject::LOCAL_INDEX_LIMIT) { 1.2705 + parser->report(ParseError, false, pn, data->let.overflow); 1.2706 + return false; 1.2707 + } 1.2708 + 1.2709 + /* 1.2710 + * Assign block-local index to pn->pn_cookie right away, encoding it as an 1.2711 + * upvar cookie whose skip tells the current static level. The emitter will 1.2712 + * adjust the node's slot based on its stack depth model -- and, for global 1.2713 + * and eval code, js::frontend::CompileScript will adjust the slot 1.2714 + * again to include script->nfixed. 1.2715 + */ 1.2716 + if (!pn->pn_cookie.set(parser->tokenStream, pc->staticLevel, index)) 1.2717 + return false; 1.2718 + 1.2719 + /* 1.2720 + * For bindings that are hoisted to the beginning of the block/function, 1.2721 + * define() right now. Otherwise, delay define until PushLetScope. 1.2722 + */ 1.2723 + if (data->let.varContext == HoistVars) { 1.2724 + JS_ASSERT(!pc->atBodyLevel()); 1.2725 + Definition *dn = pc->decls().lookupFirst(name); 1.2726 + if (dn && dn->pn_blockid == pc->blockid()) 1.2727 + return parser->reportRedeclaration(pn, dn->isConst(), name); 1.2728 + if (!pc->define(parser->tokenStream, name, pn, Definition::LET)) 1.2729 + return false; 1.2730 + } 1.2731 + 1.2732 + bool redeclared; 1.2733 + RootedId id(cx, NameToId(name)); 1.2734 + RootedShape shape(cx, StaticBlockObject::addVar(cx, blockObj, id, index, &redeclared)); 1.2735 + if (!shape) { 1.2736 + if (redeclared) 1.2737 + parser->reportRedeclaration(pn, false, name); 1.2738 + return false; 1.2739 + } 1.2740 + 1.2741 + /* Store pn in the static block object. */ 1.2742 + blockObj->setDefinitionParseNode(index, reinterpret_cast<Definition *>(pn)); 1.2743 + return true; 1.2744 +} 1.2745 + 1.2746 +template <> 1.2747 +/* static */ bool 1.2748 +Parser<SyntaxParseHandler>::bindLet(BindData<SyntaxParseHandler> *data, 1.2749 + HandlePropertyName name, Parser<SyntaxParseHandler> *parser) 1.2750 +{ 1.2751 + if (!parser->checkStrictBinding(name, data->pn)) 1.2752 + return false; 1.2753 + 1.2754 + return true; 1.2755 +} 1.2756 + 1.2757 +template <typename ParseHandler, class Op> 1.2758 +static inline bool 1.2759 +ForEachLetDef(TokenStream &ts, ParseContext<ParseHandler> *pc, 1.2760 + HandleStaticBlockObject blockObj, Op op) 1.2761 +{ 1.2762 + for (Shape::Range<CanGC> r(ts.context(), blockObj->lastProperty()); !r.empty(); r.popFront()) { 1.2763 + Shape &shape = r.front(); 1.2764 + 1.2765 + /* Beware the destructuring dummy slots. */ 1.2766 + if (JSID_IS_INT(shape.propid())) 1.2767 + continue; 1.2768 + 1.2769 + if (!op(ts, pc, blockObj, shape, JSID_TO_ATOM(shape.propid()))) 1.2770 + return false; 1.2771 + } 1.2772 + return true; 1.2773 +} 1.2774 + 1.2775 +template <typename ParseHandler> 1.2776 +struct PopLetDecl { 1.2777 + bool operator()(TokenStream &, ParseContext<ParseHandler> *pc, HandleStaticBlockObject, 1.2778 + const Shape &, JSAtom *atom) 1.2779 + { 1.2780 + pc->popLetDecl(atom); 1.2781 + return true; 1.2782 + } 1.2783 +}; 1.2784 + 1.2785 +// We compute the maximum block scope depth, in slots, of a compilation unit at 1.2786 +// parse-time. Each nested statement has a field indicating the maximum block 1.2787 +// scope depth that is nested inside it. When we leave a nested statement, we 1.2788 +// add the number of slots in the statement to the nested depth, and use that to 1.2789 +// update the maximum block scope depth of the outer statement or parse 1.2790 +// context. In the end, pc->blockScopeDepth will indicate the number of slots 1.2791 +// to reserve in the fixed part of a stack frame. 1.2792 +// 1.2793 +template <typename ParseHandler> 1.2794 +static void 1.2795 +AccumulateBlockScopeDepth(ParseContext<ParseHandler> *pc) 1.2796 +{ 1.2797 + uint32_t innerDepth = pc->topStmt->innerBlockScopeDepth; 1.2798 + StmtInfoPC *outer = pc->topStmt->down; 1.2799 + 1.2800 + if (pc->topStmt->isBlockScope) 1.2801 + innerDepth += pc->topStmt->staticScope->template as<StaticBlockObject>().numVariables(); 1.2802 + 1.2803 + if (outer) { 1.2804 + if (outer->innerBlockScopeDepth < innerDepth) 1.2805 + outer->innerBlockScopeDepth = innerDepth; 1.2806 + } else { 1.2807 + if (pc->blockScopeDepth < innerDepth) 1.2808 + pc->blockScopeDepth = innerDepth; 1.2809 + } 1.2810 +} 1.2811 + 1.2812 +template <typename ParseHandler> 1.2813 +static void 1.2814 +PopStatementPC(TokenStream &ts, ParseContext<ParseHandler> *pc) 1.2815 +{ 1.2816 + RootedNestedScopeObject scopeObj(ts.context(), pc->topStmt->staticScope); 1.2817 + JS_ASSERT(!!scopeObj == pc->topStmt->isNestedScope); 1.2818 + 1.2819 + AccumulateBlockScopeDepth(pc); 1.2820 + FinishPopStatement(pc); 1.2821 + 1.2822 + if (scopeObj) { 1.2823 + if (scopeObj->is<StaticBlockObject>()) { 1.2824 + RootedStaticBlockObject blockObj(ts.context(), &scopeObj->as<StaticBlockObject>()); 1.2825 + JS_ASSERT(!blockObj->inDictionaryMode()); 1.2826 + ForEachLetDef(ts, pc, blockObj, PopLetDecl<ParseHandler>()); 1.2827 + } 1.2828 + scopeObj->resetEnclosingNestedScopeFromParser(); 1.2829 + } 1.2830 +} 1.2831 + 1.2832 +/* 1.2833 + * The function LexicalLookup searches a static binding for the given name in 1.2834 + * the stack of statements enclosing the statement currently being parsed. Each 1.2835 + * statement that introduces a new scope has a corresponding scope object, on 1.2836 + * which the bindings for that scope are stored. LexicalLookup either returns 1.2837 + * the innermost statement which has a scope object containing a binding with 1.2838 + * the given name, or nullptr. 1.2839 + */ 1.2840 +template <class ContextT> 1.2841 +typename ContextT::StmtInfo * 1.2842 +LexicalLookup(ContextT *ct, HandleAtom atom, int *slotp, typename ContextT::StmtInfo *stmt) 1.2843 +{ 1.2844 + RootedId id(ct->sc->context, AtomToId(atom)); 1.2845 + 1.2846 + if (!stmt) 1.2847 + stmt = ct->topScopeStmt; 1.2848 + for (; stmt; stmt = stmt->downScope) { 1.2849 + /* 1.2850 + * With-statements introduce dynamic bindings. Since dynamic bindings 1.2851 + * can potentially override any static bindings introduced by statements 1.2852 + * further up the stack, we have to abort the search. 1.2853 + */ 1.2854 + if (stmt->type == STMT_WITH) 1.2855 + break; 1.2856 + 1.2857 + // Skip statements that do not introduce a new scope 1.2858 + if (!stmt->isBlockScope) 1.2859 + continue; 1.2860 + 1.2861 + StaticBlockObject &blockObj = stmt->staticBlock(); 1.2862 + Shape *shape = blockObj.nativeLookup(ct->sc->context, id); 1.2863 + if (shape) { 1.2864 + if (slotp) 1.2865 + *slotp = blockObj.shapeToIndex(*shape); 1.2866 + return stmt; 1.2867 + } 1.2868 + } 1.2869 + 1.2870 + if (slotp) 1.2871 + *slotp = -1; 1.2872 + return stmt; 1.2873 +} 1.2874 + 1.2875 +template <typename ParseHandler> 1.2876 +static inline bool 1.2877 +OuterLet(ParseContext<ParseHandler> *pc, StmtInfoPC *stmt, HandleAtom atom) 1.2878 +{ 1.2879 + while (stmt->downScope) { 1.2880 + stmt = LexicalLookup(pc, atom, nullptr, stmt->downScope); 1.2881 + if (!stmt) 1.2882 + return false; 1.2883 + if (stmt->type == STMT_BLOCK) 1.2884 + return true; 1.2885 + } 1.2886 + return false; 1.2887 +} 1.2888 + 1.2889 +template <typename ParseHandler> 1.2890 +/* static */ bool 1.2891 +Parser<ParseHandler>::bindVarOrConst(BindData<ParseHandler> *data, 1.2892 + HandlePropertyName name, Parser<ParseHandler> *parser) 1.2893 +{ 1.2894 + ExclusiveContext *cx = parser->context; 1.2895 + ParseContext<ParseHandler> *pc = parser->pc; 1.2896 + Node pn = data->pn; 1.2897 + bool isConstDecl = data->op == JSOP_DEFCONST; 1.2898 + 1.2899 + /* Default best op for pn is JSOP_NAME; we'll try to improve below. */ 1.2900 + parser->handler.setOp(pn, JSOP_NAME); 1.2901 + 1.2902 + if (!parser->checkStrictBinding(name, pn)) 1.2903 + return false; 1.2904 + 1.2905 + StmtInfoPC *stmt = LexicalLookup(pc, name, nullptr, (StmtInfoPC *)nullptr); 1.2906 + 1.2907 + if (stmt && stmt->type == STMT_WITH) { 1.2908 + parser->handler.setFlag(pn, PND_DEOPTIMIZED); 1.2909 + if (pc->sc->isFunctionBox()) { 1.2910 + FunctionBox *funbox = pc->sc->asFunctionBox(); 1.2911 + funbox->setMightAliasLocals(); 1.2912 + } 1.2913 + 1.2914 + /* 1.2915 + * This definition isn't being added to the parse context's 1.2916 + * declarations, so make sure to indicate the need to deoptimize 1.2917 + * the script's arguments object. Mark the function as if it 1.2918 + * contained a debugger statement, which will deoptimize arguments 1.2919 + * as much as possible. 1.2920 + */ 1.2921 + if (name == cx->names().arguments) 1.2922 + pc->sc->setHasDebuggerStatement(); 1.2923 + 1.2924 + return true; 1.2925 + } 1.2926 + 1.2927 + DefinitionList::Range defs = pc->decls().lookupMulti(name); 1.2928 + JS_ASSERT_IF(stmt, !defs.empty()); 1.2929 + 1.2930 + if (defs.empty()) { 1.2931 + return pc->define(parser->tokenStream, name, pn, 1.2932 + isConstDecl ? Definition::CONST : Definition::VAR); 1.2933 + } 1.2934 + 1.2935 + /* 1.2936 + * There was a previous declaration with the same name. The standard 1.2937 + * disallows several forms of redeclaration. Critically, 1.2938 + * let (x) { var x; } // error 1.2939 + * is not allowed which allows us to turn any non-error redeclaration 1.2940 + * into a use of the initial declaration. 1.2941 + */ 1.2942 + DefinitionNode dn = defs.front<ParseHandler>(); 1.2943 + Definition::Kind dn_kind = parser->handler.getDefinitionKind(dn); 1.2944 + if (dn_kind == Definition::ARG) { 1.2945 + JSAutoByteString bytes; 1.2946 + if (!AtomToPrintableString(cx, name, &bytes)) 1.2947 + return false; 1.2948 + 1.2949 + if (isConstDecl) { 1.2950 + parser->report(ParseError, false, pn, JSMSG_REDECLARED_PARAM, bytes.ptr()); 1.2951 + return false; 1.2952 + } 1.2953 + if (!parser->report(ParseExtraWarning, false, pn, JSMSG_VAR_HIDES_ARG, bytes.ptr())) 1.2954 + return false; 1.2955 + } else { 1.2956 + bool error = (isConstDecl || 1.2957 + dn_kind == Definition::CONST || 1.2958 + (dn_kind == Definition::LET && 1.2959 + (stmt->type != STMT_CATCH || OuterLet(pc, stmt, name)))); 1.2960 + 1.2961 + if (parser->options().extraWarningsOption 1.2962 + ? data->op != JSOP_DEFVAR || dn_kind != Definition::VAR 1.2963 + : error) 1.2964 + { 1.2965 + JSAutoByteString bytes; 1.2966 + ParseReportKind reporter = error ? ParseError : ParseExtraWarning; 1.2967 + if (!AtomToPrintableString(cx, name, &bytes) || 1.2968 + !parser->report(reporter, false, pn, JSMSG_REDECLARED_VAR, 1.2969 + Definition::kindString(dn_kind), bytes.ptr())) 1.2970 + { 1.2971 + return false; 1.2972 + } 1.2973 + } 1.2974 + } 1.2975 + 1.2976 + parser->handler.linkUseToDef(pn, dn); 1.2977 + return true; 1.2978 +} 1.2979 + 1.2980 +template <> 1.2981 +bool 1.2982 +Parser<FullParseHandler>::makeSetCall(ParseNode *pn, unsigned msg) 1.2983 +{ 1.2984 + JS_ASSERT(pn->isKind(PNK_CALL)); 1.2985 + JS_ASSERT(pn->isArity(PN_LIST)); 1.2986 + JS_ASSERT(pn->isOp(JSOP_CALL) || pn->isOp(JSOP_SPREADCALL) || 1.2987 + pn->isOp(JSOP_EVAL) || pn->isOp(JSOP_SPREADEVAL) || 1.2988 + pn->isOp(JSOP_FUNCALL) || pn->isOp(JSOP_FUNAPPLY)); 1.2989 + 1.2990 + if (!report(ParseStrictError, pc->sc->strict, pn, msg)) 1.2991 + return false; 1.2992 + handler.markAsSetCall(pn); 1.2993 + return true; 1.2994 +} 1.2995 + 1.2996 +template <typename ParseHandler> 1.2997 +bool 1.2998 +Parser<ParseHandler>::noteNameUse(HandlePropertyName name, Node pn) 1.2999 +{ 1.3000 + StmtInfoPC *stmt = LexicalLookup(pc, name, nullptr, (StmtInfoPC *)nullptr); 1.3001 + 1.3002 + DefinitionList::Range defs = pc->decls().lookupMulti(name); 1.3003 + 1.3004 + DefinitionNode dn; 1.3005 + if (!defs.empty()) { 1.3006 + dn = defs.front<ParseHandler>(); 1.3007 + } else { 1.3008 + /* 1.3009 + * No definition before this use in any lexical scope. 1.3010 + * Create a placeholder definition node to either: 1.3011 + * - Be adopted when we parse the real defining 1.3012 + * declaration, or 1.3013 + * - Be left as a free variable definition if we never 1.3014 + * see the real definition. 1.3015 + */ 1.3016 + dn = getOrCreateLexicalDependency(pc, name); 1.3017 + if (!dn) 1.3018 + return false; 1.3019 + } 1.3020 + 1.3021 + handler.linkUseToDef(pn, dn); 1.3022 + 1.3023 + if (stmt && stmt->type == STMT_WITH) 1.3024 + handler.setFlag(pn, PND_DEOPTIMIZED); 1.3025 + 1.3026 + return true; 1.3027 +} 1.3028 + 1.3029 +template <> 1.3030 +bool 1.3031 +Parser<FullParseHandler>::bindDestructuringVar(BindData<FullParseHandler> *data, ParseNode *pn) 1.3032 +{ 1.3033 + JS_ASSERT(pn->isKind(PNK_NAME)); 1.3034 + 1.3035 + RootedPropertyName name(context, pn->pn_atom->asPropertyName()); 1.3036 + 1.3037 + data->pn = pn; 1.3038 + if (!data->binder(data, name, this)) 1.3039 + return false; 1.3040 + 1.3041 + /* 1.3042 + * Select the appropriate name-setting opcode, respecting eager selection 1.3043 + * done by the data->binder function. 1.3044 + */ 1.3045 + if (pn->pn_dflags & PND_BOUND) 1.3046 + pn->setOp(JSOP_SETLOCAL); 1.3047 + else if (data->op == JSOP_DEFCONST) 1.3048 + pn->setOp(JSOP_SETCONST); 1.3049 + else 1.3050 + pn->setOp(JSOP_SETNAME); 1.3051 + 1.3052 + if (data->op == JSOP_DEFCONST) 1.3053 + pn->pn_dflags |= PND_CONST; 1.3054 + 1.3055 + pn->markAsAssigned(); 1.3056 + return true; 1.3057 +} 1.3058 + 1.3059 +/* 1.3060 + * Destructuring patterns can appear in two kinds of contexts: 1.3061 + * 1.3062 + * - assignment-like: assignment expressions and |for| loop heads. In 1.3063 + * these cases, the patterns' property value positions can be 1.3064 + * arbitrary lvalue expressions; the destructuring is just a fancy 1.3065 + * assignment. 1.3066 + * 1.3067 + * - declaration-like: |var| and |let| declarations, functions' formal 1.3068 + * parameter lists, |catch| clauses, and comprehension tails. In 1.3069 + * these cases, the patterns' property value positions must be 1.3070 + * simple names; the destructuring defines them as new variables. 1.3071 + * 1.3072 + * In both cases, other code parses the pattern as an arbitrary 1.3073 + * primaryExpr, and then, here in CheckDestructuring, verify that the 1.3074 + * tree is a valid destructuring expression. 1.3075 + * 1.3076 + * In assignment-like contexts, we parse the pattern with 1.3077 + * pc->inDeclDestructuring clear, so the lvalue expressions in the 1.3078 + * pattern are parsed normally. primaryExpr links variable references 1.3079 + * into the appropriate use chains; creates placeholder definitions; 1.3080 + * and so on. CheckDestructuring is called with |data| nullptr (since 1.3081 + * we won't be binding any new names), and we specialize lvalues as 1.3082 + * appropriate. 1.3083 + * 1.3084 + * In declaration-like contexts, the normal variable reference 1.3085 + * processing would just be an obstruction, because we're going to 1.3086 + * define the names that appear in the property value positions as new 1.3087 + * variables anyway. In this case, we parse the pattern with 1.3088 + * pc->inDeclDestructuring set, which directs primaryExpr to leave 1.3089 + * whatever name nodes it creates unconnected. Then, here in 1.3090 + * CheckDestructuring, we require the pattern's property value 1.3091 + * positions to be simple names, and define them as appropriate to the 1.3092 + * context. For these calls, |data| points to the right sort of 1.3093 + * BindData. 1.3094 + * 1.3095 + * The 'toplevel' is a private detail of the recursive strategy used by 1.3096 + * CheckDestructuring and callers should use the default value. 1.3097 + */ 1.3098 +template <> 1.3099 +bool 1.3100 +Parser<FullParseHandler>::checkDestructuring(BindData<FullParseHandler> *data, 1.3101 + ParseNode *left, bool toplevel) 1.3102 +{ 1.3103 + bool ok; 1.3104 + 1.3105 + if (left->isKind(PNK_ARRAYCOMP)) { 1.3106 + report(ParseError, false, left, JSMSG_ARRAY_COMP_LEFTSIDE); 1.3107 + return false; 1.3108 + } 1.3109 + 1.3110 + Rooted<StaticBlockObject *> blockObj(context); 1.3111 + blockObj = data && data->binder == bindLet ? data->let.blockObj.get() : nullptr; 1.3112 + 1.3113 + if (left->isKind(PNK_ARRAY)) { 1.3114 + for (ParseNode *pn = left->pn_head; pn; pn = pn->pn_next) { 1.3115 + if (!pn->isKind(PNK_ELISION)) { 1.3116 + if (pn->isKind(PNK_ARRAY) || pn->isKind(PNK_OBJECT)) { 1.3117 + ok = checkDestructuring(data, pn, false); 1.3118 + } else { 1.3119 + if (data) { 1.3120 + if (!pn->isKind(PNK_NAME)) { 1.3121 + report(ParseError, false, pn, JSMSG_NO_VARIABLE_NAME); 1.3122 + return false; 1.3123 + } 1.3124 + ok = bindDestructuringVar(data, pn); 1.3125 + } else { 1.3126 + ok = checkAndMarkAsAssignmentLhs(pn, KeyedDestructuringAssignment); 1.3127 + } 1.3128 + } 1.3129 + if (!ok) 1.3130 + return false; 1.3131 + } 1.3132 + } 1.3133 + } else { 1.3134 + JS_ASSERT(left->isKind(PNK_OBJECT)); 1.3135 + for (ParseNode *member = left->pn_head; member; member = member->pn_next) { 1.3136 + MOZ_ASSERT(member->isKind(PNK_COLON)); 1.3137 + ParseNode *expr = member->pn_right; 1.3138 + 1.3139 + if (expr->isKind(PNK_ARRAY) || expr->isKind(PNK_OBJECT)) { 1.3140 + ok = checkDestructuring(data, expr, false); 1.3141 + } else if (data) { 1.3142 + if (!expr->isKind(PNK_NAME)) { 1.3143 + report(ParseError, false, expr, JSMSG_NO_VARIABLE_NAME); 1.3144 + return false; 1.3145 + } 1.3146 + ok = bindDestructuringVar(data, expr); 1.3147 + } else { 1.3148 + /* 1.3149 + * If this is a destructuring shorthand ({x} = ...), then 1.3150 + * identifierName wasn't used to parse |x|. As a result, |x| 1.3151 + * hasn't been officially linked to its def or registered in 1.3152 + * lexdeps. Do that now. 1.3153 + */ 1.3154 + if (member->pn_right == member->pn_left) { 1.3155 + RootedPropertyName name(context, expr->pn_atom->asPropertyName()); 1.3156 + if (!noteNameUse(name, expr)) 1.3157 + return false; 1.3158 + } 1.3159 + ok = checkAndMarkAsAssignmentLhs(expr, KeyedDestructuringAssignment); 1.3160 + } 1.3161 + if (!ok) 1.3162 + return false; 1.3163 + } 1.3164 + } 1.3165 + 1.3166 + return true; 1.3167 +} 1.3168 + 1.3169 +template <> 1.3170 +bool 1.3171 +Parser<SyntaxParseHandler>::checkDestructuring(BindData<SyntaxParseHandler> *data, 1.3172 + Node left, bool toplevel) 1.3173 +{ 1.3174 + return abortIfSyntaxParser(); 1.3175 +} 1.3176 + 1.3177 +template <typename ParseHandler> 1.3178 +typename ParseHandler::Node 1.3179 +Parser<ParseHandler>::destructuringExpr(BindData<ParseHandler> *data, TokenKind tt) 1.3180 +{ 1.3181 + JS_ASSERT(tokenStream.isCurrentTokenType(tt)); 1.3182 + 1.3183 + pc->inDeclDestructuring = true; 1.3184 + Node pn = primaryExpr(tt); 1.3185 + pc->inDeclDestructuring = false; 1.3186 + if (!pn) 1.3187 + return null(); 1.3188 + if (!checkDestructuring(data, pn)) 1.3189 + return null(); 1.3190 + return pn; 1.3191 +} 1.3192 + 1.3193 +template <typename ParseHandler> 1.3194 +typename ParseHandler::Node 1.3195 +Parser<ParseHandler>::pushLexicalScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt) 1.3196 +{ 1.3197 + JS_ASSERT(blockObj); 1.3198 + 1.3199 + ObjectBox *blockbox = newObjectBox(blockObj); 1.3200 + if (!blockbox) 1.3201 + return null(); 1.3202 + 1.3203 + PushStatementPC(pc, stmt, STMT_BLOCK); 1.3204 + blockObj->initEnclosingNestedScopeFromParser(pc->staticScope); 1.3205 + FinishPushNestedScope(pc, stmt, *blockObj.get()); 1.3206 + stmt->isBlockScope = true; 1.3207 + 1.3208 + Node pn = handler.newLexicalScope(blockbox); 1.3209 + if (!pn) 1.3210 + return null(); 1.3211 + 1.3212 + if (!GenerateBlockId(tokenStream, pc, stmt->blockid)) 1.3213 + return null(); 1.3214 + handler.setBlockId(pn, stmt->blockid); 1.3215 + return pn; 1.3216 +} 1.3217 + 1.3218 +template <typename ParseHandler> 1.3219 +typename ParseHandler::Node 1.3220 +Parser<ParseHandler>::pushLexicalScope(StmtInfoPC *stmt) 1.3221 +{ 1.3222 + RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context)); 1.3223 + if (!blockObj) 1.3224 + return null(); 1.3225 + 1.3226 + return pushLexicalScope(blockObj, stmt); 1.3227 +} 1.3228 + 1.3229 +struct AddLetDecl 1.3230 +{ 1.3231 + uint32_t blockid; 1.3232 + 1.3233 + AddLetDecl(uint32_t blockid) : blockid(blockid) {} 1.3234 + 1.3235 + bool operator()(TokenStream &ts, ParseContext<FullParseHandler> *pc, 1.3236 + HandleStaticBlockObject blockObj, const Shape &shape, JSAtom *) 1.3237 + { 1.3238 + ParseNode *def = (ParseNode *) blockObj->getSlot(shape.slot()).toPrivate(); 1.3239 + def->pn_blockid = blockid; 1.3240 + RootedPropertyName name(ts.context(), def->name()); 1.3241 + return pc->define(ts, name, def, Definition::LET); 1.3242 + } 1.3243 +}; 1.3244 + 1.3245 +template <> 1.3246 +ParseNode * 1.3247 +Parser<FullParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt) 1.3248 +{ 1.3249 + JS_ASSERT(blockObj); 1.3250 + ParseNode *pn = pushLexicalScope(blockObj, stmt); 1.3251 + if (!pn) 1.3252 + return null(); 1.3253 + 1.3254 + pn->pn_dflags |= PND_LET; 1.3255 + 1.3256 + /* Populate the new scope with decls found in the head with updated blockid. */ 1.3257 + if (!ForEachLetDef(tokenStream, pc, blockObj, AddLetDecl(stmt->blockid))) 1.3258 + return null(); 1.3259 + 1.3260 + return pn; 1.3261 +} 1.3262 + 1.3263 +template <> 1.3264 +SyntaxParseHandler::Node 1.3265 +Parser<SyntaxParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt) 1.3266 +{ 1.3267 + JS_ALWAYS_FALSE(abortIfSyntaxParser()); 1.3268 + return SyntaxParseHandler::NodeFailure; 1.3269 +} 1.3270 + 1.3271 +/* 1.3272 + * Parse a let block statement or let expression (determined by 'letContext'). 1.3273 + * In both cases, bindings are not hoisted to the top of the enclosing block 1.3274 + * and thus must be carefully injected between variables() and the let body. 1.3275 + */ 1.3276 +template <typename ParseHandler> 1.3277 +typename ParseHandler::Node 1.3278 +Parser<ParseHandler>::letBlock(LetContext letContext) 1.3279 +{ 1.3280 + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LET)); 1.3281 + 1.3282 + RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context)); 1.3283 + if (!blockObj) 1.3284 + return null(); 1.3285 + 1.3286 + uint32_t begin = pos().begin; 1.3287 + 1.3288 + MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET); 1.3289 + 1.3290 + Node vars = variables(PNK_LET, nullptr, blockObj, DontHoistVars); 1.3291 + if (!vars) 1.3292 + return null(); 1.3293 + 1.3294 + MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET); 1.3295 + 1.3296 + StmtInfoPC stmtInfo(context); 1.3297 + Node block = pushLetScope(blockObj, &stmtInfo); 1.3298 + if (!block) 1.3299 + return null(); 1.3300 + 1.3301 + Node pnlet = handler.newBinary(PNK_LET, vars, block); 1.3302 + if (!pnlet) 1.3303 + return null(); 1.3304 + handler.setBeginPosition(pnlet, begin); 1.3305 + 1.3306 + bool needExprStmt = false; 1.3307 + if (letContext == LetStatement && !tokenStream.matchToken(TOK_LC, TokenStream::Operand)) { 1.3308 + /* 1.3309 + * Strict mode eliminates a grammar ambiguity with unparenthesized 1.3310 + * LetExpressions in an ExpressionStatement. If followed immediately 1.3311 + * by an arguments list, it's ambiguous whether the let expression 1.3312 + * is the callee or the call is inside the let expression body. 1.3313 + * 1.3314 + * See bug 569464. 1.3315 + */ 1.3316 + if (!report(ParseStrictError, pc->sc->strict, pnlet, 1.3317 + JSMSG_STRICT_CODE_LET_EXPR_STMT)) 1.3318 + { 1.3319 + return null(); 1.3320 + } 1.3321 + 1.3322 + /* 1.3323 + * If this is really an expression in let statement guise, then we 1.3324 + * need to wrap the PNK_LET node in a PNK_SEMI node so that we pop 1.3325 + * the return value of the expression. 1.3326 + */ 1.3327 + needExprStmt = true; 1.3328 + letContext = LetExpresion; 1.3329 + } 1.3330 + 1.3331 + Node expr; 1.3332 + if (letContext == LetStatement) { 1.3333 + expr = statements(); 1.3334 + if (!expr) 1.3335 + return null(); 1.3336 + MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET); 1.3337 + } else { 1.3338 + JS_ASSERT(letContext == LetExpresion); 1.3339 + expr = assignExpr(); 1.3340 + if (!expr) 1.3341 + return null(); 1.3342 + } 1.3343 + handler.setLexicalScopeBody(block, expr); 1.3344 + PopStatementPC(tokenStream, pc); 1.3345 + 1.3346 + handler.setEndPosition(pnlet, pos().end); 1.3347 + 1.3348 + if (needExprStmt) { 1.3349 + if (!MatchOrInsertSemicolon(tokenStream)) 1.3350 + return null(); 1.3351 + return handler.newExprStatement(pnlet, pos().end); 1.3352 + } 1.3353 + return pnlet; 1.3354 +} 1.3355 + 1.3356 +template <typename ParseHandler> 1.3357 +static bool 1.3358 +PushBlocklikeStatement(TokenStream &ts, StmtInfoPC *stmt, StmtType type, 1.3359 + ParseContext<ParseHandler> *pc) 1.3360 +{ 1.3361 + PushStatementPC(pc, stmt, type); 1.3362 + return GenerateBlockId(ts, pc, stmt->blockid); 1.3363 +} 1.3364 + 1.3365 +template <typename ParseHandler> 1.3366 +typename ParseHandler::Node 1.3367 +Parser<ParseHandler>::blockStatement() 1.3368 +{ 1.3369 + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LC)); 1.3370 + 1.3371 + StmtInfoPC stmtInfo(context); 1.3372 + if (!PushBlocklikeStatement(tokenStream, &stmtInfo, STMT_BLOCK, pc)) 1.3373 + return null(); 1.3374 + 1.3375 + Node list = statements(); 1.3376 + if (!list) 1.3377 + return null(); 1.3378 + 1.3379 + MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND); 1.3380 + PopStatementPC(tokenStream, pc); 1.3381 + return list; 1.3382 +} 1.3383 + 1.3384 +template <typename ParseHandler> 1.3385 +typename ParseHandler::Node 1.3386 +Parser<ParseHandler>::newBindingNode(PropertyName *name, bool functionScope, VarContext varContext) 1.3387 +{ 1.3388 + /* 1.3389 + * If this name is being injected into an existing block/function, see if 1.3390 + * it has already been declared or if it resolves an outstanding lexdep. 1.3391 + * Otherwise, this is a let block/expr that introduces a new scope and thus 1.3392 + * shadows existing decls and doesn't resolve existing lexdeps. Duplicate 1.3393 + * names are caught by bindLet. 1.3394 + */ 1.3395 + if (varContext == HoistVars) { 1.3396 + if (AtomDefnPtr p = pc->lexdeps->lookup(name)) { 1.3397 + DefinitionNode lexdep = p.value().get<ParseHandler>(); 1.3398 + JS_ASSERT(handler.getDefinitionKind(lexdep) == Definition::PLACEHOLDER); 1.3399 + 1.3400 + Node pn = handler.getDefinitionNode(lexdep); 1.3401 + if (handler.dependencyCovered(pn, pc->blockid(), functionScope)) { 1.3402 + handler.setBlockId(pn, pc->blockid()); 1.3403 + pc->lexdeps->remove(p); 1.3404 + handler.setPosition(pn, pos()); 1.3405 + return pn; 1.3406 + } 1.3407 + } 1.3408 + } 1.3409 + 1.3410 + /* Make a new node for this declarator name (or destructuring pattern). */ 1.3411 + return newName(name); 1.3412 +} 1.3413 + 1.3414 +/* 1.3415 + * The 'blockObj' parameter is non-null when parsing the 'vars' in a let 1.3416 + * expression, block statement, non-top-level let declaration in statement 1.3417 + * context, and the let-initializer of a for-statement. 1.3418 + */ 1.3419 +template <typename ParseHandler> 1.3420 +typename ParseHandler::Node 1.3421 +Parser<ParseHandler>::variables(ParseNodeKind kind, bool *psimple, 1.3422 + StaticBlockObject *blockObj, VarContext varContext) 1.3423 +{ 1.3424 + /* 1.3425 + * The four options here are: 1.3426 + * - PNK_VAR: We're parsing var declarations. 1.3427 + * - PNK_CONST: We're parsing const declarations. 1.3428 + * - PNK_LET: We are parsing a let declaration. 1.3429 + * - PNK_CALL: We are parsing the head of a let block. 1.3430 + */ 1.3431 + JS_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET || kind == PNK_CALL); 1.3432 + 1.3433 + /* 1.3434 + * The simple flag is set if the declaration has the form 'var x', with 1.3435 + * only one variable declared and no initializer expression. 1.3436 + */ 1.3437 + JS_ASSERT_IF(psimple, *psimple); 1.3438 + 1.3439 + JSOp op = blockObj ? JSOP_NOP : kind == PNK_VAR ? JSOP_DEFVAR : JSOP_DEFCONST; 1.3440 + 1.3441 + Node pn = handler.newList(kind, null(), op); 1.3442 + if (!pn) 1.3443 + return null(); 1.3444 + 1.3445 + /* 1.3446 + * SpiderMonkey const is really "write once per initialization evaluation" 1.3447 + * var, whereas let is block scoped. ES-Harmony wants block-scoped const so 1.3448 + * this code will change soon. 1.3449 + */ 1.3450 + BindData<ParseHandler> data(context); 1.3451 + if (blockObj) 1.3452 + data.initLet(varContext, *blockObj, JSMSG_TOO_MANY_LOCALS); 1.3453 + else 1.3454 + data.initVarOrConst(op); 1.3455 + 1.3456 + bool first = true; 1.3457 + Node pn2; 1.3458 + do { 1.3459 + if (psimple && !first) 1.3460 + *psimple = false; 1.3461 + first = false; 1.3462 + 1.3463 + TokenKind tt = tokenStream.getToken(); 1.3464 + if (tt == TOK_LB || tt == TOK_LC) { 1.3465 + if (psimple) 1.3466 + *psimple = false; 1.3467 + 1.3468 + pc->inDeclDestructuring = true; 1.3469 + pn2 = primaryExpr(tt); 1.3470 + pc->inDeclDestructuring = false; 1.3471 + if (!pn2) 1.3472 + return null(); 1.3473 + 1.3474 + if (!checkDestructuring(&data, pn2)) 1.3475 + return null(); 1.3476 + bool ignored; 1.3477 + if (pc->parsingForInit && matchInOrOf(&ignored)) { 1.3478 + tokenStream.ungetToken(); 1.3479 + handler.addList(pn, pn2); 1.3480 + continue; 1.3481 + } 1.3482 + 1.3483 + MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL); 1.3484 + 1.3485 + Node init = assignExpr(); 1.3486 + if (!init) 1.3487 + return null(); 1.3488 + 1.3489 + pn2 = handler.newBinaryOrAppend(PNK_ASSIGN, pn2, init, pc); 1.3490 + if (!pn2) 1.3491 + return null(); 1.3492 + handler.addList(pn, pn2); 1.3493 + continue; 1.3494 + } 1.3495 + 1.3496 + if (tt != TOK_NAME) { 1.3497 + if (tt == TOK_YIELD) { 1.3498 + if (!checkYieldNameValidity()) 1.3499 + return null(); 1.3500 + } else { 1.3501 + if (tt != TOK_ERROR) 1.3502 + report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME); 1.3503 + return null(); 1.3504 + } 1.3505 + } 1.3506 + 1.3507 + RootedPropertyName name(context, tokenStream.currentName()); 1.3508 + pn2 = newBindingNode(name, kind == PNK_VAR || kind == PNK_CONST, varContext); 1.3509 + if (!pn2) 1.3510 + return null(); 1.3511 + if (data.op == JSOP_DEFCONST) 1.3512 + handler.setFlag(pn2, PND_CONST); 1.3513 + data.pn = pn2; 1.3514 + if (!data.binder(&data, name, this)) 1.3515 + return null(); 1.3516 + handler.addList(pn, pn2); 1.3517 + 1.3518 + if (tokenStream.matchToken(TOK_ASSIGN)) { 1.3519 + if (psimple) 1.3520 + *psimple = false; 1.3521 + 1.3522 + Node init = assignExpr(); 1.3523 + if (!init) 1.3524 + return null(); 1.3525 + 1.3526 + if (!handler.finishInitializerAssignment(pn2, init, data.op)) 1.3527 + return null(); 1.3528 + } 1.3529 + } while (tokenStream.matchToken(TOK_COMMA)); 1.3530 + 1.3531 + return pn; 1.3532 +} 1.3533 + 1.3534 +template <> 1.3535 +ParseNode * 1.3536 +Parser<FullParseHandler>::letDeclaration() 1.3537 +{ 1.3538 + handler.disableSyntaxParser(); 1.3539 + 1.3540 + ParseNode *pn; 1.3541 + 1.3542 + do { 1.3543 + /* 1.3544 + * This is a let declaration. We must be directly under a block per the 1.3545 + * proposed ES4 specs, but not an implicit block created due to 1.3546 + * 'for (let ...)'. If we pass this error test, make the enclosing 1.3547 + * StmtInfoPC be our scope. Further let declarations in this block will 1.3548 + * find this scope statement and use the same block object. 1.3549 + * 1.3550 + * If we are the first let declaration in this block (i.e., when the 1.3551 + * enclosing maybe-scope StmtInfoPC isn't yet a scope statement) then 1.3552 + * we also need to set pc->blockNode to be our PNK_LEXICALSCOPE. 1.3553 + */ 1.3554 + StmtInfoPC *stmt = pc->topStmt; 1.3555 + if (stmt && (!stmt->maybeScope() || stmt->isForLetBlock)) { 1.3556 + report(ParseError, false, null(), JSMSG_LET_DECL_NOT_IN_BLOCK); 1.3557 + return null(); 1.3558 + } 1.3559 + 1.3560 + if (stmt && stmt->isBlockScope) { 1.3561 + JS_ASSERT(pc->staticScope == stmt->staticScope); 1.3562 + } else { 1.3563 + if (pc->atBodyLevel()) { 1.3564 + /* 1.3565 + * ES4 specifies that let at top level and at body-block scope 1.3566 + * does not shadow var, so convert back to var. 1.3567 + */ 1.3568 + pn = variables(PNK_VAR); 1.3569 + if (!pn) 1.3570 + return null(); 1.3571 + pn->pn_xflags |= PNX_POPVAR; 1.3572 + break; 1.3573 + } 1.3574 + 1.3575 + /* 1.3576 + * Some obvious assertions here, but they may help clarify the 1.3577 + * situation. This stmt is not yet a scope, so it must not be a 1.3578 + * catch block (catch is a lexical scope by definition). 1.3579 + */ 1.3580 + JS_ASSERT(!stmt->isBlockScope); 1.3581 + JS_ASSERT(stmt != pc->topScopeStmt); 1.3582 + JS_ASSERT(stmt->type == STMT_BLOCK || 1.3583 + stmt->type == STMT_SWITCH || 1.3584 + stmt->type == STMT_TRY || 1.3585 + stmt->type == STMT_FINALLY); 1.3586 + JS_ASSERT(!stmt->downScope); 1.3587 + 1.3588 + /* Convert the block statement into a scope statement. */ 1.3589 + StaticBlockObject *blockObj = StaticBlockObject::create(context); 1.3590 + if (!blockObj) 1.3591 + return null(); 1.3592 + 1.3593 + ObjectBox *blockbox = newObjectBox(blockObj); 1.3594 + if (!blockbox) 1.3595 + return null(); 1.3596 + 1.3597 + /* 1.3598 + * Insert stmt on the pc->topScopeStmt/stmtInfo.downScope linked 1.3599 + * list stack, if it isn't already there. If it is there, but it 1.3600 + * lacks the SIF_SCOPE flag, it must be a try, catch, or finally 1.3601 + * block. 1.3602 + */ 1.3603 + stmt->isBlockScope = stmt->isNestedScope = true; 1.3604 + stmt->downScope = pc->topScopeStmt; 1.3605 + pc->topScopeStmt = stmt; 1.3606 + 1.3607 + blockObj->initEnclosingNestedScopeFromParser(pc->staticScope); 1.3608 + pc->staticScope = blockObj; 1.3609 + stmt->staticScope = blockObj; 1.3610 + 1.3611 +#ifdef DEBUG 1.3612 + ParseNode *tmp = pc->blockNode; 1.3613 + JS_ASSERT(!tmp || !tmp->isKind(PNK_LEXICALSCOPE)); 1.3614 +#endif 1.3615 + 1.3616 + /* Create a new lexical scope node for these statements. */ 1.3617 + ParseNode *pn1 = LexicalScopeNode::create(PNK_LEXICALSCOPE, &handler); 1.3618 + if (!pn1) 1.3619 + return null(); 1.3620 + 1.3621 + pn1->pn_pos = pc->blockNode->pn_pos; 1.3622 + pn1->pn_objbox = blockbox; 1.3623 + pn1->pn_expr = pc->blockNode; 1.3624 + pn1->pn_blockid = pc->blockNode->pn_blockid; 1.3625 + pc->blockNode = pn1; 1.3626 + } 1.3627 + 1.3628 + pn = variables(PNK_LET, nullptr, &pc->staticScope->as<StaticBlockObject>(), HoistVars); 1.3629 + if (!pn) 1.3630 + return null(); 1.3631 + pn->pn_xflags = PNX_POPVAR; 1.3632 + } while (0); 1.3633 + 1.3634 + return MatchOrInsertSemicolon(tokenStream) ? pn : nullptr; 1.3635 +} 1.3636 + 1.3637 +template <> 1.3638 +SyntaxParseHandler::Node 1.3639 +Parser<SyntaxParseHandler>::letDeclaration() 1.3640 +{ 1.3641 + JS_ALWAYS_FALSE(abortIfSyntaxParser()); 1.3642 + return SyntaxParseHandler::NodeFailure; 1.3643 +} 1.3644 + 1.3645 +template <> 1.3646 +ParseNode * 1.3647 +Parser<FullParseHandler>::letStatement() 1.3648 +{ 1.3649 + handler.disableSyntaxParser(); 1.3650 + 1.3651 + /* Check for a let statement or let expression. */ 1.3652 + ParseNode *pn; 1.3653 + if (tokenStream.peekToken() == TOK_LP) { 1.3654 + pn = letBlock(LetStatement); 1.3655 + JS_ASSERT_IF(pn, pn->isKind(PNK_LET) || pn->isKind(PNK_SEMI)); 1.3656 + } else { 1.3657 + pn = letDeclaration(); 1.3658 + } 1.3659 + return pn; 1.3660 +} 1.3661 + 1.3662 +template <> 1.3663 +SyntaxParseHandler::Node 1.3664 +Parser<SyntaxParseHandler>::letStatement() 1.3665 +{ 1.3666 + JS_ALWAYS_FALSE(abortIfSyntaxParser()); 1.3667 + return SyntaxParseHandler::NodeFailure; 1.3668 +} 1.3669 + 1.3670 +template<typename ParseHandler> 1.3671 +typename ParseHandler::Node 1.3672 +Parser<ParseHandler>::importDeclaration() 1.3673 +{ 1.3674 + JS_ASSERT(tokenStream.currentToken().type == TOK_IMPORT); 1.3675 + 1.3676 + if (pc->sc->isFunctionBox() || !pc->atBodyLevel()) { 1.3677 + report(ParseError, false, null(), JSMSG_IMPORT_DECL_AT_TOP_LEVEL); 1.3678 + return null(); 1.3679 + } 1.3680 + 1.3681 + uint32_t begin = pos().begin; 1.3682 + TokenKind tt = tokenStream.getToken(); 1.3683 + 1.3684 + Node importSpecSet = handler.newList(PNK_IMPORT_SPEC_LIST); 1.3685 + if (!importSpecSet) 1.3686 + return null(); 1.3687 + 1.3688 + if (tt == TOK_NAME || tt == TOK_LC) { 1.3689 + if (tt == TOK_NAME) { 1.3690 + // Handle the form |import a from 'b'|, by adding a single import 1.3691 + // specifier to the list, with 'default' as the import name and 1.3692 + // 'a' as the binding name. This is equivalent to 1.3693 + // |import { default as a } from 'b'|. 1.3694 + Node importName = newName(context->names().default_); 1.3695 + if (!importName) 1.3696 + return null(); 1.3697 + 1.3698 + Node bindingName = newName(tokenStream.currentName()); 1.3699 + if (!bindingName) 1.3700 + return null(); 1.3701 + 1.3702 + Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName); 1.3703 + if (!importSpec) 1.3704 + return null(); 1.3705 + 1.3706 + handler.addList(importSpecSet, importSpec); 1.3707 + } else { 1.3708 + do { 1.3709 + // Handle the forms |import {} from 'a'| and 1.3710 + // |import { ..., } from 'a'| (where ... is non empty), by 1.3711 + // escaping the loop early if the next token is }. 1.3712 + tt = tokenStream.peekToken(TokenStream::KeywordIsName); 1.3713 + if (tt == TOK_ERROR) 1.3714 + return null(); 1.3715 + if (tt == TOK_RC) 1.3716 + break; 1.3717 + 1.3718 + // If the next token is a keyword, the previous call to 1.3719 + // peekToken matched it as a TOK_NAME, and put it in the 1.3720 + // lookahead buffer, so this call will match keywords as well. 1.3721 + MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME); 1.3722 + Node importName = newName(tokenStream.currentName()); 1.3723 + if (!importName) 1.3724 + return null(); 1.3725 + 1.3726 + if (tokenStream.getToken() == TOK_NAME && 1.3727 + tokenStream.currentName() == context->names().as) 1.3728 + { 1.3729 + if (tokenStream.getToken() != TOK_NAME) { 1.3730 + report(ParseError, false, null(), JSMSG_NO_BINDING_NAME); 1.3731 + return null(); 1.3732 + } 1.3733 + } else { 1.3734 + // Keywords cannot be bound to themselves, so an import name 1.3735 + // that is a keyword is a syntax error if it is not followed 1.3736 + // by the keyword 'as'. 1.3737 + if (IsKeyword(importName->name())) { 1.3738 + JSAutoByteString bytes; 1.3739 + if (!AtomToPrintableString(context, importName->name(), &bytes)) 1.3740 + return null(); 1.3741 + report(ParseError, false, null(), JSMSG_AS_AFTER_RESERVED_WORD, bytes.ptr()); 1.3742 + return null(); 1.3743 + } 1.3744 + tokenStream.ungetToken(); 1.3745 + } 1.3746 + Node bindingName = newName(tokenStream.currentName()); 1.3747 + if (!bindingName) 1.3748 + return null(); 1.3749 + 1.3750 + Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName); 1.3751 + if (!importSpec) 1.3752 + return null(); 1.3753 + 1.3754 + handler.addList(importSpecSet, importSpec); 1.3755 + } while (tokenStream.matchToken(TOK_COMMA)); 1.3756 + 1.3757 + MUST_MATCH_TOKEN(TOK_RC, JSMSG_RC_AFTER_IMPORT_SPEC_LIST); 1.3758 + } 1.3759 + 1.3760 + if (tokenStream.getToken() != TOK_NAME || 1.3761 + tokenStream.currentName() != context->names().from) 1.3762 + { 1.3763 + report(ParseError, false, null(), JSMSG_FROM_AFTER_IMPORT_SPEC_SET); 1.3764 + return null(); 1.3765 + } 1.3766 + 1.3767 + MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM); 1.3768 + } else { 1.3769 + if (tt != TOK_STRING) { 1.3770 + report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_IMPORT); 1.3771 + return null(); 1.3772 + } 1.3773 + 1.3774 + // Handle the form |import 'a'| by leaving the list empty. This is 1.3775 + // equivalent to |import {} from 'a'|. 1.3776 + importSpecSet->pn_pos.end = importSpecSet->pn_pos.begin; 1.3777 + } 1.3778 + 1.3779 + Node moduleSpec = stringLiteral(); 1.3780 + if (!moduleSpec) 1.3781 + return null(); 1.3782 + 1.3783 + if (!MatchOrInsertSemicolon(tokenStream)) 1.3784 + return null(); 1.3785 + 1.3786 + return handler.newImportDeclaration(importSpecSet, moduleSpec, 1.3787 + TokenPos(begin, pos().end)); 1.3788 +} 1.3789 + 1.3790 +template<> 1.3791 +SyntaxParseHandler::Node 1.3792 +Parser<SyntaxParseHandler>::importDeclaration() 1.3793 +{ 1.3794 + JS_ALWAYS_FALSE(abortIfSyntaxParser()); 1.3795 + return SyntaxParseHandler::NodeFailure; 1.3796 +} 1.3797 + 1.3798 +template<typename ParseHandler> 1.3799 +typename ParseHandler::Node 1.3800 +Parser<ParseHandler>::exportDeclaration() 1.3801 +{ 1.3802 + JS_ASSERT(tokenStream.currentToken().type == TOK_EXPORT); 1.3803 + 1.3804 + if (pc->sc->isFunctionBox() || !pc->atBodyLevel()) { 1.3805 + report(ParseError, false, null(), JSMSG_EXPORT_DECL_AT_TOP_LEVEL); 1.3806 + return null(); 1.3807 + } 1.3808 + 1.3809 + uint32_t begin = pos().begin; 1.3810 + 1.3811 + Node kid; 1.3812 + switch (TokenKind tt = tokenStream.getToken()) { 1.3813 + case TOK_LC: 1.3814 + case TOK_MUL: 1.3815 + kid = handler.newList(PNK_EXPORT_SPEC_LIST); 1.3816 + if (!kid) 1.3817 + return null(); 1.3818 + 1.3819 + if (tt == TOK_LC) { 1.3820 + do { 1.3821 + // Handle the forms |export {}| and |export { ..., }| (where ... 1.3822 + // is non empty), by escaping the loop early if the next token 1.3823 + // is }. 1.3824 + tt = tokenStream.peekToken(); 1.3825 + if (tt == TOK_ERROR) 1.3826 + return null(); 1.3827 + if (tt == TOK_RC) 1.3828 + break; 1.3829 + 1.3830 + MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_BINDING_NAME); 1.3831 + Node bindingName = newName(tokenStream.currentName()); 1.3832 + if (!bindingName) 1.3833 + return null(); 1.3834 + 1.3835 + if (tokenStream.getToken() == TOK_NAME && 1.3836 + tokenStream.currentName() == context->names().as) 1.3837 + { 1.3838 + if (tokenStream.getToken(TokenStream::KeywordIsName) != TOK_NAME) { 1.3839 + report(ParseError, false, null(), JSMSG_NO_EXPORT_NAME); 1.3840 + return null(); 1.3841 + } 1.3842 + } else { 1.3843 + tokenStream.ungetToken(); 1.3844 + } 1.3845 + Node exportName = newName(tokenStream.currentName()); 1.3846 + if (!exportName) 1.3847 + return null(); 1.3848 + 1.3849 + Node exportSpec = handler.newBinary(PNK_EXPORT_SPEC, bindingName, exportName); 1.3850 + if (!exportSpec) 1.3851 + return null(); 1.3852 + 1.3853 + handler.addList(kid, exportSpec); 1.3854 + } while (tokenStream.matchToken(TOK_COMMA)); 1.3855 + 1.3856 + MUST_MATCH_TOKEN(TOK_RC, JSMSG_RC_AFTER_EXPORT_SPEC_LIST); 1.3857 + } else { 1.3858 + // Handle the form |export *| by adding a special export batch 1.3859 + // specifier to the list. 1.3860 + Node exportSpec = handler.newNullary(PNK_EXPORT_BATCH_SPEC, JSOP_NOP, pos()); 1.3861 + if (!kid) 1.3862 + return null(); 1.3863 + 1.3864 + handler.addList(kid, exportSpec); 1.3865 + } 1.3866 + if (tokenStream.getToken() == TOK_NAME && 1.3867 + tokenStream.currentName() == context->names().from) 1.3868 + { 1.3869 + MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM); 1.3870 + 1.3871 + Node moduleSpec = stringLiteral(); 1.3872 + if (!moduleSpec) 1.3873 + return null(); 1.3874 + 1.3875 + if (!MatchOrInsertSemicolon(tokenStream)) 1.3876 + return null(); 1.3877 + 1.3878 + return handler.newExportFromDeclaration(begin, kid, moduleSpec); 1.3879 + } else { 1.3880 + tokenStream.ungetToken(); 1.3881 + } 1.3882 + 1.3883 + kid = MatchOrInsertSemicolon(tokenStream) ? kid : nullptr; 1.3884 + if (!kid) 1.3885 + return null(); 1.3886 + break; 1.3887 + 1.3888 + case TOK_FUNCTION: 1.3889 + kid = functionStmt(); 1.3890 + if (!kid) 1.3891 + return null(); 1.3892 + break; 1.3893 + 1.3894 + case TOK_VAR: 1.3895 + case TOK_CONST: 1.3896 + kid = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST); 1.3897 + if (!kid) 1.3898 + return null(); 1.3899 + kid->pn_xflags = PNX_POPVAR; 1.3900 + 1.3901 + kid = MatchOrInsertSemicolon(tokenStream) ? kid : nullptr; 1.3902 + if (!kid) 1.3903 + return null(); 1.3904 + break; 1.3905 + 1.3906 + case TOK_NAME: 1.3907 + // Handle the form |export a} in the same way as |export let a|, by 1.3908 + // acting as if we've just seen the let keyword. Simply unget the token 1.3909 + // and fall through. 1.3910 + tokenStream.ungetToken(); 1.3911 + case TOK_LET: 1.3912 + kid = letDeclaration(); 1.3913 + if (!kid) 1.3914 + return null(); 1.3915 + break; 1.3916 + 1.3917 + default: 1.3918 + report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_EXPORT); 1.3919 + return null(); 1.3920 + } 1.3921 + 1.3922 + return handler.newExportDeclaration(kid, TokenPos(begin, pos().end)); 1.3923 +} 1.3924 + 1.3925 +template<> 1.3926 +SyntaxParseHandler::Node 1.3927 +Parser<SyntaxParseHandler>::exportDeclaration() 1.3928 +{ 1.3929 + JS_ALWAYS_FALSE(abortIfSyntaxParser()); 1.3930 + return SyntaxParseHandler::NodeFailure; 1.3931 +} 1.3932 + 1.3933 + 1.3934 +template <typename ParseHandler> 1.3935 +typename ParseHandler::Node 1.3936 +Parser<ParseHandler>::expressionStatement() 1.3937 +{ 1.3938 + tokenStream.ungetToken(); 1.3939 + Node pnexpr = expr(); 1.3940 + if (!pnexpr) 1.3941 + return null(); 1.3942 + if (!MatchOrInsertSemicolon(tokenStream)) 1.3943 + return null(); 1.3944 + return handler.newExprStatement(pnexpr, pos().end); 1.3945 +} 1.3946 + 1.3947 +template <typename ParseHandler> 1.3948 +typename ParseHandler::Node 1.3949 +Parser<ParseHandler>::ifStatement() 1.3950 +{ 1.3951 + uint32_t begin = pos().begin; 1.3952 + 1.3953 + /* An IF node has three kids: condition, then, and optional else. */ 1.3954 + Node cond = condition(); 1.3955 + if (!cond) 1.3956 + return null(); 1.3957 + 1.3958 + if (tokenStream.peekToken(TokenStream::Operand) == TOK_SEMI && 1.3959 + !report(ParseExtraWarning, false, null(), JSMSG_EMPTY_CONSEQUENT)) 1.3960 + { 1.3961 + return null(); 1.3962 + } 1.3963 + 1.3964 + StmtInfoPC stmtInfo(context); 1.3965 + PushStatementPC(pc, &stmtInfo, STMT_IF); 1.3966 + Node thenBranch = statement(); 1.3967 + if (!thenBranch) 1.3968 + return null(); 1.3969 + 1.3970 + Node elseBranch; 1.3971 + if (tokenStream.matchToken(TOK_ELSE, TokenStream::Operand)) { 1.3972 + stmtInfo.type = STMT_ELSE; 1.3973 + elseBranch = statement(); 1.3974 + if (!elseBranch) 1.3975 + return null(); 1.3976 + } else { 1.3977 + elseBranch = null(); 1.3978 + } 1.3979 + 1.3980 + PopStatementPC(tokenStream, pc); 1.3981 + return handler.newIfStatement(begin, cond, thenBranch, elseBranch); 1.3982 +} 1.3983 + 1.3984 +template <typename ParseHandler> 1.3985 +typename ParseHandler::Node 1.3986 +Parser<ParseHandler>::doWhileStatement() 1.3987 +{ 1.3988 + uint32_t begin = pos().begin; 1.3989 + StmtInfoPC stmtInfo(context); 1.3990 + PushStatementPC(pc, &stmtInfo, STMT_DO_LOOP); 1.3991 + Node body = statement(); 1.3992 + if (!body) 1.3993 + return null(); 1.3994 + MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO); 1.3995 + Node cond = condition(); 1.3996 + if (!cond) 1.3997 + return null(); 1.3998 + PopStatementPC(tokenStream, pc); 1.3999 + 1.4000 + if (versionNumber() == JSVERSION_ECMA_3) { 1.4001 + // Pedantically require a semicolon or line break, following ES3. 1.4002 + // Bug 880329 proposes removing this case. 1.4003 + if (!MatchOrInsertSemicolon(tokenStream)) 1.4004 + return null(); 1.4005 + } else { 1.4006 + // The semicolon after do-while is even more optional than most 1.4007 + // semicolons in JS. Web compat required this by 2004: 1.4008 + // http://bugzilla.mozilla.org/show_bug.cgi?id=238945 1.4009 + // ES3 and ES5 disagreed, but ES6 conforms to Web reality: 1.4010 + // https://bugs.ecmascript.org/show_bug.cgi?id=157 1.4011 + (void) tokenStream.matchToken(TOK_SEMI); 1.4012 + } 1.4013 + 1.4014 + return handler.newDoWhileStatement(body, cond, TokenPos(begin, pos().end)); 1.4015 +} 1.4016 + 1.4017 +template <typename ParseHandler> 1.4018 +typename ParseHandler::Node 1.4019 +Parser<ParseHandler>::whileStatement() 1.4020 +{ 1.4021 + uint32_t begin = pos().begin; 1.4022 + StmtInfoPC stmtInfo(context); 1.4023 + PushStatementPC(pc, &stmtInfo, STMT_WHILE_LOOP); 1.4024 + Node cond = condition(); 1.4025 + if (!cond) 1.4026 + return null(); 1.4027 + Node body = statement(); 1.4028 + if (!body) 1.4029 + return null(); 1.4030 + PopStatementPC(tokenStream, pc); 1.4031 + return handler.newWhileStatement(begin, cond, body); 1.4032 +} 1.4033 + 1.4034 +template <typename ParseHandler> 1.4035 +bool 1.4036 +Parser<ParseHandler>::matchInOrOf(bool *isForOfp) 1.4037 +{ 1.4038 + if (tokenStream.matchToken(TOK_IN)) { 1.4039 + *isForOfp = false; 1.4040 + return true; 1.4041 + } 1.4042 + if (tokenStream.matchContextualKeyword(context->names().of)) { 1.4043 + *isForOfp = true; 1.4044 + return true; 1.4045 + } 1.4046 + return false; 1.4047 +} 1.4048 + 1.4049 +template <> 1.4050 +bool 1.4051 +Parser<FullParseHandler>::isValidForStatementLHS(ParseNode *pn1, JSVersion version, 1.4052 + bool isForDecl, bool isForEach, 1.4053 + ParseNodeKind headKind) 1.4054 +{ 1.4055 + if (isForDecl) { 1.4056 + if (pn1->pn_count > 1) 1.4057 + return false; 1.4058 + if (pn1->isOp(JSOP_DEFCONST)) 1.4059 + return false; 1.4060 + 1.4061 + // In JS 1.7 only, for (var [K, V] in EXPR) has a special meaning. 1.4062 + // Hence all other destructuring decls are banned there. 1.4063 + if (version == JSVERSION_1_7 && !isForEach && headKind == PNK_FORIN) { 1.4064 + ParseNode *lhs = pn1->pn_head; 1.4065 + if (lhs->isKind(PNK_ASSIGN)) 1.4066 + lhs = lhs->pn_left; 1.4067 + 1.4068 + if (lhs->isKind(PNK_OBJECT)) 1.4069 + return false; 1.4070 + if (lhs->isKind(PNK_ARRAY) && lhs->pn_count != 2) 1.4071 + return false; 1.4072 + } 1.4073 + return true; 1.4074 + } 1.4075 + 1.4076 + switch (pn1->getKind()) { 1.4077 + case PNK_NAME: 1.4078 + case PNK_DOT: 1.4079 + case PNK_CALL: 1.4080 + case PNK_ELEM: 1.4081 + return true; 1.4082 + 1.4083 + case PNK_ARRAY: 1.4084 + case PNK_OBJECT: 1.4085 + // In JS 1.7 only, for ([K, V] in EXPR) has a special meaning. 1.4086 + // Hence all other destructuring left-hand sides are banned there. 1.4087 + if (version == JSVERSION_1_7 && !isForEach && headKind == PNK_FORIN) 1.4088 + return pn1->isKind(PNK_ARRAY) && pn1->pn_count == 2; 1.4089 + return true; 1.4090 + 1.4091 + default: 1.4092 + return false; 1.4093 + } 1.4094 +} 1.4095 + 1.4096 +template <> 1.4097 +ParseNode * 1.4098 +Parser<FullParseHandler>::forStatement() 1.4099 +{ 1.4100 + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); 1.4101 + uint32_t begin = pos().begin; 1.4102 + 1.4103 + StmtInfoPC forStmt(context); 1.4104 + PushStatementPC(pc, &forStmt, STMT_FOR_LOOP); 1.4105 + 1.4106 + bool isForEach = false; 1.4107 + unsigned iflags = 0; 1.4108 + 1.4109 + if (allowsForEachIn() && tokenStream.matchContextualKeyword(context->names().each)) { 1.4110 + iflags = JSITER_FOREACH; 1.4111 + isForEach = true; 1.4112 + } 1.4113 + 1.4114 + MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); 1.4115 + 1.4116 + /* 1.4117 + * True if we have 'for (var/let/const ...)', except in the oddball case 1.4118 + * where 'let' begins a let-expression in 'for (let (...) ...)'. 1.4119 + */ 1.4120 + bool isForDecl = false; 1.4121 + 1.4122 + /* Non-null when isForDecl is true for a 'for (let ...)' statement. */ 1.4123 + RootedStaticBlockObject blockObj(context); 1.4124 + 1.4125 + /* Set to 'x' in 'for (x ;... ;...)' or 'for (x in ...)'. */ 1.4126 + ParseNode *pn1; 1.4127 + 1.4128 + { 1.4129 + TokenKind tt = tokenStream.peekToken(TokenStream::Operand); 1.4130 + if (tt == TOK_SEMI) { 1.4131 + pn1 = nullptr; 1.4132 + } else { 1.4133 + /* 1.4134 + * Set pn1 to a var list or an initializing expression. 1.4135 + * 1.4136 + * Set the parsingForInit flag during parsing of the first clause 1.4137 + * of the for statement. This flag will be used by the RelExpr 1.4138 + * production; if it is set, then the 'in' keyword will not be 1.4139 + * recognized as an operator, leaving it available to be parsed as 1.4140 + * part of a for/in loop. 1.4141 + * 1.4142 + * A side effect of this restriction is that (unparenthesized) 1.4143 + * expressions involving an 'in' operator are illegal in the init 1.4144 + * clause of an ordinary for loop. 1.4145 + */ 1.4146 + pc->parsingForInit = true; 1.4147 + if (tt == TOK_VAR || tt == TOK_CONST) { 1.4148 + isForDecl = true; 1.4149 + tokenStream.consumeKnownToken(tt); 1.4150 + pn1 = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST); 1.4151 + } 1.4152 + else if (tt == TOK_LET) { 1.4153 + handler.disableSyntaxParser(); 1.4154 + (void) tokenStream.getToken(); 1.4155 + if (tokenStream.peekToken() == TOK_LP) { 1.4156 + pn1 = letBlock(LetExpresion); 1.4157 + } else { 1.4158 + isForDecl = true; 1.4159 + blockObj = StaticBlockObject::create(context); 1.4160 + if (!blockObj) 1.4161 + return null(); 1.4162 + pn1 = variables(PNK_LET, nullptr, blockObj, DontHoistVars); 1.4163 + } 1.4164 + } 1.4165 + else { 1.4166 + pn1 = expr(); 1.4167 + } 1.4168 + pc->parsingForInit = false; 1.4169 + if (!pn1) 1.4170 + return null(); 1.4171 + } 1.4172 + } 1.4173 + 1.4174 + JS_ASSERT_IF(isForDecl, pn1->isArity(PN_LIST)); 1.4175 + JS_ASSERT(!!blockObj == (isForDecl && pn1->isOp(JSOP_NOP))); 1.4176 + 1.4177 + // The form 'for (let <vars>; <expr2>; <expr3>) <stmt>' generates an 1.4178 + // implicit block even if stmt is not a BlockStatement. 1.4179 + // If the loop has that exact form, then: 1.4180 + // - forLetImpliedBlock is the node for the implicit block scope. 1.4181 + // - forLetDecl is the node for the decl 'let <vars>'. 1.4182 + // Otherwise both are null. 1.4183 + ParseNode *forLetImpliedBlock = nullptr; 1.4184 + ParseNode *forLetDecl = nullptr; 1.4185 + 1.4186 + // If non-null, the node for the decl 'var v = expr1' in the weirdo form 1.4187 + // 'for (var v = expr1 in expr2) stmt'. 1.4188 + ParseNode *hoistedVar = nullptr; 1.4189 + 1.4190 + /* 1.4191 + * We can be sure that it's a for/in loop if there's still an 'in' 1.4192 + * keyword here, even if JavaScript recognizes 'in' as an operator, 1.4193 + * as we've excluded 'in' from being parsed in RelExpr by setting 1.4194 + * pc->parsingForInit. 1.4195 + */ 1.4196 + StmtInfoPC letStmt(context); /* used if blockObj != nullptr. */ 1.4197 + ParseNode *pn2, *pn3; /* forHead->pn_kid2 and pn_kid3. */ 1.4198 + ParseNodeKind headKind = PNK_FORHEAD; 1.4199 + if (pn1) { 1.4200 + bool isForOf; 1.4201 + if (matchInOrOf(&isForOf)) 1.4202 + headKind = isForOf ? PNK_FOROF : PNK_FORIN; 1.4203 + } 1.4204 + 1.4205 + if (headKind == PNK_FOROF || headKind == PNK_FORIN) { 1.4206 + /* 1.4207 + * Parse the rest of the for/in or for/of head. 1.4208 + * 1.4209 + * Here pn1 is everything to the left of 'in' or 'of'. At the end of 1.4210 + * this block, pn1 is a decl or nullptr, pn2 is the assignment target 1.4211 + * that receives the enumeration value each iteration, and pn3 is the 1.4212 + * rhs of 'in'. 1.4213 + */ 1.4214 + if (headKind == PNK_FOROF) { 1.4215 + forStmt.type = STMT_FOR_OF_LOOP; 1.4216 + forStmt.type = (headKind == PNK_FOROF) ? STMT_FOR_OF_LOOP : STMT_FOR_IN_LOOP; 1.4217 + if (isForEach) { 1.4218 + report(ParseError, false, null(), JSMSG_BAD_FOR_EACH_LOOP); 1.4219 + return null(); 1.4220 + } 1.4221 + } else { 1.4222 + forStmt.type = STMT_FOR_IN_LOOP; 1.4223 + iflags |= JSITER_ENUMERATE; 1.4224 + } 1.4225 + 1.4226 + /* Check that the left side of the 'in' or 'of' is valid. */ 1.4227 + if (!isValidForStatementLHS(pn1, versionNumber(), isForDecl, isForEach, headKind)) { 1.4228 + report(ParseError, false, pn1, JSMSG_BAD_FOR_LEFTSIDE); 1.4229 + return null(); 1.4230 + } 1.4231 + 1.4232 + /* 1.4233 + * After the following if-else, pn2 will point to the name or 1.4234 + * destructuring pattern on in's left. pn1 will point to the decl, if 1.4235 + * any, else nullptr. Note that the "declaration with initializer" case 1.4236 + * rewrites the loop-head, moving the decl and setting pn1 to nullptr. 1.4237 + */ 1.4238 + if (isForDecl) { 1.4239 + pn2 = pn1->pn_head; 1.4240 + if ((pn2->isKind(PNK_NAME) && pn2->maybeExpr()) || pn2->isKind(PNK_ASSIGN)) { 1.4241 + /* 1.4242 + * Declaration with initializer. 1.4243 + * 1.4244 + * Rewrite 'for (<decl> x = i in o)' where <decl> is 'var' or 1.4245 + * 'const' to hoist the initializer or the entire decl out of 1.4246 + * the loop head. 1.4247 + */ 1.4248 + if (headKind == PNK_FOROF) { 1.4249 + report(ParseError, false, pn2, JSMSG_INVALID_FOR_OF_INIT); 1.4250 + return null(); 1.4251 + } 1.4252 + if (blockObj) { 1.4253 + report(ParseError, false, pn2, JSMSG_INVALID_FOR_IN_INIT); 1.4254 + return null(); 1.4255 + } 1.4256 + 1.4257 + hoistedVar = pn1; 1.4258 + 1.4259 + /* 1.4260 + * All of 'var x = i' is hoisted above 'for (x in o)'. 1.4261 + * 1.4262 + * Request JSOP_POP here since the var is for a simple 1.4263 + * name (it is not a destructuring binding's left-hand 1.4264 + * side) and it has an initializer. 1.4265 + */ 1.4266 + pn1->pn_xflags |= PNX_POPVAR; 1.4267 + pn1 = nullptr; 1.4268 + 1.4269 + if (pn2->isKind(PNK_ASSIGN)) { 1.4270 + pn2 = pn2->pn_left; 1.4271 + JS_ASSERT(pn2->isKind(PNK_ARRAY) || pn2->isKind(PNK_OBJECT) || 1.4272 + pn2->isKind(PNK_NAME)); 1.4273 + } 1.4274 + } 1.4275 + } else { 1.4276 + /* Not a declaration. */ 1.4277 + JS_ASSERT(!blockObj); 1.4278 + pn2 = pn1; 1.4279 + pn1 = nullptr; 1.4280 + 1.4281 + if (!checkAndMarkAsAssignmentLhs(pn2, PlainAssignment)) 1.4282 + return null(); 1.4283 + } 1.4284 + 1.4285 + pn3 = (headKind == PNK_FOROF) ? assignExpr() : expr(); 1.4286 + if (!pn3) 1.4287 + return null(); 1.4288 + 1.4289 + if (blockObj) { 1.4290 + /* 1.4291 + * Now that the pn3 has been parsed, push the let scope. To hold 1.4292 + * the blockObj for the emitter, wrap the PNK_LEXICALSCOPE node 1.4293 + * created by PushLetScope around the for's initializer. This also 1.4294 + * serves to indicate the let-decl to the emitter. 1.4295 + */ 1.4296 + ParseNode *block = pushLetScope(blockObj, &letStmt); 1.4297 + if (!block) 1.4298 + return null(); 1.4299 + letStmt.isForLetBlock = true; 1.4300 + block->pn_expr = pn1; 1.4301 + block->pn_pos = pn1->pn_pos; 1.4302 + pn1 = block; 1.4303 + } 1.4304 + 1.4305 + if (isForDecl) { 1.4306 + /* 1.4307 + * pn2 is part of a declaration. Make a copy that can be passed to 1.4308 + * EmitAssignment. Take care to do this after PushLetScope. 1.4309 + */ 1.4310 + pn2 = cloneLeftHandSide(pn2); 1.4311 + if (!pn2) 1.4312 + return null(); 1.4313 + } 1.4314 + 1.4315 + switch (pn2->getKind()) { 1.4316 + case PNK_NAME: 1.4317 + /* Beware 'for (arguments in ...)' with or without a 'var'. */ 1.4318 + pn2->markAsAssigned(); 1.4319 + break; 1.4320 + 1.4321 + case PNK_ASSIGN: 1.4322 + MOZ_ASSUME_UNREACHABLE("forStatement TOK_ASSIGN"); 1.4323 + 1.4324 + case PNK_ARRAY: 1.4325 + case PNK_OBJECT: 1.4326 + if (versionNumber() == JSVERSION_1_7) { 1.4327 + /* 1.4328 + * Destructuring for-in requires [key, value] enumeration 1.4329 + * in JS1.7. 1.4330 + */ 1.4331 + if (!isForEach && headKind == PNK_FORIN) 1.4332 + iflags |= JSITER_FOREACH | JSITER_KEYVALUE; 1.4333 + } 1.4334 + break; 1.4335 + 1.4336 + default:; 1.4337 + } 1.4338 + } else { 1.4339 + if (isForEach) { 1.4340 + reportWithOffset(ParseError, false, begin, JSMSG_BAD_FOR_EACH_LOOP); 1.4341 + return null(); 1.4342 + } 1.4343 + 1.4344 + headKind = PNK_FORHEAD; 1.4345 + 1.4346 + if (blockObj) { 1.4347 + /* 1.4348 + * Desugar 'for (let A; B; C) D' into 'let (A) { for (; B; C) D }' 1.4349 + * to induce the correct scoping for A. 1.4350 + */ 1.4351 + forLetImpliedBlock = pushLetScope(blockObj, &letStmt); 1.4352 + if (!forLetImpliedBlock) 1.4353 + return null(); 1.4354 + letStmt.isForLetBlock = true; 1.4355 + 1.4356 + forLetDecl = pn1; 1.4357 + pn1 = nullptr; 1.4358 + } 1.4359 + 1.4360 + /* Parse the loop condition or null into pn2. */ 1.4361 + MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT); 1.4362 + if (tokenStream.peekToken(TokenStream::Operand) == TOK_SEMI) { 1.4363 + pn2 = nullptr; 1.4364 + } else { 1.4365 + pn2 = expr(); 1.4366 + if (!pn2) 1.4367 + return null(); 1.4368 + } 1.4369 + 1.4370 + /* Parse the update expression or null into pn3. */ 1.4371 + MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND); 1.4372 + if (tokenStream.peekToken(TokenStream::Operand) == TOK_RP) { 1.4373 + pn3 = nullptr; 1.4374 + } else { 1.4375 + pn3 = expr(); 1.4376 + if (!pn3) 1.4377 + return null(); 1.4378 + } 1.4379 + } 1.4380 + 1.4381 + MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL); 1.4382 + 1.4383 + TokenPos headPos(begin, pos().end); 1.4384 + ParseNode *forHead = handler.newForHead(headKind, pn1, pn2, pn3, headPos); 1.4385 + if (!forHead) 1.4386 + return null(); 1.4387 + 1.4388 + /* Parse the loop body. */ 1.4389 + ParseNode *body = statement(); 1.4390 + if (!body) 1.4391 + return null(); 1.4392 + 1.4393 + if (blockObj) 1.4394 + PopStatementPC(tokenStream, pc); 1.4395 + PopStatementPC(tokenStream, pc); 1.4396 + 1.4397 + ParseNode *forLoop = handler.newForStatement(begin, forHead, body, iflags); 1.4398 + if (!forLoop) 1.4399 + return null(); 1.4400 + 1.4401 + if (hoistedVar) { 1.4402 + ParseNode *pnseq = handler.newList(PNK_SEQ, hoistedVar); 1.4403 + if (!pnseq) 1.4404 + return null(); 1.4405 + pnseq->pn_pos = forLoop->pn_pos; 1.4406 + pnseq->append(forLoop); 1.4407 + return pnseq; 1.4408 + } 1.4409 + if (forLetImpliedBlock) { 1.4410 + forLetImpliedBlock->pn_expr = forLoop; 1.4411 + forLetImpliedBlock->pn_pos = forLoop->pn_pos; 1.4412 + ParseNode *let = handler.newBinary(PNK_LET, forLetDecl, forLetImpliedBlock); 1.4413 + if (!let) 1.4414 + return null(); 1.4415 + let->pn_pos = forLoop->pn_pos; 1.4416 + return let; 1.4417 + } 1.4418 + return forLoop; 1.4419 +} 1.4420 + 1.4421 +template <> 1.4422 +SyntaxParseHandler::Node 1.4423 +Parser<SyntaxParseHandler>::forStatement() 1.4424 +{ 1.4425 + /* 1.4426 + * 'for' statement parsing is fantastically complicated and requires being 1.4427 + * able to inspect the parse tree for previous parts of the 'for'. Syntax 1.4428 + * parsing of 'for' statements is thus done separately, and only handles 1.4429 + * the types of 'for' statements likely to be seen in web content. 1.4430 + */ 1.4431 + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); 1.4432 + 1.4433 + StmtInfoPC forStmt(context); 1.4434 + PushStatementPC(pc, &forStmt, STMT_FOR_LOOP); 1.4435 + 1.4436 + /* Don't parse 'for each' loops. */ 1.4437 + if (allowsForEachIn()) { 1.4438 + TokenKind tt = tokenStream.peekToken(); 1.4439 + // Not all "yield" tokens are names, but the ones that aren't names are 1.4440 + // invalid in this context anyway. 1.4441 + if (tt == TOK_NAME || tt == TOK_YIELD) { 1.4442 + JS_ALWAYS_FALSE(abortIfSyntaxParser()); 1.4443 + return null(); 1.4444 + } 1.4445 + } 1.4446 + 1.4447 + MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); 1.4448 + 1.4449 + /* True if we have 'for (var ...)'. */ 1.4450 + bool isForDecl = false; 1.4451 + bool simpleForDecl = true; 1.4452 + 1.4453 + /* Set to 'x' in 'for (x ;... ;...)' or 'for (x in ...)'. */ 1.4454 + Node lhsNode; 1.4455 + 1.4456 + { 1.4457 + TokenKind tt = tokenStream.peekToken(TokenStream::Operand); 1.4458 + if (tt == TOK_SEMI) { 1.4459 + lhsNode = null(); 1.4460 + } else { 1.4461 + /* Set lhsNode to a var list or an initializing expression. */ 1.4462 + pc->parsingForInit = true; 1.4463 + if (tt == TOK_VAR) { 1.4464 + isForDecl = true; 1.4465 + tokenStream.consumeKnownToken(tt); 1.4466 + lhsNode = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST, &simpleForDecl); 1.4467 + } 1.4468 + else if (tt == TOK_CONST || tt == TOK_LET) { 1.4469 + JS_ALWAYS_FALSE(abortIfSyntaxParser()); 1.4470 + return null(); 1.4471 + } 1.4472 + else { 1.4473 + lhsNode = expr(); 1.4474 + } 1.4475 + if (!lhsNode) 1.4476 + return null(); 1.4477 + pc->parsingForInit = false; 1.4478 + } 1.4479 + } 1.4480 + 1.4481 + /* 1.4482 + * We can be sure that it's a for/in loop if there's still an 'in' 1.4483 + * keyword here, even if JavaScript recognizes 'in' as an operator, 1.4484 + * as we've excluded 'in' from being parsed in RelExpr by setting 1.4485 + * pc->parsingForInit. 1.4486 + */ 1.4487 + bool isForOf; 1.4488 + if (lhsNode && matchInOrOf(&isForOf)) { 1.4489 + /* Parse the rest of the for/in or for/of head. */ 1.4490 + forStmt.type = isForOf ? STMT_FOR_OF_LOOP : STMT_FOR_IN_LOOP; 1.4491 + 1.4492 + /* Check that the left side of the 'in' or 'of' is valid. */ 1.4493 + if (!isForDecl && 1.4494 + lhsNode != SyntaxParseHandler::NodeName && 1.4495 + lhsNode != SyntaxParseHandler::NodeGetProp && 1.4496 + lhsNode != SyntaxParseHandler::NodeLValue) 1.4497 + { 1.4498 + JS_ALWAYS_FALSE(abortIfSyntaxParser()); 1.4499 + return null(); 1.4500 + } 1.4501 + 1.4502 + if (!simpleForDecl) { 1.4503 + JS_ALWAYS_FALSE(abortIfSyntaxParser()); 1.4504 + return null(); 1.4505 + } 1.4506 + 1.4507 + if (!isForDecl && !checkAndMarkAsAssignmentLhs(lhsNode, PlainAssignment)) 1.4508 + return null(); 1.4509 + 1.4510 + if (!expr()) 1.4511 + return null(); 1.4512 + } else { 1.4513 + /* Parse the loop condition or null. */ 1.4514 + MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT); 1.4515 + if (tokenStream.peekToken(TokenStream::Operand) != TOK_SEMI) { 1.4516 + if (!expr()) 1.4517 + return null(); 1.4518 + } 1.4519 + 1.4520 + /* Parse the update expression or null. */ 1.4521 + MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND); 1.4522 + if (tokenStream.peekToken(TokenStream::Operand) != TOK_RP) { 1.4523 + if (!expr()) 1.4524 + return null(); 1.4525 + } 1.4526 + } 1.4527 + 1.4528 + MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL); 1.4529 + 1.4530 + /* Parse the loop body. */ 1.4531 + if (!statement()) 1.4532 + return null(); 1.4533 + 1.4534 + PopStatementPC(tokenStream, pc); 1.4535 + return SyntaxParseHandler::NodeGeneric; 1.4536 +} 1.4537 + 1.4538 +template <typename ParseHandler> 1.4539 +typename ParseHandler::Node 1.4540 +Parser<ParseHandler>::switchStatement() 1.4541 +{ 1.4542 + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_SWITCH)); 1.4543 + uint32_t begin = pos().begin; 1.4544 + 1.4545 + MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH); 1.4546 + 1.4547 + Node discriminant = exprInParens(); 1.4548 + if (!discriminant) 1.4549 + return null(); 1.4550 + 1.4551 + MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH); 1.4552 + MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH); 1.4553 + 1.4554 + StmtInfoPC stmtInfo(context); 1.4555 + PushStatementPC(pc, &stmtInfo, STMT_SWITCH); 1.4556 + 1.4557 + if (!GenerateBlockId(tokenStream, pc, pc->topStmt->blockid)) 1.4558 + return null(); 1.4559 + 1.4560 + Node caseList = handler.newStatementList(pc->blockid(), pos()); 1.4561 + if (!caseList) 1.4562 + return null(); 1.4563 + 1.4564 + Node saveBlock = pc->blockNode; 1.4565 + pc->blockNode = caseList; 1.4566 + 1.4567 + bool seenDefault = false; 1.4568 + TokenKind tt; 1.4569 + while ((tt = tokenStream.getToken()) != TOK_RC) { 1.4570 + uint32_t caseBegin = pos().begin; 1.4571 + 1.4572 + Node caseExpr; 1.4573 + switch (tt) { 1.4574 + case TOK_DEFAULT: 1.4575 + if (seenDefault) { 1.4576 + report(ParseError, false, null(), JSMSG_TOO_MANY_DEFAULTS); 1.4577 + return null(); 1.4578 + } 1.4579 + seenDefault = true; 1.4580 + caseExpr = null(); // The default case has pn_left == nullptr. 1.4581 + break; 1.4582 + 1.4583 + case TOK_CASE: 1.4584 + caseExpr = expr(); 1.4585 + if (!caseExpr) 1.4586 + return null(); 1.4587 + break; 1.4588 + 1.4589 + case TOK_ERROR: 1.4590 + return null(); 1.4591 + 1.4592 + default: 1.4593 + report(ParseError, false, null(), JSMSG_BAD_SWITCH); 1.4594 + return null(); 1.4595 + } 1.4596 + 1.4597 + MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE); 1.4598 + 1.4599 + Node body = handler.newStatementList(pc->blockid(), pos()); 1.4600 + if (!body) 1.4601 + return null(); 1.4602 + 1.4603 + while ((tt = tokenStream.peekToken(TokenStream::Operand)) != TOK_RC && 1.4604 + tt != TOK_CASE && tt != TOK_DEFAULT) { 1.4605 + if (tt == TOK_ERROR) 1.4606 + return null(); 1.4607 + Node stmt = statement(); 1.4608 + if (!stmt) 1.4609 + return null(); 1.4610 + handler.addList(body, stmt); 1.4611 + } 1.4612 + 1.4613 + Node casepn = handler.newCaseOrDefault(caseBegin, caseExpr, body); 1.4614 + if (!casepn) 1.4615 + return null(); 1.4616 + handler.addList(caseList, casepn); 1.4617 + } 1.4618 + 1.4619 + /* 1.4620 + * Handle the case where there was a let declaration in any case in 1.4621 + * the switch body, but not within an inner block. If it replaced 1.4622 + * pc->blockNode with a new block node then we must refresh caseList and 1.4623 + * then restore pc->blockNode. 1.4624 + */ 1.4625 + if (pc->blockNode != caseList) 1.4626 + caseList = pc->blockNode; 1.4627 + pc->blockNode = saveBlock; 1.4628 + 1.4629 + PopStatementPC(tokenStream, pc); 1.4630 + 1.4631 + handler.setEndPosition(caseList, pos().end); 1.4632 + 1.4633 + return handler.newSwitchStatement(begin, discriminant, caseList); 1.4634 +} 1.4635 + 1.4636 +template <typename ParseHandler> 1.4637 +typename ParseHandler::Node 1.4638 +Parser<ParseHandler>::continueStatement() 1.4639 +{ 1.4640 + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_CONTINUE)); 1.4641 + uint32_t begin = pos().begin; 1.4642 + 1.4643 + RootedPropertyName label(context); 1.4644 + if (!matchLabel(&label)) 1.4645 + return null(); 1.4646 + 1.4647 + StmtInfoPC *stmt = pc->topStmt; 1.4648 + if (label) { 1.4649 + for (StmtInfoPC *stmt2 = nullptr; ; stmt = stmt->down) { 1.4650 + if (!stmt) { 1.4651 + report(ParseError, false, null(), JSMSG_LABEL_NOT_FOUND); 1.4652 + return null(); 1.4653 + } 1.4654 + if (stmt->type == STMT_LABEL) { 1.4655 + if (stmt->label == label) { 1.4656 + if (!stmt2 || !stmt2->isLoop()) { 1.4657 + report(ParseError, false, null(), JSMSG_BAD_CONTINUE); 1.4658 + return null(); 1.4659 + } 1.4660 + break; 1.4661 + } 1.4662 + } else { 1.4663 + stmt2 = stmt; 1.4664 + } 1.4665 + } 1.4666 + } else { 1.4667 + for (; ; stmt = stmt->down) { 1.4668 + if (!stmt) { 1.4669 + report(ParseError, false, null(), JSMSG_BAD_CONTINUE); 1.4670 + return null(); 1.4671 + } 1.4672 + if (stmt->isLoop()) 1.4673 + break; 1.4674 + } 1.4675 + } 1.4676 + 1.4677 + if (!MatchOrInsertSemicolon(tokenStream)) 1.4678 + return null(); 1.4679 + 1.4680 + return handler.newContinueStatement(label, TokenPos(begin, pos().end)); 1.4681 +} 1.4682 + 1.4683 +template <typename ParseHandler> 1.4684 +typename ParseHandler::Node 1.4685 +Parser<ParseHandler>::breakStatement() 1.4686 +{ 1.4687 + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_BREAK)); 1.4688 + uint32_t begin = pos().begin; 1.4689 + 1.4690 + RootedPropertyName label(context); 1.4691 + if (!matchLabel(&label)) 1.4692 + return null(); 1.4693 + StmtInfoPC *stmt = pc->topStmt; 1.4694 + if (label) { 1.4695 + for (; ; stmt = stmt->down) { 1.4696 + if (!stmt) { 1.4697 + report(ParseError, false, null(), JSMSG_LABEL_NOT_FOUND); 1.4698 + return null(); 1.4699 + } 1.4700 + if (stmt->type == STMT_LABEL && stmt->label == label) 1.4701 + break; 1.4702 + } 1.4703 + } else { 1.4704 + for (; ; stmt = stmt->down) { 1.4705 + if (!stmt) { 1.4706 + report(ParseError, false, null(), JSMSG_TOUGH_BREAK); 1.4707 + return null(); 1.4708 + } 1.4709 + if (stmt->isLoop() || stmt->type == STMT_SWITCH) 1.4710 + break; 1.4711 + } 1.4712 + } 1.4713 + 1.4714 + if (!MatchOrInsertSemicolon(tokenStream)) 1.4715 + return null(); 1.4716 + 1.4717 + return handler.newBreakStatement(label, TokenPos(begin, pos().end)); 1.4718 +} 1.4719 + 1.4720 +template <typename ParseHandler> 1.4721 +typename ParseHandler::Node 1.4722 +Parser<ParseHandler>::returnStatement() 1.4723 +{ 1.4724 + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_RETURN)); 1.4725 + uint32_t begin = pos().begin; 1.4726 + 1.4727 + if (!pc->sc->isFunctionBox()) { 1.4728 + report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_return_str); 1.4729 + return null(); 1.4730 + } 1.4731 + 1.4732 + // Parse an optional operand. 1.4733 + // 1.4734 + // This is ugly, but we don't want to require a semicolon. 1.4735 + Node exprNode; 1.4736 + switch (tokenStream.peekTokenSameLine(TokenStream::Operand)) { 1.4737 + case TOK_ERROR: 1.4738 + return null(); 1.4739 + case TOK_EOF: 1.4740 + case TOK_EOL: 1.4741 + case TOK_SEMI: 1.4742 + case TOK_RC: 1.4743 + exprNode = null(); 1.4744 + pc->funHasReturnVoid = true; 1.4745 + break; 1.4746 + default: { 1.4747 + exprNode = expr(); 1.4748 + if (!exprNode) 1.4749 + return null(); 1.4750 + pc->funHasReturnExpr = true; 1.4751 + } 1.4752 + } 1.4753 + 1.4754 + if (!MatchOrInsertSemicolon(tokenStream)) 1.4755 + return null(); 1.4756 + 1.4757 + Node pn = handler.newReturnStatement(exprNode, TokenPos(begin, pos().end)); 1.4758 + if (!pn) 1.4759 + return null(); 1.4760 + 1.4761 + if (options().extraWarningsOption && pc->funHasReturnExpr && pc->funHasReturnVoid && 1.4762 + !reportBadReturn(pn, ParseExtraWarning, 1.4763 + JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE)) 1.4764 + { 1.4765 + return null(); 1.4766 + } 1.4767 + 1.4768 + if (pc->isLegacyGenerator() && exprNode) { 1.4769 + /* Disallow "return v;" in legacy generators. */ 1.4770 + reportBadReturn(pn, ParseError, JSMSG_BAD_GENERATOR_RETURN, 1.4771 + JSMSG_BAD_ANON_GENERATOR_RETURN); 1.4772 + return null(); 1.4773 + } 1.4774 + 1.4775 + return pn; 1.4776 +} 1.4777 + 1.4778 +template <typename ParseHandler> 1.4779 +typename ParseHandler::Node 1.4780 +Parser<ParseHandler>::yieldExpression() 1.4781 +{ 1.4782 + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_YIELD)); 1.4783 + uint32_t begin = pos().begin; 1.4784 + 1.4785 + switch (pc->generatorKind()) { 1.4786 + case StarGenerator: 1.4787 + { 1.4788 + JS_ASSERT(pc->sc->isFunctionBox()); 1.4789 + 1.4790 + pc->lastYieldOffset = begin; 1.4791 + 1.4792 + ParseNodeKind kind = tokenStream.matchToken(TOK_MUL) ? PNK_YIELD_STAR : PNK_YIELD; 1.4793 + 1.4794 + // ES6 generators require a value. 1.4795 + Node exprNode = assignExpr(); 1.4796 + if (!exprNode) 1.4797 + return null(); 1.4798 + 1.4799 + return handler.newUnary(kind, JSOP_NOP, begin, exprNode); 1.4800 + } 1.4801 + 1.4802 + case NotGenerator: 1.4803 + // We are in code that has not seen a yield, but we are in JS 1.7 or 1.4804 + // later. Try to transition to being a legacy generator. 1.4805 + JS_ASSERT(tokenStream.versionNumber() >= JSVERSION_1_7); 1.4806 + JS_ASSERT(pc->lastYieldOffset == ParseContext<ParseHandler>::NoYieldOffset); 1.4807 + 1.4808 + if (!abortIfSyntaxParser()) 1.4809 + return null(); 1.4810 + 1.4811 + if (!pc->sc->isFunctionBox()) { 1.4812 + report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_yield_str); 1.4813 + return null(); 1.4814 + } 1.4815 + 1.4816 + pc->sc->asFunctionBox()->setGeneratorKind(LegacyGenerator); 1.4817 + 1.4818 + if (pc->funHasReturnExpr) { 1.4819 + /* As in Python (see PEP-255), disallow return v; in generators. */ 1.4820 + reportBadReturn(null(), ParseError, JSMSG_BAD_GENERATOR_RETURN, 1.4821 + JSMSG_BAD_ANON_GENERATOR_RETURN); 1.4822 + return null(); 1.4823 + } 1.4824 + // Fall through. 1.4825 + 1.4826 + case LegacyGenerator: 1.4827 + { 1.4828 + // We are in a legacy generator: a function that has already seen a 1.4829 + // yield, or in a legacy generator comprehension. 1.4830 + JS_ASSERT(pc->sc->isFunctionBox()); 1.4831 + 1.4832 + pc->lastYieldOffset = begin; 1.4833 + 1.4834 + // Legacy generators do not require a value. 1.4835 + Node exprNode; 1.4836 + switch (tokenStream.peekTokenSameLine(TokenStream::Operand)) { 1.4837 + case TOK_ERROR: 1.4838 + return null(); 1.4839 + case TOK_EOF: 1.4840 + case TOK_EOL: 1.4841 + case TOK_SEMI: 1.4842 + case TOK_RC: 1.4843 + case TOK_RB: 1.4844 + case TOK_RP: 1.4845 + case TOK_COLON: 1.4846 + case TOK_COMMA: 1.4847 + // No value. 1.4848 + exprNode = null(); 1.4849 + // ES6 does not permit yield without an operand. We should 1.4850 + // encourage users of yield expressions of this kind to pass an 1.4851 + // operand, to bring users closer to standard syntax. 1.4852 + if (!reportWithOffset(ParseWarning, false, pos().begin, JSMSG_YIELD_WITHOUT_OPERAND)) 1.4853 + return null(); 1.4854 + break; 1.4855 + default: 1.4856 + exprNode = assignExpr(); 1.4857 + if (!exprNode) 1.4858 + return null(); 1.4859 + } 1.4860 + 1.4861 + return handler.newUnary(PNK_YIELD, JSOP_NOP, begin, exprNode); 1.4862 + } 1.4863 + } 1.4864 + 1.4865 + MOZ_ASSUME_UNREACHABLE("yieldExpr"); 1.4866 +} 1.4867 + 1.4868 +template <> 1.4869 +ParseNode * 1.4870 +Parser<FullParseHandler>::withStatement() 1.4871 +{ 1.4872 + // test262/ch12/12.10/12.10-0-1.js fails if we try to parse with-statements 1.4873 + // in syntax-parse mode. See bug 892583. 1.4874 + if (handler.syntaxParser) { 1.4875 + handler.disableSyntaxParser(); 1.4876 + abortedSyntaxParse = true; 1.4877 + return null(); 1.4878 + } 1.4879 + 1.4880 + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_WITH)); 1.4881 + uint32_t begin = pos().begin; 1.4882 + 1.4883 + // In most cases, we want the constructs forbidden in strict mode code to be 1.4884 + // a subset of those that JSOPTION_EXTRA_WARNINGS warns about, and we should 1.4885 + // use reportStrictModeError. However, 'with' is the sole instance of a 1.4886 + // construct that is forbidden in strict mode code, but doesn't even merit a 1.4887 + // warning under JSOPTION_EXTRA_WARNINGS. See 1.4888 + // https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1. 1.4889 + if (pc->sc->strict && !report(ParseStrictError, true, null(), JSMSG_STRICT_CODE_WITH)) 1.4890 + return null(); 1.4891 + 1.4892 + MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH); 1.4893 + Node objectExpr = exprInParens(); 1.4894 + if (!objectExpr) 1.4895 + return null(); 1.4896 + MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH); 1.4897 + 1.4898 + bool oldParsingWith = pc->parsingWith; 1.4899 + pc->parsingWith = true; 1.4900 + 1.4901 + StmtInfoPC stmtInfo(context); 1.4902 + PushStatementPC(pc, &stmtInfo, STMT_WITH); 1.4903 + Rooted<StaticWithObject *> staticWith(context, StaticWithObject::create(context)); 1.4904 + if (!staticWith) 1.4905 + return null(); 1.4906 + staticWith->initEnclosingNestedScopeFromParser(pc->staticScope); 1.4907 + FinishPushNestedScope(pc, &stmtInfo, *staticWith); 1.4908 + 1.4909 + Node innerBlock = statement(); 1.4910 + if (!innerBlock) 1.4911 + return null(); 1.4912 + 1.4913 + PopStatementPC(tokenStream, pc); 1.4914 + 1.4915 + pc->sc->setBindingsAccessedDynamically(); 1.4916 + pc->parsingWith = oldParsingWith; 1.4917 + 1.4918 + /* 1.4919 + * Make sure to deoptimize lexical dependencies inside the |with| 1.4920 + * to safely optimize binding globals (see bug 561923). 1.4921 + */ 1.4922 + for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) { 1.4923 + DefinitionNode defn = r.front().value().get<FullParseHandler>(); 1.4924 + DefinitionNode lexdep = handler.resolve(defn); 1.4925 + handler.deoptimizeUsesWithin(lexdep, TokenPos(begin, pos().begin)); 1.4926 + } 1.4927 + 1.4928 + ObjectBox *staticWithBox = newObjectBox(staticWith); 1.4929 + if (!staticWithBox) 1.4930 + return null(); 1.4931 + return handler.newWithStatement(begin, objectExpr, innerBlock, staticWithBox); 1.4932 +} 1.4933 + 1.4934 +template <> 1.4935 +SyntaxParseHandler::Node 1.4936 +Parser<SyntaxParseHandler>::withStatement() 1.4937 +{ 1.4938 + JS_ALWAYS_FALSE(abortIfSyntaxParser()); 1.4939 + return null(); 1.4940 +} 1.4941 + 1.4942 +template <typename ParseHandler> 1.4943 +typename ParseHandler::Node 1.4944 +Parser<ParseHandler>::labeledStatement() 1.4945 +{ 1.4946 + uint32_t begin = pos().begin; 1.4947 + RootedPropertyName label(context, tokenStream.currentName()); 1.4948 + for (StmtInfoPC *stmt = pc->topStmt; stmt; stmt = stmt->down) { 1.4949 + if (stmt->type == STMT_LABEL && stmt->label == label) { 1.4950 + report(ParseError, false, null(), JSMSG_DUPLICATE_LABEL); 1.4951 + return null(); 1.4952 + } 1.4953 + } 1.4954 + 1.4955 + tokenStream.consumeKnownToken(TOK_COLON); 1.4956 + 1.4957 + /* Push a label struct and parse the statement. */ 1.4958 + StmtInfoPC stmtInfo(context); 1.4959 + PushStatementPC(pc, &stmtInfo, STMT_LABEL); 1.4960 + stmtInfo.label = label; 1.4961 + Node pn = statement(); 1.4962 + if (!pn) 1.4963 + return null(); 1.4964 + 1.4965 + /* Pop the label, set pn_expr, and return early. */ 1.4966 + PopStatementPC(tokenStream, pc); 1.4967 + 1.4968 + return handler.newLabeledStatement(label, pn, begin); 1.4969 +} 1.4970 + 1.4971 +template <typename ParseHandler> 1.4972 +typename ParseHandler::Node 1.4973 +Parser<ParseHandler>::throwStatement() 1.4974 +{ 1.4975 + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_THROW)); 1.4976 + uint32_t begin = pos().begin; 1.4977 + 1.4978 + /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */ 1.4979 + TokenKind tt = tokenStream.peekTokenSameLine(TokenStream::Operand); 1.4980 + if (tt == TOK_ERROR) 1.4981 + return null(); 1.4982 + if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) { 1.4983 + report(ParseError, false, null(), JSMSG_SYNTAX_ERROR); 1.4984 + return null(); 1.4985 + } 1.4986 + 1.4987 + Node throwExpr = expr(); 1.4988 + if (!throwExpr) 1.4989 + return null(); 1.4990 + 1.4991 + if (!MatchOrInsertSemicolon(tokenStream)) 1.4992 + return null(); 1.4993 + 1.4994 + return handler.newThrowStatement(throwExpr, TokenPos(begin, pos().end)); 1.4995 +} 1.4996 + 1.4997 +template <typename ParseHandler> 1.4998 +typename ParseHandler::Node 1.4999 +Parser<ParseHandler>::tryStatement() 1.5000 +{ 1.5001 + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_TRY)); 1.5002 + uint32_t begin = pos().begin; 1.5003 + 1.5004 + /* 1.5005 + * try nodes are ternary. 1.5006 + * kid1 is the try statement 1.5007 + * kid2 is the catch node list or null 1.5008 + * kid3 is the finally statement 1.5009 + * 1.5010 + * catch nodes are ternary. 1.5011 + * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC) 1.5012 + * kid2 is the catch guard or null if no guard 1.5013 + * kid3 is the catch block 1.5014 + * 1.5015 + * catch lvalue nodes are either: 1.5016 + * TOK_NAME for a single identifier 1.5017 + * TOK_RB or TOK_RC for a destructuring left-hand side 1.5018 + * 1.5019 + * finally nodes are TOK_LC statement lists. 1.5020 + */ 1.5021 + 1.5022 + MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY); 1.5023 + StmtInfoPC stmtInfo(context); 1.5024 + if (!PushBlocklikeStatement(tokenStream, &stmtInfo, STMT_TRY, pc)) 1.5025 + return null(); 1.5026 + Node innerBlock = statements(); 1.5027 + if (!innerBlock) 1.5028 + return null(); 1.5029 + MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY); 1.5030 + PopStatementPC(tokenStream, pc); 1.5031 + 1.5032 + bool hasUnconditionalCatch = false; 1.5033 + Node catchList = null(); 1.5034 + TokenKind tt = tokenStream.getToken(); 1.5035 + if (tt == TOK_CATCH) { 1.5036 + catchList = handler.newList(PNK_CATCH); 1.5037 + if (!catchList) 1.5038 + return null(); 1.5039 + 1.5040 + do { 1.5041 + Node pnblock; 1.5042 + BindData<ParseHandler> data(context); 1.5043 + 1.5044 + /* Check for another catch after unconditional catch. */ 1.5045 + if (hasUnconditionalCatch) { 1.5046 + report(ParseError, false, null(), JSMSG_CATCH_AFTER_GENERAL); 1.5047 + return null(); 1.5048 + } 1.5049 + 1.5050 + /* 1.5051 + * Create a lexical scope node around the whole catch clause, 1.5052 + * including the head. 1.5053 + */ 1.5054 + pnblock = pushLexicalScope(&stmtInfo); 1.5055 + if (!pnblock) 1.5056 + return null(); 1.5057 + stmtInfo.type = STMT_CATCH; 1.5058 + 1.5059 + /* 1.5060 + * Legal catch forms are: 1.5061 + * catch (lhs) 1.5062 + * catch (lhs if <boolean_expression>) 1.5063 + * where lhs is a name or a destructuring left-hand side. 1.5064 + * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD) 1.5065 + */ 1.5066 + MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH); 1.5067 + 1.5068 + /* 1.5069 + * Contrary to ECMA Ed. 3, the catch variable is lexically 1.5070 + * scoped, not a property of a new Object instance. This is 1.5071 + * an intentional change that anticipates ECMA Ed. 4. 1.5072 + */ 1.5073 + data.initLet(HoistVars, pc->staticScope->template as<StaticBlockObject>(), 1.5074 + JSMSG_TOO_MANY_CATCH_VARS); 1.5075 + JS_ASSERT(data.let.blockObj); 1.5076 + 1.5077 + tt = tokenStream.getToken(); 1.5078 + Node catchName; 1.5079 + switch (tt) { 1.5080 + case TOK_LB: 1.5081 + case TOK_LC: 1.5082 + catchName = destructuringExpr(&data, tt); 1.5083 + if (!catchName) 1.5084 + return null(); 1.5085 + break; 1.5086 + 1.5087 + case TOK_YIELD: 1.5088 + if (!checkYieldNameValidity()) 1.5089 + return null(); 1.5090 + // Fall through. 1.5091 + case TOK_NAME: 1.5092 + { 1.5093 + RootedPropertyName label(context, tokenStream.currentName()); 1.5094 + catchName = newBindingNode(label, false); 1.5095 + if (!catchName) 1.5096 + return null(); 1.5097 + data.pn = catchName; 1.5098 + if (!data.binder(&data, label, this)) 1.5099 + return null(); 1.5100 + break; 1.5101 + } 1.5102 + 1.5103 + default: 1.5104 + report(ParseError, false, null(), JSMSG_CATCH_IDENTIFIER); 1.5105 + return null(); 1.5106 + } 1.5107 + 1.5108 + Node catchGuard = null(); 1.5109 +#if JS_HAS_CATCH_GUARD 1.5110 + /* 1.5111 + * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)') 1.5112 + * to avoid conflicting with the JS2/ECMAv4 type annotation 1.5113 + * catchguard syntax. 1.5114 + */ 1.5115 + if (tokenStream.matchToken(TOK_IF)) { 1.5116 + catchGuard = expr(); 1.5117 + if (!catchGuard) 1.5118 + return null(); 1.5119 + } 1.5120 +#endif 1.5121 + MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH); 1.5122 + 1.5123 + MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH); 1.5124 + Node catchBody = statements(); 1.5125 + if (!catchBody) 1.5126 + return null(); 1.5127 + MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH); 1.5128 + PopStatementPC(tokenStream, pc); 1.5129 + 1.5130 + if (!catchGuard) 1.5131 + hasUnconditionalCatch = true; 1.5132 + 1.5133 + if (!handler.addCatchBlock(catchList, pnblock, catchName, catchGuard, catchBody)) 1.5134 + return null(); 1.5135 + handler.setEndPosition(catchList, pos().end); 1.5136 + handler.setEndPosition(pnblock, pos().end); 1.5137 + 1.5138 + tt = tokenStream.getToken(TokenStream::Operand); 1.5139 + } while (tt == TOK_CATCH); 1.5140 + } 1.5141 + 1.5142 + Node finallyBlock = null(); 1.5143 + 1.5144 + if (tt == TOK_FINALLY) { 1.5145 + MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY); 1.5146 + if (!PushBlocklikeStatement(tokenStream, &stmtInfo, STMT_FINALLY, pc)) 1.5147 + return null(); 1.5148 + finallyBlock = statements(); 1.5149 + if (!finallyBlock) 1.5150 + return null(); 1.5151 + MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY); 1.5152 + PopStatementPC(tokenStream, pc); 1.5153 + } else { 1.5154 + tokenStream.ungetToken(); 1.5155 + } 1.5156 + if (!catchList && !finallyBlock) { 1.5157 + report(ParseError, false, null(), JSMSG_CATCH_OR_FINALLY); 1.5158 + return null(); 1.5159 + } 1.5160 + 1.5161 + return handler.newTryStatement(begin, innerBlock, catchList, finallyBlock); 1.5162 +} 1.5163 + 1.5164 +template <typename ParseHandler> 1.5165 +typename ParseHandler::Node 1.5166 +Parser<ParseHandler>::debuggerStatement() 1.5167 +{ 1.5168 + TokenPos p; 1.5169 + p.begin = pos().begin; 1.5170 + if (!MatchOrInsertSemicolon(tokenStream)) 1.5171 + return null(); 1.5172 + p.end = pos().end; 1.5173 + 1.5174 + pc->sc->setBindingsAccessedDynamically(); 1.5175 + pc->sc->setHasDebuggerStatement(); 1.5176 + 1.5177 + return handler.newDebuggerStatement(p); 1.5178 +} 1.5179 + 1.5180 +template <typename ParseHandler> 1.5181 +typename ParseHandler::Node 1.5182 +Parser<ParseHandler>::statement(bool canHaveDirectives) 1.5183 +{ 1.5184 + JS_CHECK_RECURSION(context, return null()); 1.5185 + 1.5186 + switch (TokenKind tt = tokenStream.getToken(TokenStream::Operand)) { 1.5187 + case TOK_LC: 1.5188 + return blockStatement(); 1.5189 + 1.5190 + case TOK_CONST: 1.5191 + if (!abortIfSyntaxParser()) 1.5192 + return null(); 1.5193 + // FALL THROUGH 1.5194 + case TOK_VAR: { 1.5195 + Node pn = variables(tt == TOK_CONST ? PNK_CONST : PNK_VAR); 1.5196 + if (!pn) 1.5197 + return null(); 1.5198 + 1.5199 + // Tell js_EmitTree to generate a final POP. 1.5200 + handler.setListFlag(pn, PNX_POPVAR); 1.5201 + 1.5202 + if (!MatchOrInsertSemicolon(tokenStream)) 1.5203 + return null(); 1.5204 + return pn; 1.5205 + } 1.5206 + 1.5207 + case TOK_LET: 1.5208 + return letStatement(); 1.5209 + case TOK_IMPORT: 1.5210 + return importDeclaration(); 1.5211 + case TOK_EXPORT: 1.5212 + return exportDeclaration(); 1.5213 + case TOK_SEMI: 1.5214 + return handler.newEmptyStatement(pos()); 1.5215 + case TOK_IF: 1.5216 + return ifStatement(); 1.5217 + case TOK_DO: 1.5218 + return doWhileStatement(); 1.5219 + case TOK_WHILE: 1.5220 + return whileStatement(); 1.5221 + case TOK_FOR: 1.5222 + return forStatement(); 1.5223 + case TOK_SWITCH: 1.5224 + return switchStatement(); 1.5225 + case TOK_CONTINUE: 1.5226 + return continueStatement(); 1.5227 + case TOK_BREAK: 1.5228 + return breakStatement(); 1.5229 + case TOK_RETURN: 1.5230 + return returnStatement(); 1.5231 + case TOK_WITH: 1.5232 + return withStatement(); 1.5233 + case TOK_THROW: 1.5234 + return throwStatement(); 1.5235 + case TOK_TRY: 1.5236 + return tryStatement(); 1.5237 + case TOK_FUNCTION: 1.5238 + return functionStmt(); 1.5239 + case TOK_DEBUGGER: 1.5240 + return debuggerStatement(); 1.5241 + 1.5242 + /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */ 1.5243 + case TOK_CATCH: 1.5244 + report(ParseError, false, null(), JSMSG_CATCH_WITHOUT_TRY); 1.5245 + return null(); 1.5246 + 1.5247 + case TOK_FINALLY: 1.5248 + report(ParseError, false, null(), JSMSG_FINALLY_WITHOUT_TRY); 1.5249 + return null(); 1.5250 + 1.5251 + case TOK_ERROR: 1.5252 + return null(); 1.5253 + 1.5254 + case TOK_STRING: 1.5255 + if (!canHaveDirectives && tokenStream.currentToken().atom() == context->names().useAsm) { 1.5256 + if (!abortIfSyntaxParser()) 1.5257 + return null(); 1.5258 + if (!report(ParseWarning, false, null(), JSMSG_USE_ASM_DIRECTIVE_FAIL)) 1.5259 + return null(); 1.5260 + } 1.5261 + return expressionStatement(); 1.5262 + 1.5263 + case TOK_YIELD: 1.5264 + if (tokenStream.peekToken() == TOK_COLON) { 1.5265 + if (!checkYieldNameValidity()) 1.5266 + return null(); 1.5267 + return labeledStatement(); 1.5268 + } 1.5269 + return expressionStatement(); 1.5270 + 1.5271 + case TOK_NAME: 1.5272 + if (tokenStream.peekToken() == TOK_COLON) 1.5273 + return labeledStatement(); 1.5274 + return expressionStatement(); 1.5275 + 1.5276 + default: 1.5277 + return expressionStatement(); 1.5278 + } 1.5279 +} 1.5280 + 1.5281 +template <typename ParseHandler> 1.5282 +typename ParseHandler::Node 1.5283 +Parser<ParseHandler>::expr() 1.5284 +{ 1.5285 + Node pn = assignExpr(); 1.5286 + if (pn && tokenStream.matchToken(TOK_COMMA)) { 1.5287 + Node seq = handler.newList(PNK_COMMA, pn); 1.5288 + if (!seq) 1.5289 + return null(); 1.5290 + do { 1.5291 + if (handler.isUnparenthesizedYield(pn)) { 1.5292 + report(ParseError, false, pn, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str); 1.5293 + return null(); 1.5294 + } 1.5295 + 1.5296 + pn = assignExpr(); 1.5297 + if (!pn) 1.5298 + return null(); 1.5299 + handler.addList(seq, pn); 1.5300 + } while (tokenStream.matchToken(TOK_COMMA)); 1.5301 + return seq; 1.5302 + } 1.5303 + return pn; 1.5304 +} 1.5305 + 1.5306 +static const JSOp ParseNodeKindToJSOp[] = { 1.5307 + JSOP_OR, 1.5308 + JSOP_AND, 1.5309 + JSOP_BITOR, 1.5310 + JSOP_BITXOR, 1.5311 + JSOP_BITAND, 1.5312 + JSOP_STRICTEQ, 1.5313 + JSOP_EQ, 1.5314 + JSOP_STRICTNE, 1.5315 + JSOP_NE, 1.5316 + JSOP_LT, 1.5317 + JSOP_LE, 1.5318 + JSOP_GT, 1.5319 + JSOP_GE, 1.5320 + JSOP_INSTANCEOF, 1.5321 + JSOP_IN, 1.5322 + JSOP_LSH, 1.5323 + JSOP_RSH, 1.5324 + JSOP_URSH, 1.5325 + JSOP_ADD, 1.5326 + JSOP_SUB, 1.5327 + JSOP_MUL, 1.5328 + JSOP_DIV, 1.5329 + JSOP_MOD 1.5330 +}; 1.5331 + 1.5332 +static inline JSOp 1.5333 +BinaryOpParseNodeKindToJSOp(ParseNodeKind pnk) 1.5334 +{ 1.5335 + JS_ASSERT(pnk >= PNK_BINOP_FIRST); 1.5336 + JS_ASSERT(pnk <= PNK_BINOP_LAST); 1.5337 + return ParseNodeKindToJSOp[pnk - PNK_BINOP_FIRST]; 1.5338 +} 1.5339 + 1.5340 +static bool 1.5341 +IsBinaryOpToken(TokenKind tok, bool parsingForInit) 1.5342 +{ 1.5343 + return tok == TOK_IN ? !parsingForInit : TokenKindIsBinaryOp(tok); 1.5344 +} 1.5345 + 1.5346 +static ParseNodeKind 1.5347 +BinaryOpTokenKindToParseNodeKind(TokenKind tok) 1.5348 +{ 1.5349 + JS_ASSERT(TokenKindIsBinaryOp(tok)); 1.5350 + return ParseNodeKind(PNK_BINOP_FIRST + (tok - TOK_BINOP_FIRST)); 1.5351 +} 1.5352 + 1.5353 +static const int PrecedenceTable[] = { 1.5354 + 1, /* PNK_OR */ 1.5355 + 2, /* PNK_AND */ 1.5356 + 3, /* PNK_BITOR */ 1.5357 + 4, /* PNK_BITXOR */ 1.5358 + 5, /* PNK_BITAND */ 1.5359 + 6, /* PNK_STRICTEQ */ 1.5360 + 6, /* PNK_EQ */ 1.5361 + 6, /* PNK_STRICTNE */ 1.5362 + 6, /* PNK_NE */ 1.5363 + 7, /* PNK_LT */ 1.5364 + 7, /* PNK_LE */ 1.5365 + 7, /* PNK_GT */ 1.5366 + 7, /* PNK_GE */ 1.5367 + 7, /* PNK_INSTANCEOF */ 1.5368 + 7, /* PNK_IN */ 1.5369 + 8, /* PNK_LSH */ 1.5370 + 8, /* PNK_RSH */ 1.5371 + 8, /* PNK_URSH */ 1.5372 + 9, /* PNK_ADD */ 1.5373 + 9, /* PNK_SUB */ 1.5374 + 10, /* PNK_STAR */ 1.5375 + 10, /* PNK_DIV */ 1.5376 + 10 /* PNK_MOD */ 1.5377 +}; 1.5378 + 1.5379 +static const int PRECEDENCE_CLASSES = 10; 1.5380 + 1.5381 +static int 1.5382 +Precedence(ParseNodeKind pnk) { 1.5383 + // Everything binds tighter than PNK_LIMIT, because we want to reduce all 1.5384 + // nodes to a single node when we reach a token that is not another binary 1.5385 + // operator. 1.5386 + if (pnk == PNK_LIMIT) 1.5387 + return 0; 1.5388 + 1.5389 + JS_ASSERT(pnk >= PNK_BINOP_FIRST); 1.5390 + JS_ASSERT(pnk <= PNK_BINOP_LAST); 1.5391 + return PrecedenceTable[pnk - PNK_BINOP_FIRST]; 1.5392 +} 1.5393 + 1.5394 +template <typename ParseHandler> 1.5395 +MOZ_ALWAYS_INLINE typename ParseHandler::Node 1.5396 +Parser<ParseHandler>::orExpr1() 1.5397 +{ 1.5398 + // Shift-reduce parser for the left-associative binary operator part of 1.5399 + // the JS syntax. 1.5400 + 1.5401 + // Conceptually there's just one stack, a stack of pairs (lhs, op). 1.5402 + // It's implemented using two separate arrays, though. 1.5403 + Node nodeStack[PRECEDENCE_CLASSES]; 1.5404 + ParseNodeKind kindStack[PRECEDENCE_CLASSES]; 1.5405 + int depth = 0; 1.5406 + 1.5407 + bool oldParsingForInit = pc->parsingForInit; 1.5408 + pc->parsingForInit = false; 1.5409 + 1.5410 + Node pn; 1.5411 + for (;;) { 1.5412 + pn = unaryExpr(); 1.5413 + if (!pn) 1.5414 + return pn; 1.5415 + 1.5416 + // If a binary operator follows, consume it and compute the 1.5417 + // corresponding operator. 1.5418 + TokenKind tok = tokenStream.getToken(); 1.5419 + if (tok == TOK_ERROR) 1.5420 + return null(); 1.5421 + ParseNodeKind pnk; 1.5422 + if (IsBinaryOpToken(tok, oldParsingForInit)) { 1.5423 + pnk = BinaryOpTokenKindToParseNodeKind(tok); 1.5424 + } else { 1.5425 + tok = TOK_EOF; 1.5426 + pnk = PNK_LIMIT; 1.5427 + } 1.5428 + 1.5429 + // If pnk has precedence less than or equal to another operator on the 1.5430 + // stack, reduce. This combines nodes on the stack until we form the 1.5431 + // actual lhs of pnk. 1.5432 + // 1.5433 + // The >= in this condition works because all the operators in question 1.5434 + // are left-associative; if any were not, the case where two operators 1.5435 + // have equal precedence would need to be handled specially, and the 1.5436 + // stack would need to be a Vector. 1.5437 + while (depth > 0 && Precedence(kindStack[depth - 1]) >= Precedence(pnk)) { 1.5438 + depth--; 1.5439 + ParseNodeKind combiningPnk = kindStack[depth]; 1.5440 + JSOp combiningOp = BinaryOpParseNodeKindToJSOp(combiningPnk); 1.5441 + pn = handler.newBinaryOrAppend(combiningPnk, nodeStack[depth], pn, pc, combiningOp); 1.5442 + if (!pn) 1.5443 + return pn; 1.5444 + } 1.5445 + 1.5446 + if (pnk == PNK_LIMIT) 1.5447 + break; 1.5448 + 1.5449 + nodeStack[depth] = pn; 1.5450 + kindStack[depth] = pnk; 1.5451 + depth++; 1.5452 + JS_ASSERT(depth <= PRECEDENCE_CLASSES); 1.5453 + } 1.5454 + 1.5455 + JS_ASSERT(depth == 0); 1.5456 + pc->parsingForInit = oldParsingForInit; 1.5457 + return pn; 1.5458 +} 1.5459 + 1.5460 +template <typename ParseHandler> 1.5461 +MOZ_ALWAYS_INLINE typename ParseHandler::Node 1.5462 +Parser<ParseHandler>::condExpr1() 1.5463 +{ 1.5464 + Node condition = orExpr1(); 1.5465 + if (!condition || !tokenStream.isCurrentTokenType(TOK_HOOK)) 1.5466 + return condition; 1.5467 + 1.5468 + /* 1.5469 + * Always accept the 'in' operator in the middle clause of a ternary, 1.5470 + * where it's unambiguous, even if we might be parsing the init of a 1.5471 + * for statement. 1.5472 + */ 1.5473 + bool oldParsingForInit = pc->parsingForInit; 1.5474 + pc->parsingForInit = false; 1.5475 + Node thenExpr = assignExpr(); 1.5476 + pc->parsingForInit = oldParsingForInit; 1.5477 + if (!thenExpr) 1.5478 + return null(); 1.5479 + 1.5480 + MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND); 1.5481 + 1.5482 + Node elseExpr = assignExpr(); 1.5483 + if (!elseExpr) 1.5484 + return null(); 1.5485 + 1.5486 + tokenStream.getToken(); /* read one token past the end */ 1.5487 + return handler.newConditional(condition, thenExpr, elseExpr); 1.5488 +} 1.5489 + 1.5490 +template <> 1.5491 +bool 1.5492 +Parser<FullParseHandler>::checkAndMarkAsAssignmentLhs(ParseNode *pn, AssignmentFlavor flavor) 1.5493 +{ 1.5494 + switch (pn->getKind()) { 1.5495 + case PNK_NAME: 1.5496 + if (!checkStrictAssignment(pn, flavor)) 1.5497 + return false; 1.5498 + if (flavor == KeyedDestructuringAssignment) { 1.5499 + /* 1.5500 + * We may be called on a name node that has already been 1.5501 + * specialized, in the very weird "for (var [x] = i in o) ..." 1.5502 + * case. See bug 558633. 1.5503 + */ 1.5504 + if (!(js_CodeSpec[pn->getOp()].format & JOF_SET)) 1.5505 + pn->setOp(JSOP_SETNAME); 1.5506 + } else { 1.5507 + pn->setOp(pn->isOp(JSOP_GETLOCAL) ? JSOP_SETLOCAL : JSOP_SETNAME); 1.5508 + } 1.5509 + pn->markAsAssigned(); 1.5510 + break; 1.5511 + 1.5512 + case PNK_DOT: 1.5513 + case PNK_ELEM: 1.5514 + break; 1.5515 + 1.5516 + case PNK_ARRAY: 1.5517 + case PNK_OBJECT: 1.5518 + if (flavor == CompoundAssignment) { 1.5519 + report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_ASS); 1.5520 + return false; 1.5521 + } 1.5522 + if (!checkDestructuring(nullptr, pn)) 1.5523 + return false; 1.5524 + break; 1.5525 + 1.5526 + case PNK_CALL: 1.5527 + if (!makeSetCall(pn, JSMSG_BAD_LEFTSIDE_OF_ASS)) 1.5528 + return false; 1.5529 + break; 1.5530 + 1.5531 + default: 1.5532 + report(ParseError, false, pn, JSMSG_BAD_LEFTSIDE_OF_ASS); 1.5533 + return false; 1.5534 + } 1.5535 + return true; 1.5536 +} 1.5537 + 1.5538 +template <> 1.5539 +bool 1.5540 +Parser<SyntaxParseHandler>::checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor) 1.5541 +{ 1.5542 + /* Full syntax checking of valid assignment LHS terms requires a parse tree. */ 1.5543 + if (pn != SyntaxParseHandler::NodeName && 1.5544 + pn != SyntaxParseHandler::NodeGetProp && 1.5545 + pn != SyntaxParseHandler::NodeLValue) 1.5546 + { 1.5547 + return abortIfSyntaxParser(); 1.5548 + } 1.5549 + return checkStrictAssignment(pn, flavor); 1.5550 +} 1.5551 + 1.5552 +template <typename ParseHandler> 1.5553 +typename ParseHandler::Node 1.5554 +Parser<ParseHandler>::assignExpr() 1.5555 +{ 1.5556 + JS_CHECK_RECURSION(context, return null()); 1.5557 + 1.5558 + // It's very common at this point to have a "detectably simple" expression, 1.5559 + // i.e. a name/number/string token followed by one of the following tokens 1.5560 + // that obviously isn't part of an expression: , ; : ) ] } 1.5561 + // 1.5562 + // (In Parsemark this happens 81.4% of the time; in code with large 1.5563 + // numeric arrays, such as some Kraken benchmarks, it happens more often.) 1.5564 + // 1.5565 + // In such cases, we can avoid the full expression parsing route through 1.5566 + // assignExpr(), condExpr1(), orExpr1(), unaryExpr(), memberExpr(), and 1.5567 + // primaryExpr(). 1.5568 + 1.5569 + TokenKind tt = tokenStream.getToken(TokenStream::Operand); 1.5570 + 1.5571 + if (tt == TOK_NAME && tokenStream.nextTokenEndsExpr()) 1.5572 + return identifierName(); 1.5573 + 1.5574 + if (tt == TOK_NUMBER && tokenStream.nextTokenEndsExpr()) 1.5575 + return newNumber(tokenStream.currentToken()); 1.5576 + 1.5577 + if (tt == TOK_STRING && tokenStream.nextTokenEndsExpr()) 1.5578 + return stringLiteral(); 1.5579 + 1.5580 + if (tt == TOK_YIELD && (versionNumber() >= JSVERSION_1_7 || pc->isGenerator())) 1.5581 + return yieldExpression(); 1.5582 + 1.5583 + tokenStream.ungetToken(); 1.5584 + 1.5585 + // Save the tokenizer state in case we find an arrow function and have to 1.5586 + // rewind. 1.5587 + TokenStream::Position start(keepAtoms); 1.5588 + tokenStream.tell(&start); 1.5589 + 1.5590 + Node lhs = condExpr1(); 1.5591 + if (!lhs) 1.5592 + return null(); 1.5593 + 1.5594 + ParseNodeKind kind; 1.5595 + JSOp op; 1.5596 + switch (tokenStream.currentToken().type) { 1.5597 + case TOK_ASSIGN: kind = PNK_ASSIGN; op = JSOP_NOP; break; 1.5598 + case TOK_ADDASSIGN: kind = PNK_ADDASSIGN; op = JSOP_ADD; break; 1.5599 + case TOK_SUBASSIGN: kind = PNK_SUBASSIGN; op = JSOP_SUB; break; 1.5600 + case TOK_BITORASSIGN: kind = PNK_BITORASSIGN; op = JSOP_BITOR; break; 1.5601 + case TOK_BITXORASSIGN: kind = PNK_BITXORASSIGN; op = JSOP_BITXOR; break; 1.5602 + case TOK_BITANDASSIGN: kind = PNK_BITANDASSIGN; op = JSOP_BITAND; break; 1.5603 + case TOK_LSHASSIGN: kind = PNK_LSHASSIGN; op = JSOP_LSH; break; 1.5604 + case TOK_RSHASSIGN: kind = PNK_RSHASSIGN; op = JSOP_RSH; break; 1.5605 + case TOK_URSHASSIGN: kind = PNK_URSHASSIGN; op = JSOP_URSH; break; 1.5606 + case TOK_MULASSIGN: kind = PNK_MULASSIGN; op = JSOP_MUL; break; 1.5607 + case TOK_DIVASSIGN: kind = PNK_DIVASSIGN; op = JSOP_DIV; break; 1.5608 + case TOK_MODASSIGN: kind = PNK_MODASSIGN; op = JSOP_MOD; break; 1.5609 + 1.5610 + case TOK_ARROW: { 1.5611 + tokenStream.seek(start); 1.5612 + if (!abortIfSyntaxParser()) 1.5613 + return null(); 1.5614 + 1.5615 + if (tokenStream.getToken() == TOK_ERROR) 1.5616 + return null(); 1.5617 + tokenStream.ungetToken(); 1.5618 + 1.5619 + return functionDef(NullPtr(), start, Normal, Arrow, NotGenerator); 1.5620 + } 1.5621 + 1.5622 + default: 1.5623 + JS_ASSERT(!tokenStream.isCurrentTokenAssignment()); 1.5624 + tokenStream.ungetToken(); 1.5625 + return lhs; 1.5626 + } 1.5627 + 1.5628 + AssignmentFlavor flavor = kind == PNK_ASSIGN ? PlainAssignment : CompoundAssignment; 1.5629 + if (!checkAndMarkAsAssignmentLhs(lhs, flavor)) 1.5630 + return null(); 1.5631 + 1.5632 + Node rhs = assignExpr(); 1.5633 + if (!rhs) 1.5634 + return null(); 1.5635 + 1.5636 + return handler.newBinaryOrAppend(kind, lhs, rhs, pc, op); 1.5637 +} 1.5638 + 1.5639 +static const char incop_name_str[][10] = {"increment", "decrement"}; 1.5640 + 1.5641 +template <> 1.5642 +bool 1.5643 +Parser<FullParseHandler>::checkAndMarkAsIncOperand(ParseNode *kid, TokenKind tt, bool preorder) 1.5644 +{ 1.5645 + // Check. 1.5646 + if (!kid->isKind(PNK_NAME) && 1.5647 + !kid->isKind(PNK_DOT) && 1.5648 + !kid->isKind(PNK_ELEM) && 1.5649 + !(kid->isKind(PNK_CALL) && 1.5650 + (kid->isOp(JSOP_CALL) || kid->isOp(JSOP_SPREADCALL) || 1.5651 + kid->isOp(JSOP_EVAL) || kid->isOp(JSOP_SPREADEVAL) || 1.5652 + kid->isOp(JSOP_FUNCALL) || 1.5653 + kid->isOp(JSOP_FUNAPPLY)))) 1.5654 + { 1.5655 + report(ParseError, false, null(), JSMSG_BAD_OPERAND, incop_name_str[tt == TOK_DEC]); 1.5656 + return false; 1.5657 + } 1.5658 + 1.5659 + if (!checkStrictAssignment(kid, IncDecAssignment)) 1.5660 + return false; 1.5661 + 1.5662 + // Mark. 1.5663 + if (kid->isKind(PNK_NAME)) { 1.5664 + kid->markAsAssigned(); 1.5665 + } else if (kid->isKind(PNK_CALL)) { 1.5666 + if (!makeSetCall(kid, JSMSG_BAD_INCOP_OPERAND)) 1.5667 + return false; 1.5668 + } 1.5669 + return true; 1.5670 +} 1.5671 + 1.5672 +template <> 1.5673 +bool 1.5674 +Parser<SyntaxParseHandler>::checkAndMarkAsIncOperand(Node kid, TokenKind tt, bool preorder) 1.5675 +{ 1.5676 + // To the extent of what we support in syntax-parse mode, the rules for 1.5677 + // inc/dec operands are the same as for assignment. There are differences, 1.5678 + // such as destructuring; but if we hit any of those cases, we'll abort and 1.5679 + // reparse in full mode. 1.5680 + return checkAndMarkAsAssignmentLhs(kid, IncDecAssignment); 1.5681 +} 1.5682 + 1.5683 +template <typename ParseHandler> 1.5684 +typename ParseHandler::Node 1.5685 +Parser<ParseHandler>::unaryOpExpr(ParseNodeKind kind, JSOp op, uint32_t begin) 1.5686 +{ 1.5687 + Node kid = unaryExpr(); 1.5688 + if (!kid) 1.5689 + return null(); 1.5690 + return handler.newUnary(kind, op, begin, kid); 1.5691 +} 1.5692 + 1.5693 +template <typename ParseHandler> 1.5694 +typename ParseHandler::Node 1.5695 +Parser<ParseHandler>::unaryExpr() 1.5696 +{ 1.5697 + Node pn, pn2; 1.5698 + 1.5699 + JS_CHECK_RECURSION(context, return null()); 1.5700 + 1.5701 + TokenKind tt = tokenStream.getToken(TokenStream::Operand); 1.5702 + uint32_t begin = pos().begin; 1.5703 + switch (tt) { 1.5704 + case TOK_TYPEOF: 1.5705 + return unaryOpExpr(PNK_TYPEOF, JSOP_TYPEOF, begin); 1.5706 + case TOK_VOID: 1.5707 + return unaryOpExpr(PNK_VOID, JSOP_VOID, begin); 1.5708 + case TOK_NOT: 1.5709 + return unaryOpExpr(PNK_NOT, JSOP_NOT, begin); 1.5710 + case TOK_BITNOT: 1.5711 + return unaryOpExpr(PNK_BITNOT, JSOP_BITNOT, begin); 1.5712 + case TOK_ADD: 1.5713 + return unaryOpExpr(PNK_POS, JSOP_POS, begin); 1.5714 + case TOK_SUB: 1.5715 + return unaryOpExpr(PNK_NEG, JSOP_NEG, begin); 1.5716 + 1.5717 + case TOK_INC: 1.5718 + case TOK_DEC: 1.5719 + { 1.5720 + TokenKind tt2 = tokenStream.getToken(TokenStream::Operand); 1.5721 + pn2 = memberExpr(tt2, true); 1.5722 + if (!pn2) 1.5723 + return null(); 1.5724 + if (!checkAndMarkAsIncOperand(pn2, tt, true)) 1.5725 + return null(); 1.5726 + return handler.newUnary((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT, 1.5727 + JSOP_NOP, 1.5728 + begin, 1.5729 + pn2); 1.5730 + } 1.5731 + 1.5732 + case TOK_DELETE: { 1.5733 + Node expr = unaryExpr(); 1.5734 + if (!expr) 1.5735 + return null(); 1.5736 + 1.5737 + // Per spec, deleting any unary expression is valid -- it simply 1.5738 + // returns true -- except for one case that is illegal in strict mode. 1.5739 + if (handler.isName(expr)) { 1.5740 + if (!report(ParseStrictError, pc->sc->strict, expr, JSMSG_DEPRECATED_DELETE_OPERAND)) 1.5741 + return null(); 1.5742 + pc->sc->setBindingsAccessedDynamically(); 1.5743 + } 1.5744 + 1.5745 + return handler.newDelete(begin, expr); 1.5746 + } 1.5747 + 1.5748 + case TOK_ERROR: 1.5749 + return null(); 1.5750 + 1.5751 + default: 1.5752 + pn = memberExpr(tt, true); 1.5753 + if (!pn) 1.5754 + return null(); 1.5755 + 1.5756 + /* Don't look across a newline boundary for a postfix incop. */ 1.5757 + tt = tokenStream.peekTokenSameLine(TokenStream::Operand); 1.5758 + if (tt == TOK_INC || tt == TOK_DEC) { 1.5759 + tokenStream.consumeKnownToken(tt); 1.5760 + if (!checkAndMarkAsIncOperand(pn, tt, false)) 1.5761 + return null(); 1.5762 + return handler.newUnary((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT, 1.5763 + JSOP_NOP, 1.5764 + begin, 1.5765 + pn); 1.5766 + } 1.5767 + return pn; 1.5768 + } 1.5769 + MOZ_ASSUME_UNREACHABLE("unaryExpr"); 1.5770 +} 1.5771 + 1.5772 +/* 1.5773 + * A dedicated helper for transplanting the legacy comprehension expression E in 1.5774 + * 1.5775 + * [E for (V in I)] // legacy array comprehension 1.5776 + * (E for (V in I)) // legacy generator expression 1.5777 + * 1.5778 + * from its initial location in the AST, on the left of the 'for', to its final 1.5779 + * position on the right. To avoid a separate pass we do this by adjusting the 1.5780 + * blockids and name binding links that were established when E was parsed. 1.5781 + * 1.5782 + * A legacy generator expression desugars like so: 1.5783 + * 1.5784 + * (E for (V in I)) => (function () { for (var V in I) yield E; })() 1.5785 + * 1.5786 + * so the transplanter must adjust static level as well as blockid. E's source 1.5787 + * coordinates in root->pn_pos are critical to deciding which binding links to 1.5788 + * preserve and which to cut. 1.5789 + * 1.5790 + * NB: This is not a general tree transplanter -- it knows in particular that 1.5791 + * the one or more bindings induced by V have not yet been created. 1.5792 + */ 1.5793 +class LegacyCompExprTransplanter 1.5794 +{ 1.5795 + ParseNode *root; 1.5796 + Parser<FullParseHandler> *parser; 1.5797 + ParseContext<FullParseHandler> *outerpc; 1.5798 + GeneratorKind comprehensionKind; 1.5799 + unsigned adjust; 1.5800 + HashSet<Definition *> visitedImplicitArguments; 1.5801 + 1.5802 + public: 1.5803 + LegacyCompExprTransplanter(ParseNode *pn, Parser<FullParseHandler> *parser, 1.5804 + ParseContext<FullParseHandler> *outerpc, 1.5805 + GeneratorKind kind, unsigned adj) 1.5806 + : root(pn), parser(parser), outerpc(outerpc), comprehensionKind(kind), adjust(adj), 1.5807 + visitedImplicitArguments(parser->context) 1.5808 + {} 1.5809 + 1.5810 + bool init() { 1.5811 + return visitedImplicitArguments.init(); 1.5812 + } 1.5813 + 1.5814 + bool transplant(ParseNode *pn); 1.5815 +}; 1.5816 + 1.5817 +/* 1.5818 + * Any definitions nested within the legacy comprehension expression of a 1.5819 + * generator expression must move "down" one static level, which of course 1.5820 + * increases the upvar-frame-skip count. 1.5821 + */ 1.5822 +template <typename ParseHandler> 1.5823 +static bool 1.5824 +BumpStaticLevel(TokenStream &ts, ParseNode *pn, ParseContext<ParseHandler> *pc) 1.5825 +{ 1.5826 + if (pn->pn_cookie.isFree()) 1.5827 + return true; 1.5828 + 1.5829 + unsigned level = unsigned(pn->pn_cookie.level()) + 1; 1.5830 + JS_ASSERT(level >= pc->staticLevel); 1.5831 + return pn->pn_cookie.set(ts, level, pn->pn_cookie.slot()); 1.5832 +} 1.5833 + 1.5834 +template <typename ParseHandler> 1.5835 +static bool 1.5836 +AdjustBlockId(TokenStream &ts, ParseNode *pn, unsigned adjust, ParseContext<ParseHandler> *pc) 1.5837 +{ 1.5838 + JS_ASSERT(pn->isArity(PN_LIST) || pn->isArity(PN_CODE) || pn->isArity(PN_NAME)); 1.5839 + if (BlockIdLimit - pn->pn_blockid <= adjust + 1) { 1.5840 + ts.reportError(JSMSG_NEED_DIET, "program"); 1.5841 + return false; 1.5842 + } 1.5843 + pn->pn_blockid += adjust; 1.5844 + if (pn->pn_blockid >= pc->blockidGen) 1.5845 + pc->blockidGen = pn->pn_blockid + 1; 1.5846 + return true; 1.5847 +} 1.5848 + 1.5849 +bool 1.5850 +LegacyCompExprTransplanter::transplant(ParseNode *pn) 1.5851 +{ 1.5852 + ParseContext<FullParseHandler> *pc = parser->pc; 1.5853 + 1.5854 + bool isGenexp = comprehensionKind != NotGenerator; 1.5855 + 1.5856 + if (!pn) 1.5857 + return true; 1.5858 + 1.5859 + switch (pn->getArity()) { 1.5860 + case PN_LIST: 1.5861 + for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { 1.5862 + if (!transplant(pn2)) 1.5863 + return false; 1.5864 + } 1.5865 + if (pn->pn_pos >= root->pn_pos) { 1.5866 + if (!AdjustBlockId(parser->tokenStream, pn, adjust, pc)) 1.5867 + return false; 1.5868 + } 1.5869 + break; 1.5870 + 1.5871 + case PN_TERNARY: 1.5872 + if (!transplant(pn->pn_kid1) || 1.5873 + !transplant(pn->pn_kid2) || 1.5874 + !transplant(pn->pn_kid3)) 1.5875 + return false; 1.5876 + break; 1.5877 + 1.5878 + case PN_BINARY: 1.5879 + case PN_BINARY_OBJ: 1.5880 + if (!transplant(pn->pn_left)) 1.5881 + return false; 1.5882 + 1.5883 + /* Binary TOK_COLON nodes can have left == right. See bug 492714. */ 1.5884 + if (pn->pn_right != pn->pn_left) { 1.5885 + if (!transplant(pn->pn_right)) 1.5886 + return false; 1.5887 + } 1.5888 + break; 1.5889 + 1.5890 + case PN_UNARY: 1.5891 + if (!transplant(pn->pn_kid)) 1.5892 + return false; 1.5893 + break; 1.5894 + 1.5895 + case PN_CODE: 1.5896 + case PN_NAME: 1.5897 + if (!transplant(pn->maybeExpr())) 1.5898 + return false; 1.5899 + 1.5900 + if (pn->isDefn()) { 1.5901 + if (isGenexp && !BumpStaticLevel(parser->tokenStream, pn, pc)) 1.5902 + return false; 1.5903 + } else if (pn->isUsed()) { 1.5904 + JS_ASSERT(pn->pn_cookie.isFree()); 1.5905 + 1.5906 + Definition *dn = pn->pn_lexdef; 1.5907 + JS_ASSERT(dn->isDefn()); 1.5908 + 1.5909 + /* 1.5910 + * Adjust the definition's block id only if it is a placeholder not 1.5911 + * to the left of the root node, and if pn is the last use visited 1.5912 + * in the legacy comprehension expression (to avoid adjusting the 1.5913 + * blockid multiple times). 1.5914 + * 1.5915 + * Non-placeholder definitions within the legacy comprehension 1.5916 + * expression will be visited further below. 1.5917 + */ 1.5918 + if (dn->isPlaceholder() && dn->pn_pos >= root->pn_pos && dn->dn_uses == pn) { 1.5919 + if (isGenexp && !BumpStaticLevel(parser->tokenStream, dn, pc)) 1.5920 + return false; 1.5921 + if (!AdjustBlockId(parser->tokenStream, dn, adjust, pc)) 1.5922 + return false; 1.5923 + } 1.5924 + 1.5925 + RootedAtom atom(parser->context, pn->pn_atom); 1.5926 +#ifdef DEBUG 1.5927 + StmtInfoPC *stmt = LexicalLookup(pc, atom, nullptr, (StmtInfoPC *)nullptr); 1.5928 + JS_ASSERT(!stmt || stmt != pc->topStmt); 1.5929 +#endif 1.5930 + if (isGenexp && !dn->isOp(JSOP_CALLEE)) { 1.5931 + JS_ASSERT(!pc->decls().lookupFirst(atom)); 1.5932 + 1.5933 + if (dn->pn_pos < root->pn_pos) { 1.5934 + /* 1.5935 + * The variable originally appeared to be a use of a 1.5936 + * definition or placeholder outside the generator, but now 1.5937 + * we know it is scoped within the legacy comprehension 1.5938 + * tail's clauses. Make it (along with any other uses within 1.5939 + * the generator) a use of a new placeholder in the 1.5940 + * generator's lexdeps. 1.5941 + */ 1.5942 + Definition *dn2 = parser->handler.newPlaceholder(atom, parser->pc->blockid(), 1.5943 + parser->pos()); 1.5944 + if (!dn2) 1.5945 + return false; 1.5946 + dn2->pn_pos = root->pn_pos; 1.5947 + 1.5948 + /* 1.5949 + * Change all uses of |dn| that lie within the generator's 1.5950 + * |yield| expression into uses of dn2. 1.5951 + */ 1.5952 + ParseNode **pnup = &dn->dn_uses; 1.5953 + ParseNode *pnu; 1.5954 + while ((pnu = *pnup) != nullptr && pnu->pn_pos >= root->pn_pos) { 1.5955 + pnu->pn_lexdef = dn2; 1.5956 + dn2->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS; 1.5957 + pnup = &pnu->pn_link; 1.5958 + } 1.5959 + dn2->dn_uses = dn->dn_uses; 1.5960 + dn->dn_uses = *pnup; 1.5961 + *pnup = nullptr; 1.5962 + DefinitionSingle def = DefinitionSingle::new_<FullParseHandler>(dn2); 1.5963 + if (!pc->lexdeps->put(atom, def)) 1.5964 + return false; 1.5965 + if (dn->isClosed()) 1.5966 + dn2->pn_dflags |= PND_CLOSED; 1.5967 + } else if (dn->isPlaceholder()) { 1.5968 + /* 1.5969 + * The variable first occurs free in the 'yield' expression; 1.5970 + * move the existing placeholder node (and all its uses) 1.5971 + * from the parent's lexdeps into the generator's lexdeps. 1.5972 + */ 1.5973 + outerpc->lexdeps->remove(atom); 1.5974 + DefinitionSingle def = DefinitionSingle::new_<FullParseHandler>(dn); 1.5975 + if (!pc->lexdeps->put(atom, def)) 1.5976 + return false; 1.5977 + } else if (dn->isImplicitArguments()) { 1.5978 + /* 1.5979 + * Implicit 'arguments' Definition nodes (see 1.5980 + * PND_IMPLICITARGUMENTS in Parser::functionBody) are only 1.5981 + * reachable via the lexdefs of their uses. Unfortunately, 1.5982 + * there may be multiple uses, so we need to maintain a set 1.5983 + * to only bump the definition once. 1.5984 + */ 1.5985 + if (isGenexp && !visitedImplicitArguments.has(dn)) { 1.5986 + if (!BumpStaticLevel(parser->tokenStream, dn, pc)) 1.5987 + return false; 1.5988 + if (!AdjustBlockId(parser->tokenStream, dn, adjust, pc)) 1.5989 + return false; 1.5990 + if (!visitedImplicitArguments.put(dn)) 1.5991 + return false; 1.5992 + } 1.5993 + } 1.5994 + } 1.5995 + } 1.5996 + 1.5997 + if (pn->pn_pos >= root->pn_pos) { 1.5998 + if (!AdjustBlockId(parser->tokenStream, pn, adjust, pc)) 1.5999 + return false; 1.6000 + } 1.6001 + break; 1.6002 + 1.6003 + case PN_NULLARY: 1.6004 + /* Nothing. */ 1.6005 + break; 1.6006 + } 1.6007 + return true; 1.6008 +} 1.6009 + 1.6010 +// Parsing legacy (JS1.7-style) comprehensions is terrible: we parse the head 1.6011 +// expression as if it's part of a comma expression, then when we see the "for" 1.6012 +// we transplant the parsed expression into the inside of a constructed 1.6013 +// for-of/for-in/for-each tail. Transplanting an already-parsed expression is 1.6014 +// tricky, but the LegacyCompExprTransplanter handles most of that. 1.6015 +// 1.6016 +// The one remaining thing to patch up is the block scope depth. We need to 1.6017 +// compute the maximum block scope depth of a function, so we know how much 1.6018 +// space to reserve in the fixed part of a stack frame. Normally this is done 1.6019 +// whenever we leave a statement, via AccumulateBlockScopeDepth. However if the 1.6020 +// head has a let expression, we need to re-assign that depth to the tail of the 1.6021 +// comprehension. 1.6022 +// 1.6023 +// Thing is, we don't actually know what that depth is, because the only 1.6024 +// information we keep is the maximum nested depth within a statement, so we 1.6025 +// just conservatively propagate the maximum nested depth from the top statement 1.6026 +// to the comprehension tail. 1.6027 +// 1.6028 +template <typename ParseHandler> 1.6029 +static unsigned 1.6030 +LegacyComprehensionHeadBlockScopeDepth(ParseContext<ParseHandler> *pc) 1.6031 +{ 1.6032 + return pc->topStmt ? pc->topStmt->innerBlockScopeDepth : pc->blockScopeDepth; 1.6033 +} 1.6034 + 1.6035 +/* 1.6036 + * Starting from a |for| keyword after the first array initialiser element or 1.6037 + * an expression in an open parenthesis, parse the tail of the comprehension 1.6038 + * or generator expression signified by this |for| keyword in context. 1.6039 + * 1.6040 + * Return null on failure, else return the top-most parse node for the array 1.6041 + * comprehension or generator expression, with a unary node as the body of the 1.6042 + * (possibly nested) for-loop, initialized by |kind, op, kid|. 1.6043 + */ 1.6044 +template <> 1.6045 +ParseNode * 1.6046 +Parser<FullParseHandler>::legacyComprehensionTail(ParseNode *bodyStmt, unsigned blockid, 1.6047 + GeneratorKind comprehensionKind, 1.6048 + ParseContext<FullParseHandler> *outerpc, 1.6049 + unsigned innerBlockScopeDepth) 1.6050 +{ 1.6051 + /* 1.6052 + * If we saw any inner functions while processing the generator expression 1.6053 + * then they may have upvars referring to the let vars in this generator 1.6054 + * which were not correctly processed. Bail out and start over without 1.6055 + * allowing lazy parsing. 1.6056 + */ 1.6057 + if (handler.syntaxParser) { 1.6058 + handler.disableSyntaxParser(); 1.6059 + abortedSyntaxParse = true; 1.6060 + return nullptr; 1.6061 + } 1.6062 + 1.6063 + unsigned adjust; 1.6064 + ParseNode *pn, *pn2, *pn3, **pnp; 1.6065 + StmtInfoPC stmtInfo(context); 1.6066 + BindData<FullParseHandler> data(context); 1.6067 + TokenKind tt; 1.6068 + 1.6069 + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); 1.6070 + 1.6071 + bool isGenexp = comprehensionKind != NotGenerator; 1.6072 + 1.6073 + if (isGenexp) { 1.6074 + JS_ASSERT(comprehensionKind == LegacyGenerator); 1.6075 + /* 1.6076 + * Generator expression desugars to an immediately applied lambda that 1.6077 + * yields the next value from a for-in loop (possibly nested, and with 1.6078 + * optional if guard). Make pn be the TOK_LC body node. 1.6079 + */ 1.6080 + pn = pushLexicalScope(&stmtInfo); 1.6081 + if (!pn) 1.6082 + return null(); 1.6083 + adjust = pn->pn_blockid - blockid; 1.6084 + } else { 1.6085 + /* 1.6086 + * Make a parse-node and literal object representing the block scope of 1.6087 + * this array comprehension. Our caller in primaryExpr, the TOK_LB case 1.6088 + * aka the array initialiser case, has passed the blockid to claim for 1.6089 + * the comprehension's block scope. We allocate that id or one above it 1.6090 + * here, by calling PushLexicalScope. 1.6091 + * 1.6092 + * In the case of a comprehension expression that has nested blocks 1.6093 + * (e.g., let expressions), we will allocate a higher blockid but then 1.6094 + * slide all blocks "to the right" to make room for the comprehension's 1.6095 + * block scope. 1.6096 + */ 1.6097 + adjust = pc->blockid(); 1.6098 + pn = pushLexicalScope(&stmtInfo); 1.6099 + if (!pn) 1.6100 + return null(); 1.6101 + 1.6102 + JS_ASSERT(blockid <= pn->pn_blockid); 1.6103 + JS_ASSERT(blockid < pc->blockidGen); 1.6104 + JS_ASSERT(pc->bodyid < blockid); 1.6105 + pn->pn_blockid = stmtInfo.blockid = blockid; 1.6106 + JS_ASSERT(adjust < blockid); 1.6107 + adjust = blockid - adjust; 1.6108 + } 1.6109 + 1.6110 + handler.setBeginPosition(pn, bodyStmt); 1.6111 + 1.6112 + pnp = &pn->pn_expr; 1.6113 + 1.6114 + LegacyCompExprTransplanter transplanter(bodyStmt, this, outerpc, comprehensionKind, adjust); 1.6115 + if (!transplanter.init()) 1.6116 + return null(); 1.6117 + 1.6118 + if (!transplanter.transplant(bodyStmt)) 1.6119 + return null(); 1.6120 + 1.6121 + JS_ASSERT(pc->staticScope && pc->staticScope == pn->pn_objbox->object); 1.6122 + data.initLet(HoistVars, pc->staticScope->as<StaticBlockObject>(), JSMSG_ARRAY_INIT_TOO_BIG); 1.6123 + 1.6124 + do { 1.6125 + /* 1.6126 + * FOR node is binary, left is loop control and right is body. Use 1.6127 + * index to count each block-local let-variable on the left-hand side 1.6128 + * of the in/of. 1.6129 + */ 1.6130 + pn2 = BinaryNode::create(PNK_FOR, &handler); 1.6131 + if (!pn2) 1.6132 + return null(); 1.6133 + 1.6134 + pn2->setOp(JSOP_ITER); 1.6135 + pn2->pn_iflags = JSITER_ENUMERATE; 1.6136 + if (allowsForEachIn() && tokenStream.matchContextualKeyword(context->names().each)) 1.6137 + pn2->pn_iflags |= JSITER_FOREACH; 1.6138 + MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); 1.6139 + 1.6140 + uint32_t startYieldOffset = pc->lastYieldOffset; 1.6141 + 1.6142 + RootedPropertyName name(context); 1.6143 + tt = tokenStream.getToken(); 1.6144 + switch (tt) { 1.6145 + case TOK_LB: 1.6146 + case TOK_LC: 1.6147 + pc->inDeclDestructuring = true; 1.6148 + pn3 = primaryExpr(tt); 1.6149 + pc->inDeclDestructuring = false; 1.6150 + if (!pn3) 1.6151 + return null(); 1.6152 + break; 1.6153 + 1.6154 + case TOK_NAME: 1.6155 + name = tokenStream.currentName(); 1.6156 + 1.6157 + /* 1.6158 + * Create a name node with pn_op JSOP_NAME. We can't set pn_op to 1.6159 + * JSOP_GETLOCAL here, because we don't yet know the block's depth 1.6160 + * in the operand stack frame. The code generator computes that, 1.6161 + * and it tries to bind all names to slots, so we must let it do 1.6162 + * the deed. 1.6163 + */ 1.6164 + pn3 = newBindingNode(name, false); 1.6165 + if (!pn3) 1.6166 + return null(); 1.6167 + break; 1.6168 + 1.6169 + default: 1.6170 + report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME); 1.6171 + 1.6172 + case TOK_ERROR: 1.6173 + return null(); 1.6174 + } 1.6175 + 1.6176 + bool isForOf; 1.6177 + if (!matchInOrOf(&isForOf)) { 1.6178 + report(ParseError, false, null(), JSMSG_IN_AFTER_FOR_NAME); 1.6179 + return null(); 1.6180 + } 1.6181 + ParseNodeKind headKind = PNK_FORIN; 1.6182 + if (isForOf) { 1.6183 + if (pn2->pn_iflags != JSITER_ENUMERATE) { 1.6184 + JS_ASSERT(pn2->pn_iflags == (JSITER_FOREACH | JSITER_ENUMERATE)); 1.6185 + report(ParseError, false, null(), JSMSG_BAD_FOR_EACH_LOOP); 1.6186 + return null(); 1.6187 + } 1.6188 + pn2->pn_iflags = 0; 1.6189 + headKind = PNK_FOROF; 1.6190 + } 1.6191 + 1.6192 + ParseNode *pn4 = expr(); 1.6193 + if (!pn4) 1.6194 + return null(); 1.6195 + MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL); 1.6196 + 1.6197 + if (isGenexp && pc->lastYieldOffset != startYieldOffset) { 1.6198 + reportWithOffset(ParseError, false, pc->lastYieldOffset, 1.6199 + JSMSG_BAD_GENEXP_BODY, js_yield_str); 1.6200 + return null(); 1.6201 + } 1.6202 + 1.6203 + switch (tt) { 1.6204 + case TOK_LB: 1.6205 + case TOK_LC: 1.6206 + if (!checkDestructuring(&data, pn3)) 1.6207 + return null(); 1.6208 + 1.6209 + if (versionNumber() == JSVERSION_1_7 && 1.6210 + !(pn2->pn_iflags & JSITER_FOREACH) && 1.6211 + !isForOf) 1.6212 + { 1.6213 + /* Destructuring requires [key, value] enumeration in JS1.7. */ 1.6214 + if (!pn3->isKind(PNK_ARRAY) || pn3->pn_count != 2) { 1.6215 + report(ParseError, false, null(), JSMSG_BAD_FOR_LEFTSIDE); 1.6216 + return null(); 1.6217 + } 1.6218 + 1.6219 + JS_ASSERT(pn2->isOp(JSOP_ITER)); 1.6220 + JS_ASSERT(pn2->pn_iflags & JSITER_ENUMERATE); 1.6221 + JS_ASSERT(headKind == PNK_FORIN); 1.6222 + pn2->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE; 1.6223 + } 1.6224 + break; 1.6225 + 1.6226 + case TOK_NAME: 1.6227 + data.pn = pn3; 1.6228 + if (!data.binder(&data, name, this)) 1.6229 + return null(); 1.6230 + break; 1.6231 + 1.6232 + default:; 1.6233 + } 1.6234 + 1.6235 + /* 1.6236 + * Synthesize a declaration. Every definition must appear in the parse 1.6237 + * tree in order for ComprehensionTranslator to work. 1.6238 + */ 1.6239 + ParseNode *vars = ListNode::create(PNK_VAR, &handler); 1.6240 + if (!vars) 1.6241 + return null(); 1.6242 + vars->setOp(JSOP_NOP); 1.6243 + vars->pn_pos = pn3->pn_pos; 1.6244 + vars->makeEmpty(); 1.6245 + vars->append(pn3); 1.6246 + 1.6247 + /* Definitions can't be passed directly to EmitAssignment as lhs. */ 1.6248 + pn3 = cloneLeftHandSide(pn3); 1.6249 + if (!pn3) 1.6250 + return null(); 1.6251 + 1.6252 + pn2->pn_left = handler.newTernary(headKind, vars, pn3, pn4); 1.6253 + if (!pn2->pn_left) 1.6254 + return null(); 1.6255 + *pnp = pn2; 1.6256 + pnp = &pn2->pn_right; 1.6257 + } while (tokenStream.matchToken(TOK_FOR)); 1.6258 + 1.6259 + if (tokenStream.matchToken(TOK_IF)) { 1.6260 + pn2 = TernaryNode::create(PNK_IF, &handler); 1.6261 + if (!pn2) 1.6262 + return null(); 1.6263 + pn2->pn_kid1 = condition(); 1.6264 + if (!pn2->pn_kid1) 1.6265 + return null(); 1.6266 + *pnp = pn2; 1.6267 + pnp = &pn2->pn_kid2; 1.6268 + } 1.6269 + 1.6270 + *pnp = bodyStmt; 1.6271 + 1.6272 + pc->topStmt->innerBlockScopeDepth += innerBlockScopeDepth; 1.6273 + PopStatementPC(tokenStream, pc); 1.6274 + 1.6275 + handler.setEndPosition(pn, pos().end); 1.6276 + 1.6277 + return pn; 1.6278 +} 1.6279 + 1.6280 +template <> 1.6281 +SyntaxParseHandler::Node 1.6282 +Parser<SyntaxParseHandler>::legacyComprehensionTail(SyntaxParseHandler::Node bodyStmt, 1.6283 + unsigned blockid, 1.6284 + GeneratorKind comprehensionKind, 1.6285 + ParseContext<SyntaxParseHandler> *outerpc, 1.6286 + unsigned innerBlockScopeDepth) 1.6287 +{ 1.6288 + abortIfSyntaxParser(); 1.6289 + return null(); 1.6290 +} 1.6291 + 1.6292 +template <> 1.6293 +ParseNode* 1.6294 +Parser<FullParseHandler>::legacyArrayComprehension(ParseNode *array) 1.6295 +{ 1.6296 + array->setKind(PNK_ARRAYCOMP); 1.6297 + 1.6298 + // Remove the single element from array's linked list, leaving us with an 1.6299 + // empty array literal and a comprehension expression. 1.6300 + JS_ASSERT(array->pn_count == 1); 1.6301 + ParseNode *bodyExpr = array->last(); 1.6302 + array->pn_count = 0; 1.6303 + array->pn_tail = &array->pn_head; 1.6304 + *array->pn_tail = nullptr; 1.6305 + 1.6306 + ParseNode *arrayPush = handler.newUnary(PNK_ARRAYPUSH, JSOP_ARRAYPUSH, 1.6307 + bodyExpr->pn_pos.begin, bodyExpr); 1.6308 + if (!arrayPush) 1.6309 + return null(); 1.6310 + 1.6311 + ParseNode *comp = legacyComprehensionTail(arrayPush, array->pn_blockid, NotGenerator, 1.6312 + nullptr, LegacyComprehensionHeadBlockScopeDepth(pc)); 1.6313 + if (!comp) 1.6314 + return null(); 1.6315 + 1.6316 + MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION); 1.6317 + 1.6318 + TokenPos p = handler.getPosition(array); 1.6319 + p.end = pos().end; 1.6320 + return handler.newArrayComprehension(comp, array->pn_blockid, p); 1.6321 +} 1.6322 + 1.6323 +template <> 1.6324 +SyntaxParseHandler::Node 1.6325 +Parser<SyntaxParseHandler>::legacyArrayComprehension(Node array) 1.6326 +{ 1.6327 + abortIfSyntaxParser(); 1.6328 + return null(); 1.6329 +} 1.6330 + 1.6331 +template <typename ParseHandler> 1.6332 +typename ParseHandler::Node 1.6333 +Parser<ParseHandler>::generatorComprehensionLambda(GeneratorKind comprehensionKind, 1.6334 + unsigned begin, Node innerStmt) 1.6335 +{ 1.6336 + JS_ASSERT(comprehensionKind == LegacyGenerator || comprehensionKind == StarGenerator); 1.6337 + JS_ASSERT(!!innerStmt == (comprehensionKind == LegacyGenerator)); 1.6338 + 1.6339 + Node genfn = handler.newFunctionDefinition(); 1.6340 + if (!genfn) 1.6341 + return null(); 1.6342 + handler.setOp(genfn, JSOP_LAMBDA); 1.6343 + 1.6344 + ParseContext<ParseHandler> *outerpc = pc; 1.6345 + 1.6346 + // If we are off the main thread, the generator meta-objects have 1.6347 + // already been created by js::StartOffThreadParseScript, so cx will not 1.6348 + // be necessary. 1.6349 + RootedObject proto(context); 1.6350 + if (comprehensionKind == StarGenerator) { 1.6351 + JSContext *cx = context->maybeJSContext(); 1.6352 + proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global()); 1.6353 + if (!proto) 1.6354 + return null(); 1.6355 + } 1.6356 + 1.6357 + RootedFunction fun(context, newFunction(outerpc, /* atom = */ NullPtr(), Expression, proto)); 1.6358 + if (!fun) 1.6359 + return null(); 1.6360 + 1.6361 + // Create box for fun->object early to root it. 1.6362 + Directives directives(/* strict = */ outerpc->sc->strict); 1.6363 + FunctionBox *genFunbox = newFunctionBox(genfn, fun, outerpc, directives, comprehensionKind); 1.6364 + if (!genFunbox) 1.6365 + return null(); 1.6366 + 1.6367 + ParseContext<ParseHandler> genpc(this, outerpc, genfn, genFunbox, 1.6368 + /* newDirectives = */ nullptr, 1.6369 + outerpc->staticLevel + 1, outerpc->blockidGen, 1.6370 + /* blockScopeDepth = */ 0); 1.6371 + if (!genpc.init(tokenStream)) 1.6372 + return null(); 1.6373 + 1.6374 + /* 1.6375 + * We assume conservatively that any deoptimization flags in pc->sc 1.6376 + * come from the kid. So we propagate these flags into genfn. For code 1.6377 + * simplicity we also do not detect if the flags were only set in the 1.6378 + * kid and could be removed from pc->sc. 1.6379 + */ 1.6380 + genFunbox->anyCxFlags = outerpc->sc->anyCxFlags; 1.6381 + if (outerpc->sc->isFunctionBox()) 1.6382 + genFunbox->funCxFlags = outerpc->sc->asFunctionBox()->funCxFlags; 1.6383 + 1.6384 + JS_ASSERT(genFunbox->generatorKind() == comprehensionKind); 1.6385 + genFunbox->inGenexpLambda = true; 1.6386 + handler.setBlockId(genfn, genpc.bodyid); 1.6387 + 1.6388 + Node body; 1.6389 + 1.6390 + if (comprehensionKind == StarGenerator) { 1.6391 + body = comprehension(StarGenerator); 1.6392 + if (!body) 1.6393 + return null(); 1.6394 + } else { 1.6395 + JS_ASSERT(comprehensionKind == LegacyGenerator); 1.6396 + body = legacyComprehensionTail(innerStmt, outerpc->blockid(), LegacyGenerator, 1.6397 + outerpc, LegacyComprehensionHeadBlockScopeDepth(outerpc)); 1.6398 + if (!body) 1.6399 + return null(); 1.6400 + } 1.6401 + 1.6402 + if (comprehensionKind == StarGenerator) 1.6403 + MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN); 1.6404 + 1.6405 + handler.setBeginPosition(body, begin); 1.6406 + handler.setEndPosition(body, pos().end); 1.6407 + 1.6408 + handler.setBeginPosition(genfn, begin); 1.6409 + handler.setEndPosition(genfn, pos().end); 1.6410 + 1.6411 + // Note that if we ever start syntax-parsing generators, we will also 1.6412 + // need to propagate the closed-over variable set to the inner 1.6413 + // lazyscript, as in finishFunctionDefinition. 1.6414 + handler.setFunctionBody(genfn, body); 1.6415 + 1.6416 + PropagateTransitiveParseFlags(genFunbox, outerpc->sc); 1.6417 + 1.6418 + if (!leaveFunction(genfn, outerpc)) 1.6419 + return null(); 1.6420 + 1.6421 + return genfn; 1.6422 +} 1.6423 + 1.6424 +#if JS_HAS_GENERATOR_EXPRS 1.6425 + 1.6426 +/* 1.6427 + * Starting from a |for| keyword after an expression, parse the comprehension 1.6428 + * tail completing this generator expression. Wrap the expression at kid in a 1.6429 + * generator function that is immediately called to evaluate to the generator 1.6430 + * iterator that is the value of this legacy generator expression. 1.6431 + * 1.6432 + * |kid| must be the expression before the |for| keyword; we return an 1.6433 + * application of a generator function that includes the |for| loops and 1.6434 + * |if| guards, with |kid| as the operand of a |yield| expression as the 1.6435 + * innermost loop body. 1.6436 + * 1.6437 + * Note how unlike Python, we do not evaluate the expression to the right of 1.6438 + * the first |in| in the chain of |for| heads. Instead, a generator expression 1.6439 + * is merely sugar for a generator function expression and its application. 1.6440 + */ 1.6441 +template <> 1.6442 +ParseNode * 1.6443 +Parser<FullParseHandler>::legacyGeneratorExpr(ParseNode *expr) 1.6444 +{ 1.6445 + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); 1.6446 + 1.6447 + /* Create a |yield| node for |kid|. */ 1.6448 + ParseNode *yieldExpr = handler.newUnary(PNK_YIELD, JSOP_NOP, expr->pn_pos.begin, expr); 1.6449 + if (!yieldExpr) 1.6450 + return null(); 1.6451 + yieldExpr->setInParens(true); 1.6452 + 1.6453 + // A statement to wrap the yield expression. 1.6454 + ParseNode *yieldStmt = handler.newExprStatement(yieldExpr, expr->pn_pos.end); 1.6455 + if (!yieldStmt) 1.6456 + return null(); 1.6457 + 1.6458 + /* Make a new node for the desugared generator function. */ 1.6459 + ParseNode *genfn = generatorComprehensionLambda(LegacyGenerator, expr->pn_pos.begin, yieldStmt); 1.6460 + if (!genfn) 1.6461 + return null(); 1.6462 + 1.6463 + /* 1.6464 + * Our result is a call expression that invokes the anonymous generator 1.6465 + * function object. 1.6466 + */ 1.6467 + ParseNode *result = ListNode::create(PNK_GENEXP, &handler); 1.6468 + if (!result) 1.6469 + return null(); 1.6470 + result->setOp(JSOP_CALL); 1.6471 + result->pn_pos.begin = genfn->pn_pos.begin; 1.6472 + result->initList(genfn); 1.6473 + return result; 1.6474 +} 1.6475 + 1.6476 +template <> 1.6477 +SyntaxParseHandler::Node 1.6478 +Parser<SyntaxParseHandler>::legacyGeneratorExpr(Node kid) 1.6479 +{ 1.6480 + JS_ALWAYS_FALSE(abortIfSyntaxParser()); 1.6481 + return SyntaxParseHandler::NodeFailure; 1.6482 +} 1.6483 + 1.6484 +static const char js_generator_str[] = "generator"; 1.6485 + 1.6486 +#endif /* JS_HAS_GENERATOR_EXPRS */ 1.6487 + 1.6488 +template <typename ParseHandler> 1.6489 +typename ParseHandler::Node 1.6490 +Parser<ParseHandler>::comprehensionFor(GeneratorKind comprehensionKind) 1.6491 +{ 1.6492 + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); 1.6493 + 1.6494 + uint32_t begin = pos().begin; 1.6495 + 1.6496 + MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); 1.6497 + 1.6498 + // FIXME: Destructuring binding (bug 980828). 1.6499 + 1.6500 + MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME); 1.6501 + RootedPropertyName name(context, tokenStream.currentName()); 1.6502 + if (name == context->names().let) { 1.6503 + report(ParseError, false, null(), JSMSG_LET_COMP_BINDING); 1.6504 + return null(); 1.6505 + } 1.6506 + if (!tokenStream.matchContextualKeyword(context->names().of)) { 1.6507 + report(ParseError, false, null(), JSMSG_OF_AFTER_FOR_NAME); 1.6508 + return null(); 1.6509 + } 1.6510 + 1.6511 + Node rhs = assignExpr(); 1.6512 + if (!rhs) 1.6513 + return null(); 1.6514 + 1.6515 + MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_OF_ITERABLE); 1.6516 + 1.6517 + TokenPos headPos(begin, pos().end); 1.6518 + 1.6519 + StmtInfoPC stmtInfo(context); 1.6520 + BindData<ParseHandler> data(context); 1.6521 + RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context)); 1.6522 + if (!blockObj) 1.6523 + return null(); 1.6524 + data.initLet(DontHoistVars, *blockObj, JSMSG_TOO_MANY_LOCALS); 1.6525 + Node lhs = newName(name); 1.6526 + if (!lhs) 1.6527 + return null(); 1.6528 + Node decls = handler.newList(PNK_LET, lhs, JSOP_NOP); 1.6529 + if (!decls) 1.6530 + return null(); 1.6531 + data.pn = lhs; 1.6532 + if (!data.binder(&data, name, this)) 1.6533 + return null(); 1.6534 + Node letScope = pushLetScope(blockObj, &stmtInfo); 1.6535 + if (!letScope) 1.6536 + return null(); 1.6537 + handler.setLexicalScopeBody(letScope, decls); 1.6538 + 1.6539 + Node assignLhs = newName(name); 1.6540 + if (!assignLhs) 1.6541 + return null(); 1.6542 + if (!noteNameUse(name, assignLhs)) 1.6543 + return null(); 1.6544 + handler.setOp(assignLhs, JSOP_SETNAME); 1.6545 + 1.6546 + Node head = handler.newForHead(PNK_FOROF, letScope, assignLhs, rhs, headPos); 1.6547 + if (!head) 1.6548 + return null(); 1.6549 + 1.6550 + Node tail = comprehensionTail(comprehensionKind); 1.6551 + if (!tail) 1.6552 + return null(); 1.6553 + 1.6554 + PopStatementPC(tokenStream, pc); 1.6555 + 1.6556 + return handler.newForStatement(begin, head, tail, JSOP_ITER); 1.6557 +} 1.6558 + 1.6559 +template <typename ParseHandler> 1.6560 +typename ParseHandler::Node 1.6561 +Parser<ParseHandler>::comprehensionIf(GeneratorKind comprehensionKind) 1.6562 +{ 1.6563 + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_IF)); 1.6564 + 1.6565 + uint32_t begin = pos().begin; 1.6566 + 1.6567 + MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND); 1.6568 + Node cond = assignExpr(); 1.6569 + if (!cond) 1.6570 + return null(); 1.6571 + MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND); 1.6572 + 1.6573 + /* Check for (a = b) and warn about possible (a == b) mistype. */ 1.6574 + if (handler.isOperationWithoutParens(cond, PNK_ASSIGN) && 1.6575 + !report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN)) 1.6576 + { 1.6577 + return null(); 1.6578 + } 1.6579 + 1.6580 + Node then = comprehensionTail(comprehensionKind); 1.6581 + if (!then) 1.6582 + return null(); 1.6583 + 1.6584 + return handler.newIfStatement(begin, cond, then, null()); 1.6585 +} 1.6586 + 1.6587 +template <typename ParseHandler> 1.6588 +typename ParseHandler::Node 1.6589 +Parser<ParseHandler>::comprehensionTail(GeneratorKind comprehensionKind) 1.6590 +{ 1.6591 + JS_CHECK_RECURSION(context, return null()); 1.6592 + 1.6593 + if (tokenStream.matchToken(TOK_FOR, TokenStream::Operand)) 1.6594 + return comprehensionFor(comprehensionKind); 1.6595 + 1.6596 + if (tokenStream.matchToken(TOK_IF, TokenStream::Operand)) 1.6597 + return comprehensionIf(comprehensionKind); 1.6598 + 1.6599 + uint32_t begin = pos().begin; 1.6600 + 1.6601 + Node bodyExpr = assignExpr(); 1.6602 + if (!bodyExpr) 1.6603 + return null(); 1.6604 + 1.6605 + if (comprehensionKind == NotGenerator) 1.6606 + return handler.newUnary(PNK_ARRAYPUSH, JSOP_ARRAYPUSH, begin, bodyExpr); 1.6607 + 1.6608 + JS_ASSERT(comprehensionKind == StarGenerator); 1.6609 + Node yieldExpr = handler.newUnary(PNK_YIELD, JSOP_NOP, begin, bodyExpr); 1.6610 + if (!yieldExpr) 1.6611 + return null(); 1.6612 + handler.setInParens(yieldExpr); 1.6613 + 1.6614 + return handler.newExprStatement(yieldExpr, pos().end); 1.6615 +} 1.6616 + 1.6617 +// Parse an ES6 generator or array comprehension, starting at the first 'for'. 1.6618 +// The caller is responsible for matching the ending TOK_RP or TOK_RB. 1.6619 +template <typename ParseHandler> 1.6620 +typename ParseHandler::Node 1.6621 +Parser<ParseHandler>::comprehension(GeneratorKind comprehensionKind) 1.6622 +{ 1.6623 + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); 1.6624 + 1.6625 + uint32_t startYieldOffset = pc->lastYieldOffset; 1.6626 + 1.6627 + Node body = comprehensionFor(comprehensionKind); 1.6628 + if (!body) 1.6629 + return null(); 1.6630 + 1.6631 + if (comprehensionKind != NotGenerator && pc->lastYieldOffset != startYieldOffset) { 1.6632 + reportWithOffset(ParseError, false, pc->lastYieldOffset, 1.6633 + JSMSG_BAD_GENEXP_BODY, js_yield_str); 1.6634 + return null(); 1.6635 + } 1.6636 + 1.6637 + return body; 1.6638 +} 1.6639 + 1.6640 +template <typename ParseHandler> 1.6641 +typename ParseHandler::Node 1.6642 +Parser<ParseHandler>::arrayComprehension(uint32_t begin) 1.6643 +{ 1.6644 + Node inner = comprehension(NotGenerator); 1.6645 + if (!inner) 1.6646 + return null(); 1.6647 + 1.6648 + MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION); 1.6649 + 1.6650 + Node comp = handler.newList(PNK_ARRAYCOMP, inner); 1.6651 + if (!comp) 1.6652 + return null(); 1.6653 + 1.6654 + handler.setBeginPosition(comp, begin); 1.6655 + handler.setEndPosition(comp, pos().end); 1.6656 + 1.6657 + return comp; 1.6658 +} 1.6659 + 1.6660 +template <typename ParseHandler> 1.6661 +typename ParseHandler::Node 1.6662 +Parser<ParseHandler>::generatorComprehension(uint32_t begin) 1.6663 +{ 1.6664 + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); 1.6665 + 1.6666 + // We have no problem parsing generator comprehensions inside lazy 1.6667 + // functions, but the bytecode emitter currently can't handle them that way, 1.6668 + // because when it goes to emit the code for the inner generator function, 1.6669 + // it expects outer functions to have non-lazy scripts. 1.6670 + if (!abortIfSyntaxParser()) 1.6671 + return null(); 1.6672 + 1.6673 + Node genfn = generatorComprehensionLambda(StarGenerator, begin, null()); 1.6674 + if (!genfn) 1.6675 + return null(); 1.6676 + 1.6677 + Node result = handler.newList(PNK_GENEXP, genfn, JSOP_CALL); 1.6678 + if (!result) 1.6679 + return null(); 1.6680 + handler.setBeginPosition(result, begin); 1.6681 + handler.setEndPosition(result, pos().end); 1.6682 + 1.6683 + return result; 1.6684 +} 1.6685 + 1.6686 +template <typename ParseHandler> 1.6687 +typename ParseHandler::Node 1.6688 +Parser<ParseHandler>::assignExprWithoutYield(unsigned msg) 1.6689 +{ 1.6690 + uint32_t startYieldOffset = pc->lastYieldOffset; 1.6691 + Node res = assignExpr(); 1.6692 + if (res && pc->lastYieldOffset != startYieldOffset) { 1.6693 + reportWithOffset(ParseError, false, pc->lastYieldOffset, 1.6694 + msg, js_yield_str); 1.6695 + return null(); 1.6696 + } 1.6697 + return res; 1.6698 +} 1.6699 + 1.6700 +template <typename ParseHandler> 1.6701 +bool 1.6702 +Parser<ParseHandler>::argumentList(Node listNode, bool *isSpread) 1.6703 +{ 1.6704 + if (tokenStream.matchToken(TOK_RP, TokenStream::Operand)) { 1.6705 + handler.setEndPosition(listNode, pos().end); 1.6706 + return true; 1.6707 + } 1.6708 + 1.6709 + uint32_t startYieldOffset = pc->lastYieldOffset; 1.6710 + bool arg0 = true; 1.6711 + 1.6712 + do { 1.6713 + bool spread = false; 1.6714 + uint32_t begin = 0; 1.6715 + if (tokenStream.matchToken(TOK_TRIPLEDOT, TokenStream::Operand)) { 1.6716 + spread = true; 1.6717 + begin = pos().begin; 1.6718 + *isSpread = true; 1.6719 + } 1.6720 + 1.6721 + Node argNode = assignExpr(); 1.6722 + if (!argNode) 1.6723 + return false; 1.6724 + if (spread) { 1.6725 + argNode = handler.newUnary(PNK_SPREAD, JSOP_NOP, begin, argNode); 1.6726 + if (!argNode) 1.6727 + return null(); 1.6728 + } 1.6729 + 1.6730 + if (handler.isOperationWithoutParens(argNode, PNK_YIELD) && 1.6731 + tokenStream.peekToken() == TOK_COMMA) { 1.6732 + report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str); 1.6733 + return false; 1.6734 + } 1.6735 +#if JS_HAS_GENERATOR_EXPRS 1.6736 + if (!spread && tokenStream.matchToken(TOK_FOR)) { 1.6737 + if (pc->lastYieldOffset != startYieldOffset) { 1.6738 + reportWithOffset(ParseError, false, pc->lastYieldOffset, 1.6739 + JSMSG_BAD_GENEXP_BODY, js_yield_str); 1.6740 + return false; 1.6741 + } 1.6742 + argNode = legacyGeneratorExpr(argNode); 1.6743 + if (!argNode) 1.6744 + return false; 1.6745 + if (!arg0 || tokenStream.peekToken() == TOK_COMMA) { 1.6746 + report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str); 1.6747 + return false; 1.6748 + } 1.6749 + } 1.6750 +#endif 1.6751 + arg0 = false; 1.6752 + 1.6753 + handler.addList(listNode, argNode); 1.6754 + } while (tokenStream.matchToken(TOK_COMMA)); 1.6755 + 1.6756 + if (tokenStream.getToken() != TOK_RP) { 1.6757 + report(ParseError, false, null(), JSMSG_PAREN_AFTER_ARGS); 1.6758 + return false; 1.6759 + } 1.6760 + handler.setEndPosition(listNode, pos().end); 1.6761 + return true; 1.6762 +} 1.6763 + 1.6764 +template <typename ParseHandler> 1.6765 +typename ParseHandler::Node 1.6766 +Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax) 1.6767 +{ 1.6768 + JS_ASSERT(tokenStream.isCurrentTokenType(tt)); 1.6769 + 1.6770 + Node lhs; 1.6771 + 1.6772 + JS_CHECK_RECURSION(context, return null()); 1.6773 + 1.6774 + /* Check for new expression first. */ 1.6775 + if (tt == TOK_NEW) { 1.6776 + lhs = handler.newList(PNK_NEW, null(), JSOP_NEW); 1.6777 + if (!lhs) 1.6778 + return null(); 1.6779 + 1.6780 + tt = tokenStream.getToken(TokenStream::Operand); 1.6781 + Node ctorExpr = memberExpr(tt, false); 1.6782 + if (!ctorExpr) 1.6783 + return null(); 1.6784 + 1.6785 + handler.addList(lhs, ctorExpr); 1.6786 + 1.6787 + if (tokenStream.matchToken(TOK_LP)) { 1.6788 + bool isSpread = false; 1.6789 + if (!argumentList(lhs, &isSpread)) 1.6790 + return null(); 1.6791 + if (isSpread) 1.6792 + handler.setOp(lhs, JSOP_SPREADNEW); 1.6793 + } 1.6794 + } else { 1.6795 + lhs = primaryExpr(tt); 1.6796 + if (!lhs) 1.6797 + return null(); 1.6798 + } 1.6799 + 1.6800 + while ((tt = tokenStream.getToken()) > TOK_EOF) { 1.6801 + Node nextMember; 1.6802 + if (tt == TOK_DOT) { 1.6803 + tt = tokenStream.getToken(TokenStream::KeywordIsName); 1.6804 + if (tt == TOK_ERROR) 1.6805 + return null(); 1.6806 + if (tt == TOK_NAME) { 1.6807 + PropertyName *field = tokenStream.currentName(); 1.6808 + nextMember = handler.newPropertyAccess(lhs, field, pos().end); 1.6809 + if (!nextMember) 1.6810 + return null(); 1.6811 + } else { 1.6812 + report(ParseError, false, null(), JSMSG_NAME_AFTER_DOT); 1.6813 + return null(); 1.6814 + } 1.6815 + } else if (tt == TOK_LB) { 1.6816 + Node propExpr = expr(); 1.6817 + if (!propExpr) 1.6818 + return null(); 1.6819 + 1.6820 + MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX); 1.6821 + 1.6822 + nextMember = handler.newPropertyByValue(lhs, propExpr, pos().end); 1.6823 + if (!nextMember) 1.6824 + return null(); 1.6825 + } else if (allowCallSyntax && tt == TOK_LP) { 1.6826 + JSOp op = JSOP_CALL; 1.6827 + nextMember = handler.newList(PNK_CALL, null(), JSOP_CALL); 1.6828 + if (!nextMember) 1.6829 + return null(); 1.6830 + 1.6831 + if (JSAtom *atom = handler.isName(lhs)) { 1.6832 + if (atom == context->names().eval) { 1.6833 + /* Select JSOP_EVAL and flag pc as heavyweight. */ 1.6834 + op = JSOP_EVAL; 1.6835 + pc->sc->setBindingsAccessedDynamically(); 1.6836 + 1.6837 + /* 1.6838 + * In non-strict mode code, direct calls to eval can add 1.6839 + * variables to the call object. 1.6840 + */ 1.6841 + if (pc->sc->isFunctionBox() && !pc->sc->strict) 1.6842 + pc->sc->asFunctionBox()->setHasExtensibleScope(); 1.6843 + } 1.6844 + } else if (JSAtom *atom = handler.isGetProp(lhs)) { 1.6845 + /* Select JSOP_FUNAPPLY given foo.apply(...). */ 1.6846 + if (atom == context->names().apply) { 1.6847 + op = JSOP_FUNAPPLY; 1.6848 + if (pc->sc->isFunctionBox()) 1.6849 + pc->sc->asFunctionBox()->usesApply = true; 1.6850 + } else if (atom == context->names().call) { 1.6851 + op = JSOP_FUNCALL; 1.6852 + } 1.6853 + } 1.6854 + 1.6855 + handler.setBeginPosition(nextMember, lhs); 1.6856 + handler.addList(nextMember, lhs); 1.6857 + 1.6858 + bool isSpread = false; 1.6859 + if (!argumentList(nextMember, &isSpread)) 1.6860 + return null(); 1.6861 + if (isSpread) 1.6862 + op = (op == JSOP_EVAL ? JSOP_SPREADEVAL : JSOP_SPREADCALL); 1.6863 + handler.setOp(nextMember, op); 1.6864 + } else { 1.6865 + tokenStream.ungetToken(); 1.6866 + return lhs; 1.6867 + } 1.6868 + 1.6869 + lhs = nextMember; 1.6870 + } 1.6871 + if (tt == TOK_ERROR) 1.6872 + return null(); 1.6873 + return lhs; 1.6874 +} 1.6875 + 1.6876 +template <typename ParseHandler> 1.6877 +typename ParseHandler::Node 1.6878 +Parser<ParseHandler>::newName(PropertyName *name) 1.6879 +{ 1.6880 + return handler.newName(name, pc->blockid(), pos()); 1.6881 +} 1.6882 + 1.6883 +template <typename ParseHandler> 1.6884 +typename ParseHandler::Node 1.6885 +Parser<ParseHandler>::identifierName() 1.6886 +{ 1.6887 + RootedPropertyName name(context, tokenStream.currentName()); 1.6888 + Node pn = newName(name); 1.6889 + if (!pn) 1.6890 + return null(); 1.6891 + 1.6892 + if (!pc->inDeclDestructuring && !noteNameUse(name, pn)) 1.6893 + return null(); 1.6894 + 1.6895 + return pn; 1.6896 +} 1.6897 + 1.6898 +template <typename ParseHandler> 1.6899 +typename ParseHandler::Node 1.6900 +Parser<ParseHandler>::stringLiteral() 1.6901 +{ 1.6902 + JSAtom *atom = tokenStream.currentToken().atom(); 1.6903 + 1.6904 + // Large strings are fast to parse but slow to compress. Stop compression on 1.6905 + // them, so we don't wait for a long time for compression to finish at the 1.6906 + // end of compilation. 1.6907 + const size_t HUGE_STRING = 50000; 1.6908 + if (sct && sct->active() && atom->length() >= HUGE_STRING) 1.6909 + sct->abort(); 1.6910 + 1.6911 + return handler.newStringLiteral(atom, pos()); 1.6912 +} 1.6913 + 1.6914 +template <typename ParseHandler> 1.6915 +typename ParseHandler::Node 1.6916 +Parser<ParseHandler>::newRegExp() 1.6917 +{ 1.6918 + // Create the regexp even when doing a syntax parse, to check the regexp's syntax. 1.6919 + const jschar *chars = tokenStream.getTokenbuf().begin(); 1.6920 + size_t length = tokenStream.getTokenbuf().length(); 1.6921 + RegExpFlag flags = tokenStream.currentToken().regExpFlags(); 1.6922 + 1.6923 + Rooted<RegExpObject*> reobj(context); 1.6924 + if (RegExpStatics *res = context->global()->getRegExpStatics()) 1.6925 + reobj = RegExpObject::create(context, res, chars, length, flags, &tokenStream); 1.6926 + else 1.6927 + reobj = RegExpObject::createNoStatics(context, chars, length, flags, &tokenStream); 1.6928 + 1.6929 + if (!reobj) 1.6930 + return null(); 1.6931 + 1.6932 + return handler.newRegExp(reobj, pos(), *this); 1.6933 +} 1.6934 + 1.6935 +template <typename ParseHandler> 1.6936 +typename ParseHandler::Node 1.6937 +Parser<ParseHandler>::arrayInitializer() 1.6938 +{ 1.6939 + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LB)); 1.6940 + 1.6941 + uint32_t begin = pos().begin; 1.6942 + Node literal = handler.newArrayLiteral(begin, pc->blockidGen); 1.6943 + if (!literal) 1.6944 + return null(); 1.6945 + 1.6946 + if (tokenStream.matchToken(TOK_RB, TokenStream::Operand)) { 1.6947 + /* 1.6948 + * Mark empty arrays as non-constant, since we cannot easily 1.6949 + * determine their type. 1.6950 + */ 1.6951 + handler.setListFlag(literal, PNX_NONCONST); 1.6952 + } else if (tokenStream.matchToken(TOK_FOR, TokenStream::Operand)) { 1.6953 + // ES6 array comprehension. 1.6954 + return arrayComprehension(begin); 1.6955 + } else { 1.6956 + bool spread = false, missingTrailingComma = false; 1.6957 + uint32_t index = 0; 1.6958 + for (; ; index++) { 1.6959 + if (index == JSObject::NELEMENTS_LIMIT) { 1.6960 + report(ParseError, false, null(), JSMSG_ARRAY_INIT_TOO_BIG); 1.6961 + return null(); 1.6962 + } 1.6963 + 1.6964 + TokenKind tt = tokenStream.peekToken(TokenStream::Operand); 1.6965 + if (tt == TOK_RB) 1.6966 + break; 1.6967 + 1.6968 + if (tt == TOK_COMMA) { 1.6969 + tokenStream.consumeKnownToken(TOK_COMMA); 1.6970 + if (!handler.addElision(literal, pos())) 1.6971 + return null(); 1.6972 + } else if (tt == TOK_TRIPLEDOT) { 1.6973 + spread = true; 1.6974 + tokenStream.consumeKnownToken(TOK_TRIPLEDOT); 1.6975 + uint32_t begin = pos().begin; 1.6976 + Node inner = assignExpr(); 1.6977 + if (!inner) 1.6978 + return null(); 1.6979 + if (!handler.addSpreadElement(literal, begin, inner)) 1.6980 + return null(); 1.6981 + } else { 1.6982 + Node element = assignExpr(); 1.6983 + if (!element) 1.6984 + return null(); 1.6985 + if (foldConstants && !FoldConstants(context, &element, this)) 1.6986 + return null(); 1.6987 + if (!handler.addArrayElement(literal, element)) 1.6988 + return null(); 1.6989 + } 1.6990 + 1.6991 + if (tt != TOK_COMMA) { 1.6992 + /* If we didn't already match TOK_COMMA in above case. */ 1.6993 + if (!tokenStream.matchToken(TOK_COMMA)) { 1.6994 + missingTrailingComma = true; 1.6995 + break; 1.6996 + } 1.6997 + } 1.6998 + } 1.6999 + 1.7000 + /* 1.7001 + * At this point, (index == 0 && missingTrailingComma) implies one 1.7002 + * element initialiser was parsed. 1.7003 + * 1.7004 + * A legacy array comprehension of the form: 1.7005 + * 1.7006 + * [i * j for (i in o) for (j in p) if (i != j)] 1.7007 + * 1.7008 + * translates to roughly the following let expression: 1.7009 + * 1.7010 + * let (array = new Array, i, j) { 1.7011 + * for (i in o) let { 1.7012 + * for (j in p) 1.7013 + * if (i != j) 1.7014 + * array.push(i * j) 1.7015 + * } 1.7016 + * array 1.7017 + * } 1.7018 + * 1.7019 + * where array is a nameless block-local variable. The "roughly" means 1.7020 + * that an implementation may optimize away the array.push. A legacy 1.7021 + * array comprehension opens exactly one block scope, no matter how many 1.7022 + * for heads it contains. 1.7023 + * 1.7024 + * Each let () {...} or for (let ...) ... compiles to: 1.7025 + * 1.7026 + * JSOP_PUSHN <N> // Push space for block-scoped locals. 1.7027 + * (JSOP_PUSHBLOCKSCOPE <O>) // If a local is aliased, push on scope 1.7028 + * // chain. 1.7029 + * ... 1.7030 + * JSOP_DEBUGLEAVEBLOCK // Invalidate any DebugScope proxies. 1.7031 + * JSOP_POPBLOCKSCOPE? // Pop off scope chain, if needed. 1.7032 + * JSOP_POPN <N> // Pop space for block-scoped locals. 1.7033 + * 1.7034 + * where <o> is a literal object representing the block scope, 1.7035 + * with <n> properties, naming each var declared in the block. 1.7036 + * 1.7037 + * Each var declaration in a let-block binds a name in <o> at compile 1.7038 + * time. A block-local var is accessed by the JSOP_GETLOCAL and 1.7039 + * JSOP_SETLOCAL ops. These ops have an immediate operand, the local 1.7040 + * slot's stack index from fp->spbase. 1.7041 + * 1.7042 + * The legacy array comprehension iteration step, array.push(i * j) in 1.7043 + * the example above, is done by <i * j>; JSOP_ARRAYPUSH <array>, where 1.7044 + * <array> is the index of array's stack slot. 1.7045 + */ 1.7046 + if (index == 0 && !spread && tokenStream.matchToken(TOK_FOR) && missingTrailingComma) 1.7047 + return legacyArrayComprehension(literal); 1.7048 + 1.7049 + MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST); 1.7050 + } 1.7051 + handler.setEndPosition(literal, pos().end); 1.7052 + return literal; 1.7053 +} 1.7054 + 1.7055 +static JSAtom* 1.7056 +DoubleToAtom(ExclusiveContext *cx, double value) 1.7057 +{ 1.7058 + // This is safe because doubles can not be moved. 1.7059 + Value tmp = DoubleValue(value); 1.7060 + return ToAtom<CanGC>(cx, HandleValue::fromMarkedLocation(&tmp)); 1.7061 +} 1.7062 + 1.7063 +template <typename ParseHandler> 1.7064 +typename ParseHandler::Node 1.7065 +Parser<ParseHandler>::objectLiteral() 1.7066 +{ 1.7067 + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LC)); 1.7068 + 1.7069 + /* 1.7070 + * A map from property names we've seen thus far to a mask of property 1.7071 + * assignment types. 1.7072 + */ 1.7073 + AtomIndexMap seen; 1.7074 + 1.7075 + enum AssignmentType { 1.7076 + GET = 0x1, 1.7077 + SET = 0x2, 1.7078 + VALUE = 0x4 | GET | SET 1.7079 + }; 1.7080 + 1.7081 + Node literal = handler.newObjectLiteral(pos().begin); 1.7082 + if (!literal) 1.7083 + return null(); 1.7084 + 1.7085 + RootedAtom atom(context); 1.7086 + for (;;) { 1.7087 + TokenKind ltok = tokenStream.getToken(TokenStream::KeywordIsName); 1.7088 + if (ltok == TOK_RC) 1.7089 + break; 1.7090 + 1.7091 + JSOp op = JSOP_INITPROP; 1.7092 + Node propname; 1.7093 + switch (ltok) { 1.7094 + case TOK_NUMBER: 1.7095 + atom = DoubleToAtom(context, tokenStream.currentToken().number()); 1.7096 + if (!atom) 1.7097 + return null(); 1.7098 + propname = newNumber(tokenStream.currentToken()); 1.7099 + break; 1.7100 + 1.7101 + case TOK_NAME: { 1.7102 + atom = tokenStream.currentName(); 1.7103 + if (atom == context->names().get) { 1.7104 + op = JSOP_INITPROP_GETTER; 1.7105 + } else if (atom == context->names().set) { 1.7106 + op = JSOP_INITPROP_SETTER; 1.7107 + } else { 1.7108 + propname = handler.newIdentifier(atom, pos()); 1.7109 + if (!propname) 1.7110 + return null(); 1.7111 + break; 1.7112 + } 1.7113 + 1.7114 + // We have parsed |get| or |set|. Look for an accessor property 1.7115 + // name next. 1.7116 + TokenKind tt = tokenStream.getToken(TokenStream::KeywordIsName); 1.7117 + if (tt == TOK_NAME) { 1.7118 + atom = tokenStream.currentName(); 1.7119 + propname = newName(atom->asPropertyName()); 1.7120 + if (!propname) 1.7121 + return null(); 1.7122 + } else if (tt == TOK_STRING) { 1.7123 + atom = tokenStream.currentToken().atom(); 1.7124 + 1.7125 + uint32_t index; 1.7126 + if (atom->isIndex(&index)) { 1.7127 + propname = handler.newNumber(index, NoDecimal, pos()); 1.7128 + if (!propname) 1.7129 + return null(); 1.7130 + atom = DoubleToAtom(context, index); 1.7131 + if (!atom) 1.7132 + return null(); 1.7133 + } else { 1.7134 + propname = stringLiteral(); 1.7135 + if (!propname) 1.7136 + return null(); 1.7137 + } 1.7138 + } else if (tt == TOK_NUMBER) { 1.7139 + atom = DoubleToAtom(context, tokenStream.currentToken().number()); 1.7140 + if (!atom) 1.7141 + return null(); 1.7142 + propname = newNumber(tokenStream.currentToken()); 1.7143 + if (!propname) 1.7144 + return null(); 1.7145 + } else { 1.7146 + // Not an accessor property after all. 1.7147 + tokenStream.ungetToken(); 1.7148 + propname = handler.newIdentifier(atom, pos()); 1.7149 + if (!propname) 1.7150 + return null(); 1.7151 + op = JSOP_INITPROP; 1.7152 + break; 1.7153 + } 1.7154 + 1.7155 + JS_ASSERT(op == JSOP_INITPROP_GETTER || op == JSOP_INITPROP_SETTER); 1.7156 + break; 1.7157 + } 1.7158 + 1.7159 + case TOK_STRING: { 1.7160 + atom = tokenStream.currentToken().atom(); 1.7161 + uint32_t index; 1.7162 + if (atom->isIndex(&index)) { 1.7163 + propname = handler.newNumber(index, NoDecimal, pos()); 1.7164 + if (!propname) 1.7165 + return null(); 1.7166 + } else { 1.7167 + propname = stringLiteral(); 1.7168 + if (!propname) 1.7169 + return null(); 1.7170 + } 1.7171 + break; 1.7172 + } 1.7173 + 1.7174 + default: 1.7175 + report(ParseError, false, null(), JSMSG_BAD_PROP_ID); 1.7176 + return null(); 1.7177 + } 1.7178 + 1.7179 + if (op == JSOP_INITPROP) { 1.7180 + TokenKind tt = tokenStream.getToken(); 1.7181 + Node propexpr; 1.7182 + if (tt == TOK_COLON) { 1.7183 + propexpr = assignExpr(); 1.7184 + if (!propexpr) 1.7185 + return null(); 1.7186 + 1.7187 + if (foldConstants && !FoldConstants(context, &propexpr, this)) 1.7188 + return null(); 1.7189 + 1.7190 + /* 1.7191 + * Treat initializers which mutate __proto__ as non-constant, 1.7192 + * so that we can later assume singleton objects delegate to 1.7193 + * the default Object.prototype. 1.7194 + */ 1.7195 + if (!handler.isConstant(propexpr) || atom == context->names().proto) 1.7196 + handler.setListFlag(literal, PNX_NONCONST); 1.7197 + 1.7198 + if (!handler.addPropertyDefinition(literal, propname, propexpr)) 1.7199 + return null(); 1.7200 + } 1.7201 + else if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC)) { 1.7202 + /* 1.7203 + * Support, e.g., |var {x, y} = o| as destructuring shorthand 1.7204 + * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8. 1.7205 + */ 1.7206 + if (!abortIfSyntaxParser()) 1.7207 + return null(); 1.7208 + tokenStream.ungetToken(); 1.7209 + if (!tokenStream.checkForKeyword(atom->charsZ(), atom->length(), nullptr)) 1.7210 + return null(); 1.7211 + PropertyName *name = handler.isName(propname); 1.7212 + JS_ASSERT(atom); 1.7213 + propname = newName(name); 1.7214 + if (!propname) 1.7215 + return null(); 1.7216 + if (!handler.addShorthandPropertyDefinition(literal, propname)) 1.7217 + return null(); 1.7218 + } 1.7219 + else { 1.7220 + report(ParseError, false, null(), JSMSG_COLON_AFTER_ID); 1.7221 + return null(); 1.7222 + } 1.7223 + } else { 1.7224 + /* NB: Getter function in { get x(){} } is unnamed. */ 1.7225 + Rooted<PropertyName*> funName(context, nullptr); 1.7226 + TokenStream::Position start(keepAtoms); 1.7227 + tokenStream.tell(&start); 1.7228 + Node accessor = functionDef(funName, start, op == JSOP_INITPROP_GETTER ? Getter : Setter, 1.7229 + Expression, NotGenerator); 1.7230 + if (!accessor) 1.7231 + return null(); 1.7232 + if (!handler.addAccessorPropertyDefinition(literal, propname, accessor, op)) 1.7233 + return null(); 1.7234 + } 1.7235 + 1.7236 + /* 1.7237 + * Check for duplicate property names. Duplicate data properties 1.7238 + * only conflict in strict mode. Duplicate getter or duplicate 1.7239 + * setter halves always conflict. A data property conflicts with 1.7240 + * any part of an accessor property. 1.7241 + */ 1.7242 + AssignmentType assignType; 1.7243 + if (op == JSOP_INITPROP) 1.7244 + assignType = VALUE; 1.7245 + else if (op == JSOP_INITPROP_GETTER) 1.7246 + assignType = GET; 1.7247 + else if (op == JSOP_INITPROP_SETTER) 1.7248 + assignType = SET; 1.7249 + else 1.7250 + MOZ_ASSUME_UNREACHABLE("bad opcode in object initializer"); 1.7251 + 1.7252 + AtomIndexAddPtr p = seen.lookupForAdd(atom); 1.7253 + if (p) { 1.7254 + jsatomid index = p.value(); 1.7255 + AssignmentType oldAssignType = AssignmentType(index); 1.7256 + if ((oldAssignType & assignType) && 1.7257 + (oldAssignType != VALUE || assignType != VALUE || pc->sc->needStrictChecks())) 1.7258 + { 1.7259 + JSAutoByteString name; 1.7260 + if (!AtomToPrintableString(context, atom, &name)) 1.7261 + return null(); 1.7262 + 1.7263 + ParseReportKind reportKind = 1.7264 + (oldAssignType == VALUE && assignType == VALUE && !pc->sc->needStrictChecks()) 1.7265 + ? ParseWarning 1.7266 + : (pc->sc->needStrictChecks() ? ParseStrictError : ParseError); 1.7267 + if (!report(reportKind, pc->sc->strict, null(), 1.7268 + JSMSG_DUPLICATE_PROPERTY, name.ptr())) 1.7269 + { 1.7270 + return null(); 1.7271 + } 1.7272 + } 1.7273 + p.value() = assignType | oldAssignType; 1.7274 + } else { 1.7275 + if (!seen.add(p, atom, assignType)) 1.7276 + return null(); 1.7277 + } 1.7278 + 1.7279 + TokenKind tt = tokenStream.getToken(); 1.7280 + if (tt == TOK_RC) 1.7281 + break; 1.7282 + if (tt != TOK_COMMA) { 1.7283 + report(ParseError, false, null(), JSMSG_CURLY_AFTER_LIST); 1.7284 + return null(); 1.7285 + } 1.7286 + } 1.7287 + 1.7288 + handler.setEndPosition(literal, pos().end); 1.7289 + return literal; 1.7290 +} 1.7291 + 1.7292 +template <typename ParseHandler> 1.7293 +typename ParseHandler::Node 1.7294 +Parser<ParseHandler>::primaryExpr(TokenKind tt) 1.7295 +{ 1.7296 + JS_ASSERT(tokenStream.isCurrentTokenType(tt)); 1.7297 + JS_CHECK_RECURSION(context, return null()); 1.7298 + 1.7299 + switch (tt) { 1.7300 + case TOK_FUNCTION: 1.7301 + return functionExpr(); 1.7302 + 1.7303 + case TOK_LB: 1.7304 + return arrayInitializer(); 1.7305 + 1.7306 + case TOK_LC: 1.7307 + return objectLiteral(); 1.7308 + 1.7309 + case TOK_LET: 1.7310 + return letBlock(LetExpresion); 1.7311 + 1.7312 + case TOK_LP: 1.7313 + return parenExprOrGeneratorComprehension(); 1.7314 + 1.7315 + case TOK_STRING: 1.7316 + return stringLiteral(); 1.7317 + 1.7318 + case TOK_YIELD: 1.7319 + if (!checkYieldNameValidity()) 1.7320 + return null(); 1.7321 + // Fall through. 1.7322 + case TOK_NAME: 1.7323 + return identifierName(); 1.7324 + 1.7325 + case TOK_REGEXP: 1.7326 + return newRegExp(); 1.7327 + 1.7328 + case TOK_NUMBER: 1.7329 + return newNumber(tokenStream.currentToken()); 1.7330 + 1.7331 + case TOK_TRUE: 1.7332 + return handler.newBooleanLiteral(true, pos()); 1.7333 + case TOK_FALSE: 1.7334 + return handler.newBooleanLiteral(false, pos()); 1.7335 + case TOK_THIS: 1.7336 + return handler.newThisLiteral(pos()); 1.7337 + case TOK_NULL: 1.7338 + return handler.newNullLiteral(pos()); 1.7339 + 1.7340 + case TOK_RP: 1.7341 + // Not valid expression syntax, but this is valid in an arrow function 1.7342 + // with no params: `() => body`. 1.7343 + if (tokenStream.peekToken() == TOK_ARROW) { 1.7344 + tokenStream.ungetToken(); // put back right paren 1.7345 + 1.7346 + // Now just return something that will allow parsing to continue. 1.7347 + // It doesn't matter what; when we reach the =>, we will rewind and 1.7348 + // reparse the whole arrow function. See Parser::assignExpr. 1.7349 + return handler.newNullLiteral(pos()); 1.7350 + } 1.7351 + report(ParseError, false, null(), JSMSG_SYNTAX_ERROR); 1.7352 + return null(); 1.7353 + 1.7354 + case TOK_TRIPLEDOT: 1.7355 + // Not valid expression syntax, but this is valid in an arrow function 1.7356 + // with a rest param: `(a, b, ...rest) => body`. 1.7357 + if (tokenStream.matchToken(TOK_NAME) && 1.7358 + tokenStream.matchToken(TOK_RP) && 1.7359 + tokenStream.peekToken() == TOK_ARROW) 1.7360 + { 1.7361 + tokenStream.ungetToken(); // put back right paren 1.7362 + 1.7363 + // Return an arbitrary expression node. See case TOK_RP above. 1.7364 + return handler.newNullLiteral(pos()); 1.7365 + } 1.7366 + report(ParseError, false, null(), JSMSG_SYNTAX_ERROR); 1.7367 + return null(); 1.7368 + 1.7369 + case TOK_ERROR: 1.7370 + /* The scanner or one of its subroutines reported the error. */ 1.7371 + return null(); 1.7372 + 1.7373 + default: 1.7374 + report(ParseError, false, null(), JSMSG_SYNTAX_ERROR); 1.7375 + return null(); 1.7376 + } 1.7377 +} 1.7378 + 1.7379 +template <typename ParseHandler> 1.7380 +typename ParseHandler::Node 1.7381 +Parser<ParseHandler>::parenExprOrGeneratorComprehension() 1.7382 +{ 1.7383 + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LP)); 1.7384 + uint32_t begin = pos().begin; 1.7385 + uint32_t startYieldOffset = pc->lastYieldOffset; 1.7386 + 1.7387 + if (tokenStream.matchToken(TOK_FOR, TokenStream::Operand)) 1.7388 + return generatorComprehension(begin); 1.7389 + 1.7390 + /* 1.7391 + * Always accept the 'in' operator in a parenthesized expression, 1.7392 + * where it's unambiguous, even if we might be parsing the init of a 1.7393 + * for statement. 1.7394 + */ 1.7395 + bool oldParsingForInit = pc->parsingForInit; 1.7396 + pc->parsingForInit = false; 1.7397 + Node pn = expr(); 1.7398 + pc->parsingForInit = oldParsingForInit; 1.7399 + 1.7400 + if (!pn) 1.7401 + return null(); 1.7402 + 1.7403 +#if JS_HAS_GENERATOR_EXPRS 1.7404 + if (tokenStream.matchToken(TOK_FOR)) { 1.7405 + if (pc->lastYieldOffset != startYieldOffset) { 1.7406 + reportWithOffset(ParseError, false, pc->lastYieldOffset, 1.7407 + JSMSG_BAD_GENEXP_BODY, js_yield_str); 1.7408 + return null(); 1.7409 + } 1.7410 + if (handler.isOperationWithoutParens(pn, PNK_COMMA)) { 1.7411 + report(ParseError, false, null(), 1.7412 + JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str); 1.7413 + return null(); 1.7414 + } 1.7415 + pn = legacyGeneratorExpr(pn); 1.7416 + if (!pn) 1.7417 + return null(); 1.7418 + handler.setBeginPosition(pn, begin); 1.7419 + if (tokenStream.getToken() != TOK_RP) { 1.7420 + report(ParseError, false, null(), 1.7421 + JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str); 1.7422 + return null(); 1.7423 + } 1.7424 + handler.setEndPosition(pn, pos().end); 1.7425 + handler.setInParens(pn); 1.7426 + return pn; 1.7427 + } 1.7428 +#endif /* JS_HAS_GENERATOR_EXPRS */ 1.7429 + 1.7430 + pn = handler.setInParens(pn); 1.7431 + 1.7432 + MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN); 1.7433 + 1.7434 + return pn; 1.7435 +} 1.7436 + 1.7437 +// Legacy generator comprehensions can sometimes appear without parentheses. 1.7438 +// For example: 1.7439 +// 1.7440 +// foo(x for (x in bar)) 1.7441 +// 1.7442 +// In this case the parens are part of the call, and not part of the generator 1.7443 +// comprehension. This can happen in these contexts: 1.7444 +// 1.7445 +// if (_) 1.7446 +// while (_) {} 1.7447 +// do {} while (_) 1.7448 +// switch (_) {} 1.7449 +// with (_) {} 1.7450 +// foo(_) // must be first and only argument 1.7451 +// 1.7452 +// This is not the case for ES6 generator comprehensions; they must always be in 1.7453 +// parentheses. 1.7454 + 1.7455 +template <typename ParseHandler> 1.7456 +typename ParseHandler::Node 1.7457 +Parser<ParseHandler>::exprInParens() 1.7458 +{ 1.7459 + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LP)); 1.7460 + uint32_t begin = pos().begin; 1.7461 + uint32_t startYieldOffset = pc->lastYieldOffset; 1.7462 + 1.7463 + /* 1.7464 + * Always accept the 'in' operator in a parenthesized expression, 1.7465 + * where it's unambiguous, even if we might be parsing the init of a 1.7466 + * for statement. 1.7467 + */ 1.7468 + bool oldParsingForInit = pc->parsingForInit; 1.7469 + pc->parsingForInit = false; 1.7470 + Node pn = expr(); 1.7471 + pc->parsingForInit = oldParsingForInit; 1.7472 + 1.7473 + if (!pn) 1.7474 + return null(); 1.7475 + 1.7476 +#if JS_HAS_GENERATOR_EXPRS 1.7477 + if (tokenStream.matchToken(TOK_FOR)) { 1.7478 + if (pc->lastYieldOffset != startYieldOffset) { 1.7479 + reportWithOffset(ParseError, false, pc->lastYieldOffset, 1.7480 + JSMSG_BAD_GENEXP_BODY, js_yield_str); 1.7481 + return null(); 1.7482 + } 1.7483 + if (handler.isOperationWithoutParens(pn, PNK_COMMA)) { 1.7484 + report(ParseError, false, null(), 1.7485 + JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str); 1.7486 + return null(); 1.7487 + } 1.7488 + pn = legacyGeneratorExpr(pn); 1.7489 + if (!pn) 1.7490 + return null(); 1.7491 + handler.setBeginPosition(pn, begin); 1.7492 + } 1.7493 +#endif /* JS_HAS_GENERATOR_EXPRS */ 1.7494 + 1.7495 + return pn; 1.7496 +} 1.7497 + 1.7498 +template class Parser<FullParseHandler>; 1.7499 +template class Parser<SyntaxParseHandler>; 1.7500 + 1.7501 +} /* namespace frontend */ 1.7502 +} /* namespace js */