js/src/frontend/Parser.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /*
michael@0 8 * JS parser.
michael@0 9 *
michael@0 10 * This is a recursive-descent parser for the JavaScript language specified by
michael@0 11 * "The JavaScript 1.5 Language Specification". It uses lexical and semantic
michael@0 12 * feedback to disambiguate non-LL(1) structures. It generates trees of nodes
michael@0 13 * induced by the recursive parsing (not precise syntax trees, see Parser.h).
michael@0 14 * After tree construction, it rewrites trees to fold constants and evaluate
michael@0 15 * compile-time expressions.
michael@0 16 *
michael@0 17 * This parser attempts no error recovery.
michael@0 18 */
michael@0 19
michael@0 20 #include "frontend/Parser-inl.h"
michael@0 21
michael@0 22 #include "jsapi.h"
michael@0 23 #include "jsatom.h"
michael@0 24 #include "jscntxt.h"
michael@0 25 #include "jsfun.h"
michael@0 26 #include "jsobj.h"
michael@0 27 #include "jsopcode.h"
michael@0 28 #include "jsscript.h"
michael@0 29 #include "jstypes.h"
michael@0 30
michael@0 31 #include "frontend/BytecodeCompiler.h"
michael@0 32 #include "frontend/FoldConstants.h"
michael@0 33 #include "frontend/ParseMaps.h"
michael@0 34 #include "frontend/TokenStream.h"
michael@0 35 #include "jit/AsmJS.h"
michael@0 36 #include "vm/Shape.h"
michael@0 37
michael@0 38 #include "jsatominlines.h"
michael@0 39 #include "jsscriptinlines.h"
michael@0 40
michael@0 41 #include "frontend/ParseNode-inl.h"
michael@0 42
michael@0 43 using namespace js;
michael@0 44 using namespace js::gc;
michael@0 45 using mozilla::Maybe;
michael@0 46
michael@0 47 namespace js {
michael@0 48 namespace frontend {
michael@0 49
michael@0 50 typedef Rooted<StaticBlockObject*> RootedStaticBlockObject;
michael@0 51 typedef Handle<StaticBlockObject*> HandleStaticBlockObject;
michael@0 52 typedef Rooted<NestedScopeObject*> RootedNestedScopeObject;
michael@0 53 typedef Handle<NestedScopeObject*> HandleNestedScopeObject;
michael@0 54
michael@0 55
michael@0 56 /*
michael@0 57 * Insist that the next token be of type tt, or report errno and return null.
michael@0 58 * NB: this macro uses cx and ts from its lexical environment.
michael@0 59 */
michael@0 60 #define MUST_MATCH_TOKEN(tt, errno) \
michael@0 61 JS_BEGIN_MACRO \
michael@0 62 if (tokenStream.getToken() != tt) { \
michael@0 63 report(ParseError, false, null(), errno); \
michael@0 64 return null(); \
michael@0 65 } \
michael@0 66 JS_END_MACRO
michael@0 67
michael@0 68 static const unsigned BlockIdLimit = 1 << ParseNode::NumBlockIdBits;
michael@0 69
michael@0 70 template <typename ParseHandler>
michael@0 71 bool
michael@0 72 GenerateBlockId(TokenStream &ts, ParseContext<ParseHandler> *pc, uint32_t &blockid)
michael@0 73 {
michael@0 74 if (pc->blockidGen == BlockIdLimit) {
michael@0 75 ts.reportError(JSMSG_NEED_DIET, "program");
michael@0 76 return false;
michael@0 77 }
michael@0 78 JS_ASSERT(pc->blockidGen < BlockIdLimit);
michael@0 79 blockid = pc->blockidGen++;
michael@0 80 return true;
michael@0 81 }
michael@0 82
michael@0 83 template bool
michael@0 84 GenerateBlockId(TokenStream &ts, ParseContext<SyntaxParseHandler> *pc, uint32_t &blockid);
michael@0 85
michael@0 86 template bool
michael@0 87 GenerateBlockId(TokenStream &ts, ParseContext<FullParseHandler> *pc, uint32_t &blockid);
michael@0 88
michael@0 89 template <typename ParseHandler>
michael@0 90 static void
michael@0 91 PushStatementPC(ParseContext<ParseHandler> *pc, StmtInfoPC *stmt, StmtType type)
michael@0 92 {
michael@0 93 stmt->blockid = pc->blockid();
michael@0 94 PushStatement(pc, stmt, type);
michael@0 95 }
michael@0 96
michael@0 97 // See comment on member function declaration.
michael@0 98 template <>
michael@0 99 bool
michael@0 100 ParseContext<FullParseHandler>::define(TokenStream &ts,
michael@0 101 HandlePropertyName name, ParseNode *pn, Definition::Kind kind)
michael@0 102 {
michael@0 103 JS_ASSERT(!pn->isUsed());
michael@0 104 JS_ASSERT_IF(pn->isDefn(), pn->isPlaceholder());
michael@0 105
michael@0 106 Definition *prevDef = nullptr;
michael@0 107 if (kind == Definition::LET)
michael@0 108 prevDef = decls_.lookupFirst(name);
michael@0 109 else
michael@0 110 JS_ASSERT(!decls_.lookupFirst(name));
michael@0 111
michael@0 112 if (!prevDef)
michael@0 113 prevDef = lexdeps.lookupDefn<FullParseHandler>(name);
michael@0 114
michael@0 115 if (prevDef) {
michael@0 116 ParseNode **pnup = &prevDef->dn_uses;
michael@0 117 ParseNode *pnu;
michael@0 118 unsigned start = (kind == Definition::LET) ? pn->pn_blockid : bodyid;
michael@0 119
michael@0 120 while ((pnu = *pnup) != nullptr && pnu->pn_blockid >= start) {
michael@0 121 JS_ASSERT(pnu->pn_blockid >= bodyid);
michael@0 122 JS_ASSERT(pnu->isUsed());
michael@0 123 pnu->pn_lexdef = (Definition *) pn;
michael@0 124 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
michael@0 125 pnup = &pnu->pn_link;
michael@0 126 }
michael@0 127
michael@0 128 if (!pnu || pnu != prevDef->dn_uses) {
michael@0 129 *pnup = pn->dn_uses;
michael@0 130 pn->dn_uses = prevDef->dn_uses;
michael@0 131 prevDef->dn_uses = pnu;
michael@0 132
michael@0 133 if (!pnu && prevDef->isPlaceholder())
michael@0 134 lexdeps->remove(name);
michael@0 135 }
michael@0 136
michael@0 137 pn->pn_dflags |= prevDef->pn_dflags & PND_CLOSED;
michael@0 138 }
michael@0 139
michael@0 140 JS_ASSERT_IF(kind != Definition::LET, !lexdeps->lookup(name));
michael@0 141 pn->setDefn(true);
michael@0 142 pn->pn_dflags &= ~PND_PLACEHOLDER;
michael@0 143 if (kind == Definition::CONST)
michael@0 144 pn->pn_dflags |= PND_CONST;
michael@0 145
michael@0 146 Definition *dn = (Definition *)pn;
michael@0 147 switch (kind) {
michael@0 148 case Definition::ARG:
michael@0 149 JS_ASSERT(sc->isFunctionBox());
michael@0 150 dn->setOp(JSOP_GETARG);
michael@0 151 dn->pn_dflags |= PND_BOUND;
michael@0 152 if (!dn->pn_cookie.set(ts, staticLevel, args_.length()))
michael@0 153 return false;
michael@0 154 if (!args_.append(dn))
michael@0 155 return false;
michael@0 156 if (args_.length() >= ARGNO_LIMIT) {
michael@0 157 ts.reportError(JSMSG_TOO_MANY_FUN_ARGS);
michael@0 158 return false;
michael@0 159 }
michael@0 160 if (name == ts.names().empty)
michael@0 161 break;
michael@0 162 if (!decls_.addUnique(name, dn))
michael@0 163 return false;
michael@0 164 break;
michael@0 165
michael@0 166 case Definition::CONST:
michael@0 167 case Definition::VAR:
michael@0 168 if (sc->isFunctionBox()) {
michael@0 169 dn->setOp(JSOP_GETLOCAL);
michael@0 170 dn->pn_dflags |= PND_BOUND;
michael@0 171 if (!dn->pn_cookie.set(ts, staticLevel, vars_.length()))
michael@0 172 return false;
michael@0 173 if (!vars_.append(dn))
michael@0 174 return false;
michael@0 175 if (vars_.length() >= LOCALNO_LIMIT) {
michael@0 176 ts.reportError(JSMSG_TOO_MANY_LOCALS);
michael@0 177 return false;
michael@0 178 }
michael@0 179 }
michael@0 180 if (!decls_.addUnique(name, dn))
michael@0 181 return false;
michael@0 182 break;
michael@0 183
michael@0 184 case Definition::LET:
michael@0 185 dn->setOp(JSOP_GETLOCAL);
michael@0 186 dn->pn_dflags |= (PND_LET | PND_BOUND);
michael@0 187 JS_ASSERT(dn->pn_cookie.level() == staticLevel); /* see bindLet */
michael@0 188 if (!decls_.addShadow(name, dn))
michael@0 189 return false;
michael@0 190 break;
michael@0 191
michael@0 192 default:
michael@0 193 MOZ_ASSUME_UNREACHABLE("unexpected kind");
michael@0 194 }
michael@0 195
michael@0 196 return true;
michael@0 197 }
michael@0 198
michael@0 199 template <>
michael@0 200 bool
michael@0 201 ParseContext<SyntaxParseHandler>::define(TokenStream &ts, HandlePropertyName name, Node pn,
michael@0 202 Definition::Kind kind)
michael@0 203 {
michael@0 204 JS_ASSERT(!decls_.lookupFirst(name));
michael@0 205
michael@0 206 if (lexdeps.lookupDefn<SyntaxParseHandler>(name))
michael@0 207 lexdeps->remove(name);
michael@0 208
michael@0 209 // Keep track of the number of arguments in args_, for fun->nargs.
michael@0 210 if (kind == Definition::ARG) {
michael@0 211 if (!args_.append((Definition *) nullptr))
michael@0 212 return false;
michael@0 213 if (args_.length() >= ARGNO_LIMIT) {
michael@0 214 ts.reportError(JSMSG_TOO_MANY_FUN_ARGS);
michael@0 215 return false;
michael@0 216 }
michael@0 217 }
michael@0 218
michael@0 219 return decls_.addUnique(name, kind);
michael@0 220 }
michael@0 221
michael@0 222 template <typename ParseHandler>
michael@0 223 void
michael@0 224 ParseContext<ParseHandler>::prepareToAddDuplicateArg(HandlePropertyName name, DefinitionNode prevDecl)
michael@0 225 {
michael@0 226 JS_ASSERT(decls_.lookupFirst(name) == prevDecl);
michael@0 227 decls_.remove(name);
michael@0 228 }
michael@0 229
michael@0 230 template <typename ParseHandler>
michael@0 231 void
michael@0 232 ParseContext<ParseHandler>::updateDecl(JSAtom *atom, Node pn)
michael@0 233 {
michael@0 234 Definition *oldDecl = decls_.lookupFirst(atom);
michael@0 235
michael@0 236 pn->setDefn(true);
michael@0 237 Definition *newDecl = (Definition *)pn;
michael@0 238 decls_.updateFirst(atom, newDecl);
michael@0 239
michael@0 240 if (!sc->isFunctionBox()) {
michael@0 241 JS_ASSERT(newDecl->isFreeVar());
michael@0 242 return;
michael@0 243 }
michael@0 244
michael@0 245 JS_ASSERT(oldDecl->isBound());
michael@0 246 JS_ASSERT(!oldDecl->pn_cookie.isFree());
michael@0 247 newDecl->pn_cookie = oldDecl->pn_cookie;
michael@0 248 newDecl->pn_dflags |= PND_BOUND;
michael@0 249 if (IsArgOp(oldDecl->getOp())) {
michael@0 250 newDecl->setOp(JSOP_GETARG);
michael@0 251 JS_ASSERT(args_[oldDecl->pn_cookie.slot()] == oldDecl);
michael@0 252 args_[oldDecl->pn_cookie.slot()] = newDecl;
michael@0 253 } else {
michael@0 254 JS_ASSERT(IsLocalOp(oldDecl->getOp()));
michael@0 255 newDecl->setOp(JSOP_GETLOCAL);
michael@0 256 JS_ASSERT(vars_[oldDecl->pn_cookie.slot()] == oldDecl);
michael@0 257 vars_[oldDecl->pn_cookie.slot()] = newDecl;
michael@0 258 }
michael@0 259 }
michael@0 260
michael@0 261 template <typename ParseHandler>
michael@0 262 void
michael@0 263 ParseContext<ParseHandler>::popLetDecl(JSAtom *atom)
michael@0 264 {
michael@0 265 JS_ASSERT(ParseHandler::getDefinitionKind(decls_.lookupFirst(atom)) == Definition::LET);
michael@0 266 decls_.remove(atom);
michael@0 267 }
michael@0 268
michael@0 269 template <typename ParseHandler>
michael@0 270 static void
michael@0 271 AppendPackedBindings(const ParseContext<ParseHandler> *pc, const DeclVector &vec, Binding *dst)
michael@0 272 {
michael@0 273 for (size_t i = 0; i < vec.length(); ++i, ++dst) {
michael@0 274 Definition *dn = vec[i];
michael@0 275 PropertyName *name = dn->name();
michael@0 276
michael@0 277 Binding::Kind kind;
michael@0 278 switch (dn->kind()) {
michael@0 279 case Definition::VAR:
michael@0 280 kind = Binding::VARIABLE;
michael@0 281 break;
michael@0 282 case Definition::CONST:
michael@0 283 kind = Binding::CONSTANT;
michael@0 284 break;
michael@0 285 case Definition::ARG:
michael@0 286 kind = Binding::ARGUMENT;
michael@0 287 break;
michael@0 288 default:
michael@0 289 MOZ_ASSUME_UNREACHABLE("unexpected dn->kind");
michael@0 290 }
michael@0 291
michael@0 292 /*
michael@0 293 * Bindings::init does not check for duplicates so we must ensure that
michael@0 294 * only one binding with a given name is marked aliased. pc->decls
michael@0 295 * maintains the canonical definition for each name, so use that.
michael@0 296 */
michael@0 297 JS_ASSERT_IF(dn->isClosed(), pc->decls().lookupFirst(name) == dn);
michael@0 298 bool aliased = dn->isClosed() ||
michael@0 299 (pc->sc->allLocalsAliased() &&
michael@0 300 pc->decls().lookupFirst(name) == dn);
michael@0 301
michael@0 302 *dst = Binding(name, kind, aliased);
michael@0 303 }
michael@0 304 }
michael@0 305
michael@0 306 template <typename ParseHandler>
michael@0 307 bool
michael@0 308 ParseContext<ParseHandler>::generateFunctionBindings(ExclusiveContext *cx, TokenStream &ts,
michael@0 309 LifoAlloc &alloc,
michael@0 310 InternalHandle<Bindings*> bindings) const
michael@0 311 {
michael@0 312 JS_ASSERT(sc->isFunctionBox());
michael@0 313 JS_ASSERT(args_.length() < ARGNO_LIMIT);
michael@0 314 JS_ASSERT(vars_.length() < LOCALNO_LIMIT);
michael@0 315
michael@0 316 /*
michael@0 317 * Avoid pathological edge cases by explicitly limiting the total number of
michael@0 318 * bindings to what will fit in a uint32_t.
michael@0 319 */
michael@0 320 if (UINT32_MAX - args_.length() <= vars_.length())
michael@0 321 return ts.reportError(JSMSG_TOO_MANY_LOCALS);
michael@0 322
michael@0 323 uint32_t count = args_.length() + vars_.length();
michael@0 324 Binding *packedBindings = alloc.newArrayUninitialized<Binding>(count);
michael@0 325 if (!packedBindings) {
michael@0 326 js_ReportOutOfMemory(cx);
michael@0 327 return false;
michael@0 328 }
michael@0 329
michael@0 330 AppendPackedBindings(this, args_, packedBindings);
michael@0 331 AppendPackedBindings(this, vars_, packedBindings + args_.length());
michael@0 332
michael@0 333 return Bindings::initWithTemporaryStorage(cx, bindings, args_.length(), vars_.length(),
michael@0 334 packedBindings, blockScopeDepth);
michael@0 335 }
michael@0 336
michael@0 337 template <typename ParseHandler>
michael@0 338 bool
michael@0 339 Parser<ParseHandler>::reportHelper(ParseReportKind kind, bool strict, uint32_t offset,
michael@0 340 unsigned errorNumber, va_list args)
michael@0 341 {
michael@0 342 bool result = false;
michael@0 343 switch (kind) {
michael@0 344 case ParseError:
michael@0 345 result = tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_ERROR, errorNumber, args);
michael@0 346 break;
michael@0 347 case ParseWarning:
michael@0 348 result =
michael@0 349 tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_WARNING, errorNumber, args);
michael@0 350 break;
michael@0 351 case ParseExtraWarning:
michael@0 352 result = tokenStream.reportStrictWarningErrorNumberVA(offset, errorNumber, args);
michael@0 353 break;
michael@0 354 case ParseStrictError:
michael@0 355 result = tokenStream.reportStrictModeErrorNumberVA(offset, strict, errorNumber, args);
michael@0 356 break;
michael@0 357 }
michael@0 358 return result;
michael@0 359 }
michael@0 360
michael@0 361 template <typename ParseHandler>
michael@0 362 bool
michael@0 363 Parser<ParseHandler>::report(ParseReportKind kind, bool strict, Node pn, unsigned errorNumber, ...)
michael@0 364 {
michael@0 365 uint32_t offset = (pn ? handler.getPosition(pn) : pos()).begin;
michael@0 366
michael@0 367 va_list args;
michael@0 368 va_start(args, errorNumber);
michael@0 369 bool result = reportHelper(kind, strict, offset, errorNumber, args);
michael@0 370 va_end(args);
michael@0 371 return result;
michael@0 372 }
michael@0 373
michael@0 374 template <typename ParseHandler>
michael@0 375 bool
michael@0 376 Parser<ParseHandler>::reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...)
michael@0 377 {
michael@0 378 va_list args;
michael@0 379 va_start(args, errorNumber);
michael@0 380 bool result = reportHelper(kind, strict, TokenStream::NoOffset, errorNumber, args);
michael@0 381 va_end(args);
michael@0 382 return result;
michael@0 383 }
michael@0 384
michael@0 385 template <typename ParseHandler>
michael@0 386 bool
michael@0 387 Parser<ParseHandler>::reportWithOffset(ParseReportKind kind, bool strict, uint32_t offset,
michael@0 388 unsigned errorNumber, ...)
michael@0 389 {
michael@0 390 va_list args;
michael@0 391 va_start(args, errorNumber);
michael@0 392 bool result = reportHelper(kind, strict, offset, errorNumber, args);
michael@0 393 va_end(args);
michael@0 394 return result;
michael@0 395 }
michael@0 396
michael@0 397 template <>
michael@0 398 bool
michael@0 399 Parser<FullParseHandler>::abortIfSyntaxParser()
michael@0 400 {
michael@0 401 handler.disableSyntaxParser();
michael@0 402 return true;
michael@0 403 }
michael@0 404
michael@0 405 template <>
michael@0 406 bool
michael@0 407 Parser<SyntaxParseHandler>::abortIfSyntaxParser()
michael@0 408 {
michael@0 409 abortedSyntaxParse = true;
michael@0 410 return false;
michael@0 411 }
michael@0 412
michael@0 413 template <typename ParseHandler>
michael@0 414 Parser<ParseHandler>::Parser(ExclusiveContext *cx, LifoAlloc *alloc,
michael@0 415 const ReadOnlyCompileOptions &options,
michael@0 416 const jschar *chars, size_t length, bool foldConstants,
michael@0 417 Parser<SyntaxParseHandler> *syntaxParser,
michael@0 418 LazyScript *lazyOuterFunction)
michael@0 419 : AutoGCRooter(cx, PARSER),
michael@0 420 context(cx),
michael@0 421 alloc(*alloc),
michael@0 422 tokenStream(cx, options, chars, length, thisForCtor()),
michael@0 423 traceListHead(nullptr),
michael@0 424 pc(nullptr),
michael@0 425 sct(nullptr),
michael@0 426 ss(nullptr),
michael@0 427 keepAtoms(cx->perThreadData),
michael@0 428 foldConstants(foldConstants),
michael@0 429 abortedSyntaxParse(false),
michael@0 430 isUnexpectedEOF_(false),
michael@0 431 handler(cx, *alloc, tokenStream, foldConstants, syntaxParser, lazyOuterFunction)
michael@0 432 {
michael@0 433 {
michael@0 434 AutoLockForExclusiveAccess lock(cx);
michael@0 435 cx->perThreadData->addActiveCompilation();
michael@0 436 }
michael@0 437
michael@0 438 // The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings
michael@0 439 // which are not generated if functions are parsed lazily. Note that the
michael@0 440 // standard "use strict" does not inhibit lazy parsing.
michael@0 441 if (options.extraWarningsOption)
michael@0 442 handler.disableSyntaxParser();
michael@0 443
michael@0 444 tempPoolMark = alloc->mark();
michael@0 445 }
michael@0 446
michael@0 447 template <typename ParseHandler>
michael@0 448 Parser<ParseHandler>::~Parser()
michael@0 449 {
michael@0 450 alloc.release(tempPoolMark);
michael@0 451
michael@0 452 /*
michael@0 453 * The parser can allocate enormous amounts of memory for large functions.
michael@0 454 * Eagerly free the memory now (which otherwise won't be freed until the
michael@0 455 * next GC) to avoid unnecessary OOMs.
michael@0 456 */
michael@0 457 alloc.freeAllIfHugeAndUnused();
michael@0 458
michael@0 459 {
michael@0 460 AutoLockForExclusiveAccess lock(context);
michael@0 461 context->perThreadData->removeActiveCompilation();
michael@0 462 }
michael@0 463 }
michael@0 464
michael@0 465 template <typename ParseHandler>
michael@0 466 ObjectBox *
michael@0 467 Parser<ParseHandler>::newObjectBox(JSObject *obj)
michael@0 468 {
michael@0 469 JS_ASSERT(obj && !IsPoisonedPtr(obj));
michael@0 470
michael@0 471 /*
michael@0 472 * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
michael@0 473 * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
michael@0 474 * arenas containing the entries must be alive until we are done with
michael@0 475 * scanning, parsing and code generation for the whole script or top-level
michael@0 476 * function.
michael@0 477 */
michael@0 478
michael@0 479 ObjectBox *objbox = alloc.new_<ObjectBox>(obj, traceListHead);
michael@0 480 if (!objbox) {
michael@0 481 js_ReportOutOfMemory(context);
michael@0 482 return nullptr;
michael@0 483 }
michael@0 484
michael@0 485 traceListHead = objbox;
michael@0 486
michael@0 487 return objbox;
michael@0 488 }
michael@0 489
michael@0 490 template <typename ParseHandler>
michael@0 491 FunctionBox::FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunction *fun,
michael@0 492 ParseContext<ParseHandler> *outerpc, Directives directives,
michael@0 493 bool extraWarnings, GeneratorKind generatorKind)
michael@0 494 : ObjectBox(fun, traceListHead),
michael@0 495 SharedContext(cx, directives, extraWarnings),
michael@0 496 bindings(),
michael@0 497 bufStart(0),
michael@0 498 bufEnd(0),
michael@0 499 length(0),
michael@0 500 generatorKindBits_(GeneratorKindAsBits(generatorKind)),
michael@0 501 inWith(false), // initialized below
michael@0 502 inGenexpLambda(false),
michael@0 503 hasDestructuringArgs(false),
michael@0 504 useAsm(directives.asmJS()),
michael@0 505 insideUseAsm(outerpc && outerpc->useAsmOrInsideUseAsm()),
michael@0 506 usesArguments(false),
michael@0 507 usesApply(false),
michael@0 508 funCxFlags()
michael@0 509 {
michael@0 510 // Functions created at parse time may be set singleton after parsing and
michael@0 511 // baked into JIT code, so they must be allocated tenured. They are held by
michael@0 512 // the JSScript so cannot be collected during a minor GC anyway.
michael@0 513 JS_ASSERT(fun->isTenured());
michael@0 514
michael@0 515 if (!outerpc) {
michael@0 516 inWith = false;
michael@0 517
michael@0 518 } else if (outerpc->parsingWith) {
michael@0 519 // This covers cases that don't involve eval(). For example:
michael@0 520 //
michael@0 521 // with (o) { (function() { g(); })(); }
michael@0 522 //
michael@0 523 // In this case, |outerpc| corresponds to global code, and
michael@0 524 // outerpc->parsingWith is true.
michael@0 525 inWith = true;
michael@0 526
michael@0 527 } else if (outerpc->sc->isGlobalSharedContext()) {
michael@0 528 // This covers the case where a function is nested within an eval()
michael@0 529 // within a |with| statement.
michael@0 530 //
michael@0 531 // with (o) { eval("(function() { g(); })();"); }
michael@0 532 //
michael@0 533 // In this case, |outerpc| corresponds to the eval(),
michael@0 534 // outerpc->parsingWith is false because the eval() breaks the
michael@0 535 // ParseContext chain, and |parent| is nullptr (again because of the
michael@0 536 // eval(), so we have to look at |outerpc|'s scopeChain.
michael@0 537 //
michael@0 538 JSObject *scope = outerpc->sc->asGlobalSharedContext()->scopeChain();
michael@0 539 while (scope) {
michael@0 540 if (scope->is<DynamicWithObject>())
michael@0 541 inWith = true;
michael@0 542 scope = scope->enclosingScope();
michael@0 543 }
michael@0 544 } else if (outerpc->sc->isFunctionBox()) {
michael@0 545 // This is like the above case, but for more deeply nested functions.
michael@0 546 // For example:
michael@0 547 //
michael@0 548 // with (o) { eval("(function() { (function() { g(); })(); })();"); } }
michael@0 549 //
michael@0 550 // In this case, the inner anonymous function needs to inherit the
michael@0 551 // setting of |inWith| from the outer one.
michael@0 552 FunctionBox *parent = outerpc->sc->asFunctionBox();
michael@0 553 if (parent && parent->inWith)
michael@0 554 inWith = true;
michael@0 555 }
michael@0 556 }
michael@0 557
michael@0 558 template <typename ParseHandler>
michael@0 559 FunctionBox *
michael@0 560 Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction *fun, ParseContext<ParseHandler> *outerpc,
michael@0 561 Directives inheritedDirectives, GeneratorKind generatorKind)
michael@0 562 {
michael@0 563 JS_ASSERT(fun && !IsPoisonedPtr(fun));
michael@0 564
michael@0 565 /*
michael@0 566 * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
michael@0 567 * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
michael@0 568 * arenas containing the entries must be alive until we are done with
michael@0 569 * scanning, parsing and code generation for the whole script or top-level
michael@0 570 * function.
michael@0 571 */
michael@0 572 FunctionBox *funbox =
michael@0 573 alloc.new_<FunctionBox>(context, traceListHead, fun, outerpc,
michael@0 574 inheritedDirectives, options().extraWarningsOption,
michael@0 575 generatorKind);
michael@0 576 if (!funbox) {
michael@0 577 js_ReportOutOfMemory(context);
michael@0 578 return nullptr;
michael@0 579 }
michael@0 580
michael@0 581 traceListHead = funbox;
michael@0 582 if (fn)
michael@0 583 handler.setFunctionBox(fn, funbox);
michael@0 584
michael@0 585 return funbox;
michael@0 586 }
michael@0 587
michael@0 588 template <typename ParseHandler>
michael@0 589 void
michael@0 590 Parser<ParseHandler>::trace(JSTracer *trc)
michael@0 591 {
michael@0 592 traceListHead->trace(trc);
michael@0 593 }
michael@0 594
michael@0 595 void
michael@0 596 MarkParser(JSTracer *trc, AutoGCRooter *parser)
michael@0 597 {
michael@0 598 static_cast<Parser<FullParseHandler> *>(parser)->trace(trc);
michael@0 599 }
michael@0 600
michael@0 601 /*
michael@0 602 * Parse a top-level JS script.
michael@0 603 */
michael@0 604 template <typename ParseHandler>
michael@0 605 typename ParseHandler::Node
michael@0 606 Parser<ParseHandler>::parse(JSObject *chain)
michael@0 607 {
michael@0 608 /*
michael@0 609 * Protect atoms from being collected by a GC activation, which might
michael@0 610 * - nest on this thread due to out of memory (the so-called "last ditch"
michael@0 611 * GC attempted within js_NewGCThing), or
michael@0 612 * - run for any reason on another thread if this thread is suspended on
michael@0 613 * an object lock before it finishes generating bytecode into a script
michael@0 614 * protected from the GC by a root or a stack frame reference.
michael@0 615 */
michael@0 616 Directives directives(options().strictOption);
michael@0 617 GlobalSharedContext globalsc(context, chain, directives, options().extraWarningsOption);
michael@0 618 ParseContext<ParseHandler> globalpc(this, /* parent = */ nullptr, ParseHandler::null(),
michael@0 619 &globalsc, /* newDirectives = */ nullptr,
michael@0 620 /* staticLevel = */ 0, /* bodyid = */ 0,
michael@0 621 /* blockScopeDepth = */ 0);
michael@0 622 if (!globalpc.init(tokenStream))
michael@0 623 return null();
michael@0 624
michael@0 625 Node pn = statements();
michael@0 626 if (pn) {
michael@0 627 if (!tokenStream.matchToken(TOK_EOF)) {
michael@0 628 report(ParseError, false, null(), JSMSG_SYNTAX_ERROR);
michael@0 629 return null();
michael@0 630 }
michael@0 631 if (foldConstants) {
michael@0 632 if (!FoldConstants(context, &pn, this))
michael@0 633 return null();
michael@0 634 }
michael@0 635 }
michael@0 636 return pn;
michael@0 637 }
michael@0 638
michael@0 639 /*
michael@0 640 * Insist on a final return before control flows out of pn. Try to be a bit
michael@0 641 * smart about loops: do {...; return e2;} while(0) at the end of a function
michael@0 642 * that contains an early return e1 will get a strict warning. Similarly for
michael@0 643 * iloops: while (true){...} is treated as though ... returns.
michael@0 644 */
michael@0 645 enum {
michael@0 646 ENDS_IN_OTHER = 0,
michael@0 647 ENDS_IN_RETURN = 1,
michael@0 648 ENDS_IN_BREAK = 2
michael@0 649 };
michael@0 650
michael@0 651 static int
michael@0 652 HasFinalReturn(ParseNode *pn)
michael@0 653 {
michael@0 654 ParseNode *pn2, *pn3;
michael@0 655 unsigned rv, rv2, hasDefault;
michael@0 656
michael@0 657 switch (pn->getKind()) {
michael@0 658 case PNK_STATEMENTLIST:
michael@0 659 if (!pn->pn_head)
michael@0 660 return ENDS_IN_OTHER;
michael@0 661 return HasFinalReturn(pn->last());
michael@0 662
michael@0 663 case PNK_IF:
michael@0 664 if (!pn->pn_kid3)
michael@0 665 return ENDS_IN_OTHER;
michael@0 666 return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3);
michael@0 667
michael@0 668 case PNK_WHILE:
michael@0 669 pn2 = pn->pn_left;
michael@0 670 if (pn2->isKind(PNK_TRUE))
michael@0 671 return ENDS_IN_RETURN;
michael@0 672 if (pn2->isKind(PNK_NUMBER) && pn2->pn_dval)
michael@0 673 return ENDS_IN_RETURN;
michael@0 674 return ENDS_IN_OTHER;
michael@0 675
michael@0 676 case PNK_DOWHILE:
michael@0 677 pn2 = pn->pn_right;
michael@0 678 if (pn2->isKind(PNK_FALSE))
michael@0 679 return HasFinalReturn(pn->pn_left);
michael@0 680 if (pn2->isKind(PNK_TRUE))
michael@0 681 return ENDS_IN_RETURN;
michael@0 682 if (pn2->isKind(PNK_NUMBER)) {
michael@0 683 if (pn2->pn_dval == 0)
michael@0 684 return HasFinalReturn(pn->pn_left);
michael@0 685 return ENDS_IN_RETURN;
michael@0 686 }
michael@0 687 return ENDS_IN_OTHER;
michael@0 688
michael@0 689 case PNK_FOR:
michael@0 690 pn2 = pn->pn_left;
michael@0 691 if (pn2->isArity(PN_TERNARY) && !pn2->pn_kid2)
michael@0 692 return ENDS_IN_RETURN;
michael@0 693 return ENDS_IN_OTHER;
michael@0 694
michael@0 695 case PNK_SWITCH:
michael@0 696 rv = ENDS_IN_RETURN;
michael@0 697 hasDefault = ENDS_IN_OTHER;
michael@0 698 pn2 = pn->pn_right;
michael@0 699 if (pn2->isKind(PNK_LEXICALSCOPE))
michael@0 700 pn2 = pn2->expr();
michael@0 701 for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
michael@0 702 if (pn2->isKind(PNK_DEFAULT))
michael@0 703 hasDefault = ENDS_IN_RETURN;
michael@0 704 pn3 = pn2->pn_right;
michael@0 705 JS_ASSERT(pn3->isKind(PNK_STATEMENTLIST));
michael@0 706 if (pn3->pn_head) {
michael@0 707 rv2 = HasFinalReturn(pn3->last());
michael@0 708 if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
michael@0 709 /* Falling through to next case or default. */;
michael@0 710 else
michael@0 711 rv &= rv2;
michael@0 712 }
michael@0 713 }
michael@0 714 /* If a final switch has no default case, we judge it harshly. */
michael@0 715 rv &= hasDefault;
michael@0 716 return rv;
michael@0 717
michael@0 718 case PNK_BREAK:
michael@0 719 return ENDS_IN_BREAK;
michael@0 720
michael@0 721 case PNK_WITH:
michael@0 722 return HasFinalReturn(pn->pn_right);
michael@0 723
michael@0 724 case PNK_RETURN:
michael@0 725 return ENDS_IN_RETURN;
michael@0 726
michael@0 727 case PNK_COLON:
michael@0 728 case PNK_LEXICALSCOPE:
michael@0 729 return HasFinalReturn(pn->expr());
michael@0 730
michael@0 731 case PNK_THROW:
michael@0 732 return ENDS_IN_RETURN;
michael@0 733
michael@0 734 case PNK_TRY:
michael@0 735 /* If we have a finally block that returns, we are done. */
michael@0 736 if (pn->pn_kid3) {
michael@0 737 rv = HasFinalReturn(pn->pn_kid3);
michael@0 738 if (rv == ENDS_IN_RETURN)
michael@0 739 return rv;
michael@0 740 }
michael@0 741
michael@0 742 /* Else check the try block and any and all catch statements. */
michael@0 743 rv = HasFinalReturn(pn->pn_kid1);
michael@0 744 if (pn->pn_kid2) {
michael@0 745 JS_ASSERT(pn->pn_kid2->isArity(PN_LIST));
michael@0 746 for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next)
michael@0 747 rv &= HasFinalReturn(pn2);
michael@0 748 }
michael@0 749 return rv;
michael@0 750
michael@0 751 case PNK_CATCH:
michael@0 752 /* Check this catch block's body. */
michael@0 753 return HasFinalReturn(pn->pn_kid3);
michael@0 754
michael@0 755 case PNK_LET:
michael@0 756 /* Non-binary let statements are let declarations. */
michael@0 757 if (!pn->isArity(PN_BINARY))
michael@0 758 return ENDS_IN_OTHER;
michael@0 759 return HasFinalReturn(pn->pn_right);
michael@0 760
michael@0 761 default:
michael@0 762 return ENDS_IN_OTHER;
michael@0 763 }
michael@0 764 }
michael@0 765
michael@0 766 static int
michael@0 767 HasFinalReturn(SyntaxParseHandler::Node pn)
michael@0 768 {
michael@0 769 return ENDS_IN_RETURN;
michael@0 770 }
michael@0 771
michael@0 772 template <typename ParseHandler>
michael@0 773 bool
michael@0 774 Parser<ParseHandler>::reportBadReturn(Node pn, ParseReportKind kind,
michael@0 775 unsigned errnum, unsigned anonerrnum)
michael@0 776 {
michael@0 777 JSAutoByteString name;
michael@0 778 JSAtom *atom = pc->sc->asFunctionBox()->function()->atom();
michael@0 779 if (atom) {
michael@0 780 if (!AtomToPrintableString(context, atom, &name))
michael@0 781 return false;
michael@0 782 } else {
michael@0 783 errnum = anonerrnum;
michael@0 784 }
michael@0 785 return report(kind, pc->sc->strict, pn, errnum, name.ptr());
michael@0 786 }
michael@0 787
michael@0 788 template <typename ParseHandler>
michael@0 789 bool
michael@0 790 Parser<ParseHandler>::checkFinalReturn(Node pn)
michael@0 791 {
michael@0 792 JS_ASSERT(pc->sc->isFunctionBox());
michael@0 793 return HasFinalReturn(pn) == ENDS_IN_RETURN ||
michael@0 794 reportBadReturn(pn, ParseExtraWarning,
michael@0 795 JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
michael@0 796 }
michael@0 797
michael@0 798 /*
michael@0 799 * Check that assigning to lhs is permitted. Assigning to 'eval' or
michael@0 800 * 'arguments' is banned in strict mode and in destructuring assignment.
michael@0 801 */
michael@0 802 template <typename ParseHandler>
michael@0 803 bool
michael@0 804 Parser<ParseHandler>::checkStrictAssignment(Node lhs, AssignmentFlavor flavor)
michael@0 805 {
michael@0 806 if (!pc->sc->needStrictChecks() && flavor != KeyedDestructuringAssignment)
michael@0 807 return true;
michael@0 808
michael@0 809 JSAtom *atom = handler.isName(lhs);
michael@0 810 if (!atom)
michael@0 811 return true;
michael@0 812
michael@0 813 if (atom == context->names().eval || atom == context->names().arguments) {
michael@0 814 JSAutoByteString name;
michael@0 815 if (!AtomToPrintableString(context, atom, &name))
michael@0 816 return false;
michael@0 817
michael@0 818 ParseReportKind kind;
michael@0 819 unsigned errnum;
michael@0 820 if (pc->sc->strict || flavor != KeyedDestructuringAssignment) {
michael@0 821 kind = ParseStrictError;
michael@0 822 errnum = JSMSG_BAD_STRICT_ASSIGN;
michael@0 823 } else {
michael@0 824 kind = ParseError;
michael@0 825 errnum = JSMSG_BAD_DESTRUCT_ASSIGN;
michael@0 826 }
michael@0 827 if (!report(kind, pc->sc->strict, lhs, errnum, name.ptr()))
michael@0 828 return false;
michael@0 829 }
michael@0 830 return true;
michael@0 831 }
michael@0 832
michael@0 833 /*
michael@0 834 * Check that it is permitted to introduce a binding for atom. Strict mode
michael@0 835 * forbids introducing new definitions for 'eval', 'arguments', or for any
michael@0 836 * strict mode reserved keyword. Use pn for reporting error locations, or use
michael@0 837 * pc's token stream if pn is nullptr.
michael@0 838 */
michael@0 839 template <typename ParseHandler>
michael@0 840 bool
michael@0 841 Parser<ParseHandler>::checkStrictBinding(PropertyName *name, Node pn)
michael@0 842 {
michael@0 843 if (!pc->sc->needStrictChecks())
michael@0 844 return true;
michael@0 845
michael@0 846 if (name == context->names().eval || name == context->names().arguments || IsKeyword(name)) {
michael@0 847 JSAutoByteString bytes;
michael@0 848 if (!AtomToPrintableString(context, name, &bytes))
michael@0 849 return false;
michael@0 850 return report(ParseStrictError, pc->sc->strict, pn,
michael@0 851 JSMSG_BAD_BINDING, bytes.ptr());
michael@0 852 }
michael@0 853
michael@0 854 return true;
michael@0 855 }
michael@0 856
michael@0 857 template <>
michael@0 858 ParseNode *
michael@0 859 Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals,
michael@0 860 GeneratorKind generatorKind,
michael@0 861 Directives inheritedDirectives,
michael@0 862 Directives *newDirectives)
michael@0 863 {
michael@0 864 Node fn = handler.newFunctionDefinition();
michael@0 865 if (!fn)
michael@0 866 return null();
michael@0 867
michael@0 868 ParseNode *argsbody = ListNode::create(PNK_ARGSBODY, &handler);
michael@0 869 if (!argsbody)
michael@0 870 return null();
michael@0 871 argsbody->setOp(JSOP_NOP);
michael@0 872 argsbody->makeEmpty();
michael@0 873 fn->pn_body = argsbody;
michael@0 874
michael@0 875 FunctionBox *funbox = newFunctionBox(fn, fun, /* outerpc = */ nullptr, inheritedDirectives,
michael@0 876 generatorKind);
michael@0 877 if (!funbox)
michael@0 878 return null();
michael@0 879 funbox->length = fun->nargs() - fun->hasRest();
michael@0 880 handler.setFunctionBox(fn, funbox);
michael@0 881
michael@0 882 ParseContext<FullParseHandler> funpc(this, pc, fn, funbox, newDirectives,
michael@0 883 /* staticLevel = */ 0, /* bodyid = */ 0,
michael@0 884 /* blockScopeDepth = */ 0);
michael@0 885 if (!funpc.init(tokenStream))
michael@0 886 return null();
michael@0 887
michael@0 888 for (unsigned i = 0; i < formals.length(); i++) {
michael@0 889 if (!defineArg(fn, formals[i]))
michael@0 890 return null();
michael@0 891 }
michael@0 892
michael@0 893 ParseNode *pn = functionBody(Statement, StatementListBody);
michael@0 894 if (!pn)
michael@0 895 return null();
michael@0 896
michael@0 897 if (!tokenStream.matchToken(TOK_EOF)) {
michael@0 898 report(ParseError, false, null(), JSMSG_SYNTAX_ERROR);
michael@0 899 return null();
michael@0 900 }
michael@0 901
michael@0 902 if (!FoldConstants(context, &pn, this))
michael@0 903 return null();
michael@0 904
michael@0 905 InternalHandle<Bindings*> funboxBindings =
michael@0 906 InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
michael@0 907 if (!funpc.generateFunctionBindings(context, tokenStream, alloc, funboxBindings))
michael@0 908 return null();
michael@0 909
michael@0 910 JS_ASSERT(fn->pn_body->isKind(PNK_ARGSBODY));
michael@0 911 fn->pn_body->append(pn);
michael@0 912 fn->pn_body->pn_pos = pn->pn_pos;
michael@0 913 return fn;
michael@0 914 }
michael@0 915
michael@0 916 template <>
michael@0 917 bool
michael@0 918 Parser<FullParseHandler>::checkFunctionArguments()
michael@0 919 {
michael@0 920 /*
michael@0 921 * Non-top-level functions use JSOP_DEFFUN which is a dynamic scope
michael@0 922 * operation which means it aliases any bindings with the same name.
michael@0 923 */
michael@0 924 if (FuncStmtSet *set = pc->funcStmts) {
michael@0 925 for (FuncStmtSet::Range r = set->all(); !r.empty(); r.popFront()) {
michael@0 926 PropertyName *name = r.front()->asPropertyName();
michael@0 927 if (Definition *dn = pc->decls().lookupFirst(name))
michael@0 928 dn->pn_dflags |= PND_CLOSED;
michael@0 929 }
michael@0 930 }
michael@0 931
michael@0 932 /* Time to implement the odd semantics of 'arguments'. */
michael@0 933 HandlePropertyName arguments = context->names().arguments;
michael@0 934
michael@0 935 /*
michael@0 936 * As explained by the ContextFlags::funArgumentsHasLocalBinding comment,
michael@0 937 * create a declaration for 'arguments' if there are any unbound uses in
michael@0 938 * the function body.
michael@0 939 */
michael@0 940 for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) {
michael@0 941 if (r.front().key() == arguments) {
michael@0 942 Definition *dn = r.front().value().get<FullParseHandler>();
michael@0 943 pc->lexdeps->remove(arguments);
michael@0 944 dn->pn_dflags |= PND_IMPLICITARGUMENTS;
michael@0 945 if (!pc->define(tokenStream, arguments, dn, Definition::VAR))
michael@0 946 return false;
michael@0 947 pc->sc->asFunctionBox()->usesArguments = true;
michael@0 948 break;
michael@0 949 }
michael@0 950 }
michael@0 951
michael@0 952 /*
michael@0 953 * Report error if both rest parameters and 'arguments' are used. Do this
michael@0 954 * check before adding artificial 'arguments' below.
michael@0 955 */
michael@0 956 Definition *maybeArgDef = pc->decls().lookupFirst(arguments);
michael@0 957 bool argumentsHasBinding = !!maybeArgDef;
michael@0 958 bool argumentsHasLocalBinding = maybeArgDef && maybeArgDef->kind() != Definition::ARG;
michael@0 959 bool hasRest = pc->sc->asFunctionBox()->function()->hasRest();
michael@0 960 if (hasRest && argumentsHasLocalBinding) {
michael@0 961 report(ParseError, false, nullptr, JSMSG_ARGUMENTS_AND_REST);
michael@0 962 return false;
michael@0 963 }
michael@0 964
michael@0 965 /*
michael@0 966 * Even if 'arguments' isn't explicitly mentioned, dynamic name lookup
michael@0 967 * forces an 'arguments' binding. The exception is that functions with rest
michael@0 968 * parameters are free from 'arguments'.
michael@0 969 */
michael@0 970 if (!argumentsHasBinding && pc->sc->bindingsAccessedDynamically() && !hasRest) {
michael@0 971 ParseNode *pn = newName(arguments);
michael@0 972 if (!pn)
michael@0 973 return false;
michael@0 974 if (!pc->define(tokenStream, arguments, pn, Definition::VAR))
michael@0 975 return false;
michael@0 976 argumentsHasBinding = true;
michael@0 977 argumentsHasLocalBinding = true;
michael@0 978 }
michael@0 979
michael@0 980 /*
michael@0 981 * Now that all possible 'arguments' bindings have been added, note whether
michael@0 982 * 'arguments' has a local binding and whether it unconditionally needs an
michael@0 983 * arguments object. (Also see the flags' comments in ContextFlags.)
michael@0 984 */
michael@0 985 if (argumentsHasLocalBinding) {
michael@0 986 FunctionBox *funbox = pc->sc->asFunctionBox();
michael@0 987 funbox->setArgumentsHasLocalBinding();
michael@0 988
michael@0 989 /*
michael@0 990 * If a script has both explicit mentions of 'arguments' and dynamic
michael@0 991 * name lookups which could access the arguments, an arguments object
michael@0 992 * must be created eagerly. The SSA analysis used for lazy arguments
michael@0 993 * cannot cope with dynamic name accesses, so any 'arguments' accessed
michael@0 994 * via a NAME opcode must force construction of the arguments object.
michael@0 995 */
michael@0 996 if (pc->sc->bindingsAccessedDynamically() && maybeArgDef)
michael@0 997 funbox->setDefinitelyNeedsArgsObj();
michael@0 998
michael@0 999 /*
michael@0 1000 * If a script contains the debugger statement either directly or
michael@0 1001 * within an inner function, the arguments object must be created
michael@0 1002 * eagerly. The debugger can walk the scope chain and observe any
michael@0 1003 * values along it.
michael@0 1004 */
michael@0 1005 if (pc->sc->hasDebuggerStatement())
michael@0 1006 funbox->setDefinitelyNeedsArgsObj();
michael@0 1007
michael@0 1008 /*
michael@0 1009 * Check whether any parameters have been assigned within this
michael@0 1010 * function. In strict mode parameters do not alias arguments[i], and
michael@0 1011 * to make the arguments object reflect initial parameter values prior
michael@0 1012 * to any mutation we create it eagerly whenever parameters are (or
michael@0 1013 * might, in the case of calls to eval) be assigned.
michael@0 1014 */
michael@0 1015 if (pc->sc->needStrictChecks()) {
michael@0 1016 for (AtomDefnListMap::Range r = pc->decls().all(); !r.empty(); r.popFront()) {
michael@0 1017 DefinitionList &dlist = r.front().value();
michael@0 1018 for (DefinitionList::Range dr = dlist.all(); !dr.empty(); dr.popFront()) {
michael@0 1019 Definition *dn = dr.front<FullParseHandler>();
michael@0 1020 if (dn->kind() == Definition::ARG && dn->isAssigned())
michael@0 1021 funbox->setDefinitelyNeedsArgsObj();
michael@0 1022 }
michael@0 1023 }
michael@0 1024 /* Watch for mutation of arguments through e.g. eval(). */
michael@0 1025 if (pc->sc->bindingsAccessedDynamically())
michael@0 1026 funbox->setDefinitelyNeedsArgsObj();
michael@0 1027 }
michael@0 1028 }
michael@0 1029
michael@0 1030 return true;
michael@0 1031 }
michael@0 1032
michael@0 1033 template <>
michael@0 1034 bool
michael@0 1035 Parser<SyntaxParseHandler>::checkFunctionArguments()
michael@0 1036 {
michael@0 1037 bool hasRest = pc->sc->asFunctionBox()->function()->hasRest();
michael@0 1038
michael@0 1039 if (pc->lexdeps->lookup(context->names().arguments)) {
michael@0 1040 pc->sc->asFunctionBox()->usesArguments = true;
michael@0 1041 if (hasRest) {
michael@0 1042 report(ParseError, false, null(), JSMSG_ARGUMENTS_AND_REST);
michael@0 1043 return false;
michael@0 1044 }
michael@0 1045 } else if (hasRest) {
michael@0 1046 DefinitionNode maybeArgDef = pc->decls().lookupFirst(context->names().arguments);
michael@0 1047 if (maybeArgDef && handler.getDefinitionKind(maybeArgDef) != Definition::ARG) {
michael@0 1048 report(ParseError, false, null(), JSMSG_ARGUMENTS_AND_REST);
michael@0 1049 return false;
michael@0 1050 }
michael@0 1051 }
michael@0 1052
michael@0 1053 return true;
michael@0 1054 }
michael@0 1055
michael@0 1056 template <typename ParseHandler>
michael@0 1057 typename ParseHandler::Node
michael@0 1058 Parser<ParseHandler>::functionBody(FunctionSyntaxKind kind, FunctionBodyType type)
michael@0 1059 {
michael@0 1060 JS_ASSERT(pc->sc->isFunctionBox());
michael@0 1061 JS_ASSERT(!pc->funHasReturnExpr && !pc->funHasReturnVoid);
michael@0 1062
michael@0 1063 #ifdef DEBUG
michael@0 1064 uint32_t startYieldOffset = pc->lastYieldOffset;
michael@0 1065 #endif
michael@0 1066
michael@0 1067 Node pn;
michael@0 1068 if (type == StatementListBody) {
michael@0 1069 pn = statements();
michael@0 1070 if (!pn)
michael@0 1071 return null();
michael@0 1072 } else {
michael@0 1073 JS_ASSERT(type == ExpressionBody);
michael@0 1074 JS_ASSERT(JS_HAS_EXPR_CLOSURES);
michael@0 1075
michael@0 1076 Node kid = assignExpr();
michael@0 1077 if (!kid)
michael@0 1078 return null();
michael@0 1079
michael@0 1080 pn = handler.newReturnStatement(kid, handler.getPosition(kid));
michael@0 1081 if (!pn)
michael@0 1082 return null();
michael@0 1083 }
michael@0 1084
michael@0 1085 switch (pc->generatorKind()) {
michael@0 1086 case NotGenerator:
michael@0 1087 JS_ASSERT(pc->lastYieldOffset == startYieldOffset);
michael@0 1088 break;
michael@0 1089
michael@0 1090 case LegacyGenerator:
michael@0 1091 // FIXME: Catch these errors eagerly, in yieldExpression().
michael@0 1092 JS_ASSERT(pc->lastYieldOffset != startYieldOffset);
michael@0 1093 if (kind == Arrow) {
michael@0 1094 reportWithOffset(ParseError, false, pc->lastYieldOffset,
michael@0 1095 JSMSG_YIELD_IN_ARROW, js_yield_str);
michael@0 1096 return null();
michael@0 1097 }
michael@0 1098 if (type == ExpressionBody) {
michael@0 1099 reportBadReturn(pn, ParseError,
michael@0 1100 JSMSG_BAD_GENERATOR_RETURN,
michael@0 1101 JSMSG_BAD_ANON_GENERATOR_RETURN);
michael@0 1102 return null();
michael@0 1103 }
michael@0 1104 break;
michael@0 1105
michael@0 1106 case StarGenerator:
michael@0 1107 JS_ASSERT(kind != Arrow);
michael@0 1108 JS_ASSERT(type == StatementListBody);
michael@0 1109 break;
michael@0 1110 }
michael@0 1111
michael@0 1112 /* Check for falling off the end of a function that returns a value. */
michael@0 1113 if (options().extraWarningsOption && pc->funHasReturnExpr && !checkFinalReturn(pn))
michael@0 1114 return null();
michael@0 1115
michael@0 1116 /* Define the 'arguments' binding if necessary. */
michael@0 1117 if (!checkFunctionArguments())
michael@0 1118 return null();
michael@0 1119
michael@0 1120 return pn;
michael@0 1121 }
michael@0 1122
michael@0 1123 /* See comment for use in Parser::functionDef. */
michael@0 1124 template <>
michael@0 1125 bool
michael@0 1126 Parser<FullParseHandler>::makeDefIntoUse(Definition *dn, ParseNode *pn, JSAtom *atom)
michael@0 1127 {
michael@0 1128 /* Turn pn into a definition. */
michael@0 1129 pc->updateDecl(atom, pn);
michael@0 1130
michael@0 1131 /* Change all uses of dn to be uses of pn. */
michael@0 1132 for (ParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
michael@0 1133 JS_ASSERT(pnu->isUsed());
michael@0 1134 JS_ASSERT(!pnu->isDefn());
michael@0 1135 pnu->pn_lexdef = (Definition *) pn;
michael@0 1136 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
michael@0 1137 }
michael@0 1138 pn->pn_dflags |= dn->pn_dflags & PND_USE2DEF_FLAGS;
michael@0 1139 pn->dn_uses = dn;
michael@0 1140
michael@0 1141 /*
michael@0 1142 * A PNK_FUNCTION node must be a definition, so convert shadowed function
michael@0 1143 * statements into nops. This is valid since all body-level function
michael@0 1144 * statement initialization happens at the beginning of the function
michael@0 1145 * (thus, only the last statement's effect is visible). E.g., in
michael@0 1146 *
michael@0 1147 * function outer() {
michael@0 1148 * function g() { return 1 }
michael@0 1149 * assertEq(g(), 2);
michael@0 1150 * function g() { return 2 }
michael@0 1151 * assertEq(g(), 2);
michael@0 1152 * }
michael@0 1153 *
michael@0 1154 * both asserts are valid.
michael@0 1155 */
michael@0 1156 if (dn->getKind() == PNK_FUNCTION) {
michael@0 1157 JS_ASSERT(dn->functionIsHoisted());
michael@0 1158 pn->dn_uses = dn->pn_link;
michael@0 1159 handler.prepareNodeForMutation(dn);
michael@0 1160 dn->setKind(PNK_NOP);
michael@0 1161 dn->setArity(PN_NULLARY);
michael@0 1162 return true;
michael@0 1163 }
michael@0 1164
michael@0 1165 /*
michael@0 1166 * If dn is arg, or in [var, const, let] and has an initializer, then we
michael@0 1167 * must rewrite it to be an assignment node, whose freshly allocated
michael@0 1168 * left-hand side becomes a use of pn.
michael@0 1169 */
michael@0 1170 if (dn->canHaveInitializer()) {
michael@0 1171 if (ParseNode *rhs = dn->expr()) {
michael@0 1172 ParseNode *lhs = handler.makeAssignment(dn, rhs);
michael@0 1173 if (!lhs)
michael@0 1174 return false;
michael@0 1175 pn->dn_uses = lhs;
michael@0 1176 dn->pn_link = nullptr;
michael@0 1177 dn = (Definition *) lhs;
michael@0 1178 }
michael@0 1179 }
michael@0 1180
michael@0 1181 /* Turn dn into a use of pn. */
michael@0 1182 JS_ASSERT(dn->isKind(PNK_NAME));
michael@0 1183 JS_ASSERT(dn->isArity(PN_NAME));
michael@0 1184 JS_ASSERT(dn->pn_atom == atom);
michael@0 1185 dn->setOp((js_CodeSpec[dn->getOp()].format & JOF_SET) ? JSOP_SETNAME : JSOP_NAME);
michael@0 1186 dn->setDefn(false);
michael@0 1187 dn->setUsed(true);
michael@0 1188 dn->pn_lexdef = (Definition *) pn;
michael@0 1189 dn->pn_cookie.makeFree();
michael@0 1190 dn->pn_dflags &= ~PND_BOUND;
michael@0 1191 return true;
michael@0 1192 }
michael@0 1193
michael@0 1194 /*
michael@0 1195 * Parameter block types for the several Binder functions. We use a common
michael@0 1196 * helper function signature in order to share code among destructuring and
michael@0 1197 * simple variable declaration parsers. In the destructuring case, the binder
michael@0 1198 * function is called indirectly from the variable declaration parser by way
michael@0 1199 * of CheckDestructuring and its friends.
michael@0 1200 */
michael@0 1201
michael@0 1202 template <typename ParseHandler>
michael@0 1203 struct BindData
michael@0 1204 {
michael@0 1205 BindData(ExclusiveContext *cx) : let(cx) {}
michael@0 1206
michael@0 1207 typedef bool
michael@0 1208 (*Binder)(BindData *data, HandlePropertyName name, Parser<ParseHandler> *parser);
michael@0 1209
michael@0 1210 /* name node for definition processing and error source coordinates */
michael@0 1211 typename ParseHandler::Node pn;
michael@0 1212
michael@0 1213 JSOp op; /* prolog bytecode or nop */
michael@0 1214 Binder binder; /* binder, discriminates u */
michael@0 1215
michael@0 1216 struct LetData {
michael@0 1217 LetData(ExclusiveContext *cx) : blockObj(cx) {}
michael@0 1218 VarContext varContext;
michael@0 1219 RootedStaticBlockObject blockObj;
michael@0 1220 unsigned overflow;
michael@0 1221 } let;
michael@0 1222
michael@0 1223 void initLet(VarContext varContext, StaticBlockObject &blockObj, unsigned overflow) {
michael@0 1224 this->pn = ParseHandler::null();
michael@0 1225 this->op = JSOP_NOP;
michael@0 1226 this->binder = Parser<ParseHandler>::bindLet;
michael@0 1227 this->let.varContext = varContext;
michael@0 1228 this->let.blockObj = &blockObj;
michael@0 1229 this->let.overflow = overflow;
michael@0 1230 }
michael@0 1231
michael@0 1232 void initVarOrConst(JSOp op) {
michael@0 1233 this->op = op;
michael@0 1234 this->binder = Parser<ParseHandler>::bindVarOrConst;
michael@0 1235 }
michael@0 1236 };
michael@0 1237
michael@0 1238 template <typename ParseHandler>
michael@0 1239 JSFunction *
michael@0 1240 Parser<ParseHandler>::newFunction(GenericParseContext *pc, HandleAtom atom,
michael@0 1241 FunctionSyntaxKind kind, JSObject *proto)
michael@0 1242 {
michael@0 1243 JS_ASSERT_IF(kind == Statement, atom != nullptr);
michael@0 1244
michael@0 1245 /*
michael@0 1246 * Find the global compilation context in order to pre-set the newborn
michael@0 1247 * function's parent slot to pc->sc->as<GlobalObject>()->scopeChain. If the
michael@0 1248 * global context is a compile-and-go one, we leave the pre-set parent
michael@0 1249 * intact; otherwise we clear parent and proto.
michael@0 1250 */
michael@0 1251 while (pc->parent)
michael@0 1252 pc = pc->parent;
michael@0 1253
michael@0 1254 RootedFunction fun(context);
michael@0 1255 JSFunction::Flags flags = (kind == Expression)
michael@0 1256 ? JSFunction::INTERPRETED_LAMBDA
michael@0 1257 : (kind == Arrow)
michael@0 1258 ? JSFunction::INTERPRETED_LAMBDA_ARROW
michael@0 1259 : JSFunction::INTERPRETED;
michael@0 1260 gc::AllocKind allocKind = JSFunction::FinalizeKind;
michael@0 1261 if (kind == Arrow)
michael@0 1262 allocKind = JSFunction::ExtendedFinalizeKind;
michael@0 1263 fun = NewFunctionWithProto(context, NullPtr(), nullptr, 0, flags, NullPtr(), atom, proto,
michael@0 1264 allocKind, MaybeSingletonObject);
michael@0 1265 if (!fun)
michael@0 1266 return nullptr;
michael@0 1267 if (options().selfHostingMode)
michael@0 1268 fun->setIsSelfHostedBuiltin();
michael@0 1269 return fun;
michael@0 1270 }
michael@0 1271
michael@0 1272 static bool
michael@0 1273 MatchOrInsertSemicolon(TokenStream &ts)
michael@0 1274 {
michael@0 1275 TokenKind tt = ts.peekTokenSameLine(TokenStream::Operand);
michael@0 1276 if (tt == TOK_ERROR)
michael@0 1277 return false;
michael@0 1278 if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
michael@0 1279 /* Advance the scanner for proper error location reporting. */
michael@0 1280 ts.getToken(TokenStream::Operand);
michael@0 1281 ts.reportError(JSMSG_SEMI_BEFORE_STMNT);
michael@0 1282 return false;
michael@0 1283 }
michael@0 1284 (void) ts.matchToken(TOK_SEMI);
michael@0 1285 return true;
michael@0 1286 }
michael@0 1287
michael@0 1288 template <typename ParseHandler>
michael@0 1289 typename ParseHandler::DefinitionNode
michael@0 1290 Parser<ParseHandler>::getOrCreateLexicalDependency(ParseContext<ParseHandler> *pc, JSAtom *atom)
michael@0 1291 {
michael@0 1292 AtomDefnAddPtr p = pc->lexdeps->lookupForAdd(atom);
michael@0 1293 if (p)
michael@0 1294 return p.value().get<ParseHandler>();
michael@0 1295
michael@0 1296 DefinitionNode dn = handler.newPlaceholder(atom, pc->blockid(), pos());
michael@0 1297 if (!dn)
michael@0 1298 return ParseHandler::nullDefinition();
michael@0 1299 DefinitionSingle def = DefinitionSingle::new_<ParseHandler>(dn);
michael@0 1300 if (!pc->lexdeps->add(p, atom, def))
michael@0 1301 return ParseHandler::nullDefinition();
michael@0 1302 return dn;
michael@0 1303 }
michael@0 1304
michael@0 1305 static bool
michael@0 1306 ConvertDefinitionToNamedLambdaUse(TokenStream &ts, ParseContext<FullParseHandler> *pc,
michael@0 1307 FunctionBox *funbox, Definition *dn)
michael@0 1308 {
michael@0 1309 dn->setOp(JSOP_CALLEE);
michael@0 1310 if (!dn->pn_cookie.set(ts, pc->staticLevel, 0))
michael@0 1311 return false;
michael@0 1312 dn->pn_dflags |= PND_BOUND;
michael@0 1313 JS_ASSERT(dn->kind() == Definition::NAMED_LAMBDA);
michael@0 1314
michael@0 1315 /*
michael@0 1316 * Since 'dn' is a placeholder, it has not been defined in the
michael@0 1317 * ParseContext and hence we must manually flag a closed-over
michael@0 1318 * callee name as needing a dynamic scope (this is done for all
michael@0 1319 * definitions in the ParseContext by generateFunctionBindings).
michael@0 1320 *
michael@0 1321 * If 'dn' has been assigned to, then we also flag the function
michael@0 1322 * scope has needing a dynamic scope so that dynamic scope
michael@0 1323 * setter can either ignore the set (in non-strict mode) or
michael@0 1324 * produce an error (in strict mode).
michael@0 1325 */
michael@0 1326 if (dn->isClosed() || dn->isAssigned())
michael@0 1327 funbox->setNeedsDeclEnvObject();
michael@0 1328 return true;
michael@0 1329 }
michael@0 1330
michael@0 1331 /*
michael@0 1332 * Beware: this function is called for functions nested in other functions or
michael@0 1333 * global scripts but not for functions compiled through the Function
michael@0 1334 * constructor or JSAPI. To always execute code when a function has finished
michael@0 1335 * parsing, use Parser::functionBody.
michael@0 1336 */
michael@0 1337 template <>
michael@0 1338 bool
michael@0 1339 Parser<FullParseHandler>::leaveFunction(ParseNode *fn, ParseContext<FullParseHandler> *outerpc,
michael@0 1340 FunctionSyntaxKind kind)
michael@0 1341 {
michael@0 1342 outerpc->blockidGen = pc->blockidGen;
michael@0 1343
michael@0 1344 FunctionBox *funbox = fn->pn_funbox;
michael@0 1345 JS_ASSERT(funbox == pc->sc->asFunctionBox());
michael@0 1346
michael@0 1347 /* Propagate unresolved lexical names up to outerpc->lexdeps. */
michael@0 1348 if (pc->lexdeps->count()) {
michael@0 1349 for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) {
michael@0 1350 JSAtom *atom = r.front().key();
michael@0 1351 Definition *dn = r.front().value().get<FullParseHandler>();
michael@0 1352 JS_ASSERT(dn->isPlaceholder());
michael@0 1353
michael@0 1354 if (atom == funbox->function()->name() && kind == Expression) {
michael@0 1355 if (!ConvertDefinitionToNamedLambdaUse(tokenStream, pc, funbox, dn))
michael@0 1356 return false;
michael@0 1357 continue;
michael@0 1358 }
michael@0 1359
michael@0 1360 Definition *outer_dn = outerpc->decls().lookupFirst(atom);
michael@0 1361
michael@0 1362 /*
michael@0 1363 * Make sure to deoptimize lexical dependencies that are polluted
michael@0 1364 * by eval and function statements (which both flag the function as
michael@0 1365 * having an extensible scope) or any enclosing 'with'.
michael@0 1366 */
michael@0 1367 if (funbox->hasExtensibleScope() || outerpc->parsingWith)
michael@0 1368 handler.deoptimizeUsesWithin(dn, fn->pn_pos);
michael@0 1369
michael@0 1370 if (!outer_dn) {
michael@0 1371 /*
michael@0 1372 * Create a new placeholder for our outer lexdep. We could
michael@0 1373 * simply re-use the inner placeholder, but that introduces
michael@0 1374 * subtleties in the case where we find a later definition
michael@0 1375 * that captures an existing lexdep. For example:
michael@0 1376 *
michael@0 1377 * function f() { function g() { x; } let x; }
michael@0 1378 *
michael@0 1379 * Here, g's TOK_UPVARS node lists the placeholder for x,
michael@0 1380 * which must be captured by the 'let' declaration later,
michael@0 1381 * since 'let's are hoisted. Taking g's placeholder as our
michael@0 1382 * own would work fine. But consider:
michael@0 1383 *
michael@0 1384 * function f() { x; { function g() { x; } let x; } }
michael@0 1385 *
michael@0 1386 * Here, the 'let' must not capture all the uses of f's
michael@0 1387 * lexdep entry for x, but it must capture the x node
michael@0 1388 * referred to from g's TOK_UPVARS node. Always turning
michael@0 1389 * inherited lexdeps into uses of a new outer definition
michael@0 1390 * allows us to handle both these cases in a natural way.
michael@0 1391 */
michael@0 1392 outer_dn = getOrCreateLexicalDependency(outerpc, atom);
michael@0 1393 if (!outer_dn)
michael@0 1394 return false;
michael@0 1395 }
michael@0 1396
michael@0 1397 /*
michael@0 1398 * Insert dn's uses list at the front of outer_dn's list.
michael@0 1399 *
michael@0 1400 * Without loss of generality or correctness, we allow a dn to
michael@0 1401 * be in inner and outer lexdeps, since the purpose of lexdeps
michael@0 1402 * is one-pass coordination of name use and definition across
michael@0 1403 * functions, and if different dn's are used we'll merge lists
michael@0 1404 * when leaving the inner function.
michael@0 1405 *
michael@0 1406 * The dn == outer_dn case arises with generator expressions
michael@0 1407 * (see LegacyCompExprTransplanter::transplant, the PN_CODE/PN_NAME
michael@0 1408 * case), and nowhere else, currently.
michael@0 1409 */
michael@0 1410 if (dn != outer_dn) {
michael@0 1411 if (ParseNode *pnu = dn->dn_uses) {
michael@0 1412 while (true) {
michael@0 1413 pnu->pn_lexdef = outer_dn;
michael@0 1414 if (!pnu->pn_link)
michael@0 1415 break;
michael@0 1416 pnu = pnu->pn_link;
michael@0 1417 }
michael@0 1418 pnu->pn_link = outer_dn->dn_uses;
michael@0 1419 outer_dn->dn_uses = dn->dn_uses;
michael@0 1420 dn->dn_uses = nullptr;
michael@0 1421 }
michael@0 1422
michael@0 1423 outer_dn->pn_dflags |= dn->pn_dflags & ~PND_PLACEHOLDER;
michael@0 1424 }
michael@0 1425
michael@0 1426 /* Mark the outer dn as escaping. */
michael@0 1427 outer_dn->pn_dflags |= PND_CLOSED;
michael@0 1428 }
michael@0 1429 }
michael@0 1430
michael@0 1431 InternalHandle<Bindings*> bindings =
michael@0 1432 InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
michael@0 1433 return pc->generateFunctionBindings(context, tokenStream, alloc, bindings);
michael@0 1434 }
michael@0 1435
michael@0 1436 template <>
michael@0 1437 bool
michael@0 1438 Parser<SyntaxParseHandler>::leaveFunction(Node fn, ParseContext<SyntaxParseHandler> *outerpc,
michael@0 1439 FunctionSyntaxKind kind)
michael@0 1440 {
michael@0 1441 outerpc->blockidGen = pc->blockidGen;
michael@0 1442
michael@0 1443 FunctionBox *funbox = pc->sc->asFunctionBox();
michael@0 1444 return addFreeVariablesFromLazyFunction(funbox->function(), outerpc);
michael@0 1445 }
michael@0 1446
michael@0 1447 /*
michael@0 1448 * defineArg is called for both the arguments of a regular function definition
michael@0 1449 * and the arguments specified by the Function constructor.
michael@0 1450 *
michael@0 1451 * The 'disallowDuplicateArgs' bool indicates whether the use of another
michael@0 1452 * feature (destructuring or default arguments) disables duplicate arguments.
michael@0 1453 * (ECMA-262 requires us to support duplicate parameter names, but, for newer
michael@0 1454 * features, we consider the code to have "opted in" to higher standards and
michael@0 1455 * forbid duplicates.)
michael@0 1456 *
michael@0 1457 * If 'duplicatedArg' is non-null, then DefineArg assigns to it any previous
michael@0 1458 * argument with the same name. The caller may use this to report an error when
michael@0 1459 * one of the abovementioned features occurs after a duplicate.
michael@0 1460 */
michael@0 1461 template <typename ParseHandler>
michael@0 1462 bool
michael@0 1463 Parser<ParseHandler>::defineArg(Node funcpn, HandlePropertyName name,
michael@0 1464 bool disallowDuplicateArgs, Node *duplicatedArg)
michael@0 1465 {
michael@0 1466 SharedContext *sc = pc->sc;
michael@0 1467
michael@0 1468 /* Handle duplicate argument names. */
michael@0 1469 if (DefinitionNode prevDecl = pc->decls().lookupFirst(name)) {
michael@0 1470 Node pn = handler.getDefinitionNode(prevDecl);
michael@0 1471
michael@0 1472 /*
michael@0 1473 * Strict-mode disallows duplicate args. We may not know whether we are
michael@0 1474 * in strict mode or not (since the function body hasn't been parsed).
michael@0 1475 * In such cases, report will queue up the potential error and return
michael@0 1476 * 'true'.
michael@0 1477 */
michael@0 1478 if (sc->needStrictChecks()) {
michael@0 1479 JSAutoByteString bytes;
michael@0 1480 if (!AtomToPrintableString(context, name, &bytes))
michael@0 1481 return false;
michael@0 1482 if (!report(ParseStrictError, pc->sc->strict, pn,
michael@0 1483 JSMSG_DUPLICATE_FORMAL, bytes.ptr()))
michael@0 1484 {
michael@0 1485 return false;
michael@0 1486 }
michael@0 1487 }
michael@0 1488
michael@0 1489 if (disallowDuplicateArgs) {
michael@0 1490 report(ParseError, false, pn, JSMSG_BAD_DUP_ARGS);
michael@0 1491 return false;
michael@0 1492 }
michael@0 1493
michael@0 1494 if (duplicatedArg)
michael@0 1495 *duplicatedArg = pn;
michael@0 1496
michael@0 1497 /* ParseContext::define assumes and asserts prevDecl is not in decls. */
michael@0 1498 JS_ASSERT(handler.getDefinitionKind(prevDecl) == Definition::ARG);
michael@0 1499 pc->prepareToAddDuplicateArg(name, prevDecl);
michael@0 1500 }
michael@0 1501
michael@0 1502 Node argpn = newName(name);
michael@0 1503 if (!argpn)
michael@0 1504 return false;
michael@0 1505
michael@0 1506 if (!checkStrictBinding(name, argpn))
michael@0 1507 return false;
michael@0 1508
michael@0 1509 handler.addFunctionArgument(funcpn, argpn);
michael@0 1510 return pc->define(tokenStream, name, argpn, Definition::ARG);
michael@0 1511 }
michael@0 1512
michael@0 1513 template <typename ParseHandler>
michael@0 1514 /* static */ bool
michael@0 1515 Parser<ParseHandler>::bindDestructuringArg(BindData<ParseHandler> *data,
michael@0 1516 HandlePropertyName name, Parser<ParseHandler> *parser)
michael@0 1517 {
michael@0 1518 ParseContext<ParseHandler> *pc = parser->pc;
michael@0 1519 JS_ASSERT(pc->sc->isFunctionBox());
michael@0 1520
michael@0 1521 if (pc->decls().lookupFirst(name)) {
michael@0 1522 parser->report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS);
michael@0 1523 return false;
michael@0 1524 }
michael@0 1525
michael@0 1526 if (!parser->checkStrictBinding(name, data->pn))
michael@0 1527 return false;
michael@0 1528
michael@0 1529 return pc->define(parser->tokenStream, name, data->pn, Definition::VAR);
michael@0 1530 }
michael@0 1531
michael@0 1532 template <typename ParseHandler>
michael@0 1533 bool
michael@0 1534 Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, Node funcpn,
michael@0 1535 bool *hasRest)
michael@0 1536 {
michael@0 1537 FunctionBox *funbox = pc->sc->asFunctionBox();
michael@0 1538
michael@0 1539 *hasRest = false;
michael@0 1540
michael@0 1541 bool parenFreeArrow = false;
michael@0 1542 if (kind == Arrow && tokenStream.peekToken() == TOK_NAME) {
michael@0 1543 parenFreeArrow = true;
michael@0 1544 } else {
michael@0 1545 if (tokenStream.getToken() != TOK_LP) {
michael@0 1546 report(ParseError, false, null(),
michael@0 1547 kind == Arrow ? JSMSG_BAD_ARROW_ARGS : JSMSG_PAREN_BEFORE_FORMAL);
michael@0 1548 return false;
michael@0 1549 }
michael@0 1550
michael@0 1551 // Record the start of function source (for FunctionToString). If we
michael@0 1552 // are parenFreeArrow, we will set this below, after consuming the NAME.
michael@0 1553 funbox->setStart(tokenStream);
michael@0 1554 }
michael@0 1555
michael@0 1556 Node argsbody = handler.newList(PNK_ARGSBODY);
michael@0 1557 if (!argsbody)
michael@0 1558 return false;
michael@0 1559 handler.setFunctionBody(funcpn, argsbody);
michael@0 1560
michael@0 1561 if (parenFreeArrow || !tokenStream.matchToken(TOK_RP)) {
michael@0 1562 bool hasDefaults = false;
michael@0 1563 Node duplicatedArg = null();
michael@0 1564 Node list = null();
michael@0 1565
michael@0 1566 do {
michael@0 1567 if (*hasRest) {
michael@0 1568 report(ParseError, false, null(), JSMSG_PARAMETER_AFTER_REST);
michael@0 1569 return false;
michael@0 1570 }
michael@0 1571
michael@0 1572 TokenKind tt = tokenStream.getToken();
michael@0 1573 JS_ASSERT_IF(parenFreeArrow, tt == TOK_NAME);
michael@0 1574 switch (tt) {
michael@0 1575 case TOK_LB:
michael@0 1576 case TOK_LC:
michael@0 1577 {
michael@0 1578 /* See comment below in the TOK_NAME case. */
michael@0 1579 if (duplicatedArg) {
michael@0 1580 report(ParseError, false, duplicatedArg, JSMSG_BAD_DUP_ARGS);
michael@0 1581 return false;
michael@0 1582 }
michael@0 1583
michael@0 1584 if (hasDefaults) {
michael@0 1585 report(ParseError, false, null(), JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT);
michael@0 1586 return false;
michael@0 1587 }
michael@0 1588
michael@0 1589 funbox->hasDestructuringArgs = true;
michael@0 1590
michael@0 1591 /*
michael@0 1592 * A destructuring formal parameter turns into one or more
michael@0 1593 * local variables initialized from properties of a single
michael@0 1594 * anonymous positional parameter, so here we must tweak our
michael@0 1595 * binder and its data.
michael@0 1596 */
michael@0 1597 BindData<ParseHandler> data(context);
michael@0 1598 data.pn = ParseHandler::null();
michael@0 1599 data.op = JSOP_DEFVAR;
michael@0 1600 data.binder = bindDestructuringArg;
michael@0 1601 Node lhs = destructuringExpr(&data, tt);
michael@0 1602 if (!lhs)
michael@0 1603 return false;
michael@0 1604
michael@0 1605 /*
michael@0 1606 * Synthesize a destructuring assignment from the single
michael@0 1607 * anonymous positional parameter into the destructuring
michael@0 1608 * left-hand-side expression and accumulate it in list.
michael@0 1609 */
michael@0 1610 HandlePropertyName name = context->names().empty;
michael@0 1611 Node rhs = newName(name);
michael@0 1612 if (!rhs)
michael@0 1613 return false;
michael@0 1614
michael@0 1615 if (!pc->define(tokenStream, name, rhs, Definition::ARG))
michael@0 1616 return false;
michael@0 1617
michael@0 1618 Node item = handler.newBinary(PNK_ASSIGN, lhs, rhs);
michael@0 1619 if (!item)
michael@0 1620 return false;
michael@0 1621 if (list) {
michael@0 1622 handler.addList(list, item);
michael@0 1623 } else {
michael@0 1624 list = handler.newList(PNK_VAR, item);
michael@0 1625 if (!list)
michael@0 1626 return false;
michael@0 1627 *listp = list;
michael@0 1628 }
michael@0 1629 break;
michael@0 1630 }
michael@0 1631
michael@0 1632 case TOK_YIELD:
michael@0 1633 if (!checkYieldNameValidity())
michael@0 1634 return false;
michael@0 1635 goto TOK_NAME;
michael@0 1636
michael@0 1637 case TOK_TRIPLEDOT:
michael@0 1638 {
michael@0 1639 *hasRest = true;
michael@0 1640 tt = tokenStream.getToken();
michael@0 1641 if (tt != TOK_NAME) {
michael@0 1642 if (tt != TOK_ERROR)
michael@0 1643 report(ParseError, false, null(), JSMSG_NO_REST_NAME);
michael@0 1644 return false;
michael@0 1645 }
michael@0 1646 goto TOK_NAME;
michael@0 1647 }
michael@0 1648
michael@0 1649 TOK_NAME:
michael@0 1650 case TOK_NAME:
michael@0 1651 {
michael@0 1652 if (parenFreeArrow)
michael@0 1653 funbox->setStart(tokenStream);
michael@0 1654
michael@0 1655 RootedPropertyName name(context, tokenStream.currentName());
michael@0 1656 bool disallowDuplicateArgs = funbox->hasDestructuringArgs || hasDefaults;
michael@0 1657 if (!defineArg(funcpn, name, disallowDuplicateArgs, &duplicatedArg))
michael@0 1658 return false;
michael@0 1659
michael@0 1660 if (tokenStream.matchToken(TOK_ASSIGN)) {
michael@0 1661 // A default argument without parentheses would look like:
michael@0 1662 // a = expr => body, but both operators are right-associative, so
michael@0 1663 // that would have been parsed as a = (expr => body) instead.
michael@0 1664 // Therefore it's impossible to get here with parenFreeArrow.
michael@0 1665 JS_ASSERT(!parenFreeArrow);
michael@0 1666
michael@0 1667 if (*hasRest) {
michael@0 1668 report(ParseError, false, null(), JSMSG_REST_WITH_DEFAULT);
michael@0 1669 return false;
michael@0 1670 }
michael@0 1671 if (duplicatedArg) {
michael@0 1672 report(ParseError, false, duplicatedArg, JSMSG_BAD_DUP_ARGS);
michael@0 1673 return false;
michael@0 1674 }
michael@0 1675 if (!hasDefaults) {
michael@0 1676 hasDefaults = true;
michael@0 1677
michael@0 1678 // The Function.length property is the number of formals
michael@0 1679 // before the first default argument.
michael@0 1680 funbox->length = pc->numArgs() - 1;
michael@0 1681 }
michael@0 1682 Node def_expr = assignExprWithoutYield(JSMSG_YIELD_IN_DEFAULT);
michael@0 1683 if (!def_expr)
michael@0 1684 return false;
michael@0 1685 handler.setLastFunctionArgumentDefault(funcpn, def_expr);
michael@0 1686 }
michael@0 1687
michael@0 1688 break;
michael@0 1689 }
michael@0 1690
michael@0 1691 default:
michael@0 1692 report(ParseError, false, null(), JSMSG_MISSING_FORMAL);
michael@0 1693 /* FALL THROUGH */
michael@0 1694 case TOK_ERROR:
michael@0 1695 return false;
michael@0 1696 }
michael@0 1697 } while (!parenFreeArrow && tokenStream.matchToken(TOK_COMMA));
michael@0 1698
michael@0 1699 if (!parenFreeArrow && tokenStream.getToken() != TOK_RP) {
michael@0 1700 report(ParseError, false, null(), JSMSG_PAREN_AFTER_FORMAL);
michael@0 1701 return false;
michael@0 1702 }
michael@0 1703
michael@0 1704 if (!hasDefaults)
michael@0 1705 funbox->length = pc->numArgs() - *hasRest;
michael@0 1706 }
michael@0 1707
michael@0 1708 return true;
michael@0 1709 }
michael@0 1710
michael@0 1711 template <>
michael@0 1712 bool
michael@0 1713 Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
michael@0 1714 ParseNode **pn_, FunctionSyntaxKind kind,
michael@0 1715 bool *pbodyProcessed)
michael@0 1716 {
michael@0 1717 ParseNode *&pn = *pn_;
michael@0 1718 *pbodyProcessed = false;
michael@0 1719
michael@0 1720 /* Function statements add a binding to the enclosing scope. */
michael@0 1721 bool bodyLevel = pc->atBodyLevel();
michael@0 1722
michael@0 1723 if (kind == Statement) {
michael@0 1724 /*
michael@0 1725 * Handle redeclaration and optimize cases where we can statically bind the
michael@0 1726 * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup).
michael@0 1727 */
michael@0 1728 if (Definition *dn = pc->decls().lookupFirst(funName)) {
michael@0 1729 JS_ASSERT(!dn->isUsed());
michael@0 1730 JS_ASSERT(dn->isDefn());
michael@0 1731
michael@0 1732 if (options().extraWarningsOption || dn->kind() == Definition::CONST) {
michael@0 1733 JSAutoByteString name;
michael@0 1734 ParseReportKind reporter = (dn->kind() != Definition::CONST)
michael@0 1735 ? ParseExtraWarning
michael@0 1736 : ParseError;
michael@0 1737 if (!AtomToPrintableString(context, funName, &name) ||
michael@0 1738 !report(reporter, false, nullptr, JSMSG_REDECLARED_VAR,
michael@0 1739 Definition::kindString(dn->kind()), name.ptr()))
michael@0 1740 {
michael@0 1741 return false;
michael@0 1742 }
michael@0 1743 }
michael@0 1744
michael@0 1745 /*
michael@0 1746 * Body-level function statements are effectively variable
michael@0 1747 * declarations where the initialization is hoisted to the
michael@0 1748 * beginning of the block. This means that any other variable
michael@0 1749 * declaration with the same name is really just an assignment to
michael@0 1750 * the function's binding (which is mutable), so turn any existing
michael@0 1751 * declaration into a use.
michael@0 1752 */
michael@0 1753 if (bodyLevel && !makeDefIntoUse(dn, pn, funName))
michael@0 1754 return false;
michael@0 1755 } else if (bodyLevel) {
michael@0 1756 /*
michael@0 1757 * If this function was used before it was defined, claim the
michael@0 1758 * pre-created definition node for this function that primaryExpr
michael@0 1759 * put in pc->lexdeps on first forward reference, and recycle pn.
michael@0 1760 */
michael@0 1761 if (Definition *fn = pc->lexdeps.lookupDefn<FullParseHandler>(funName)) {
michael@0 1762 JS_ASSERT(fn->isDefn());
michael@0 1763 fn->setKind(PNK_FUNCTION);
michael@0 1764 fn->setArity(PN_CODE);
michael@0 1765 fn->pn_pos.begin = pn->pn_pos.begin;
michael@0 1766 fn->pn_pos.end = pn->pn_pos.end;
michael@0 1767
michael@0 1768 fn->pn_body = nullptr;
michael@0 1769 fn->pn_cookie.makeFree();
michael@0 1770
michael@0 1771 pc->lexdeps->remove(funName);
michael@0 1772 handler.freeTree(pn);
michael@0 1773 pn = fn;
michael@0 1774 }
michael@0 1775
michael@0 1776 if (!pc->define(tokenStream, funName, pn, Definition::VAR))
michael@0 1777 return false;
michael@0 1778 }
michael@0 1779
michael@0 1780 if (bodyLevel) {
michael@0 1781 JS_ASSERT(pn->functionIsHoisted());
michael@0 1782 JS_ASSERT_IF(pc->sc->isFunctionBox(), !pn->pn_cookie.isFree());
michael@0 1783 JS_ASSERT_IF(!pc->sc->isFunctionBox(), pn->pn_cookie.isFree());
michael@0 1784 } else {
michael@0 1785 /*
michael@0 1786 * As a SpiderMonkey-specific extension, non-body-level function
michael@0 1787 * statements (e.g., functions in an "if" or "while" block) are
michael@0 1788 * dynamically bound when control flow reaches the statement.
michael@0 1789 */
michael@0 1790 JS_ASSERT(!pc->sc->strict);
michael@0 1791 JS_ASSERT(pn->pn_cookie.isFree());
michael@0 1792 if (pc->sc->isFunctionBox()) {
michael@0 1793 FunctionBox *funbox = pc->sc->asFunctionBox();
michael@0 1794 funbox->setMightAliasLocals();
michael@0 1795 funbox->setHasExtensibleScope();
michael@0 1796 }
michael@0 1797 pn->setOp(JSOP_DEFFUN);
michael@0 1798
michael@0 1799 /*
michael@0 1800 * Instead of setting bindingsAccessedDynamically, which would be
michael@0 1801 * overly conservative, remember the names of all function
michael@0 1802 * statements and mark any bindings with the same as aliased at the
michael@0 1803 * end of functionBody.
michael@0 1804 */
michael@0 1805 if (!pc->funcStmts) {
michael@0 1806 pc->funcStmts = context->new_<FuncStmtSet>(context);
michael@0 1807 if (!pc->funcStmts || !pc->funcStmts->init())
michael@0 1808 return false;
michael@0 1809 }
michael@0 1810 if (!pc->funcStmts->put(funName))
michael@0 1811 return false;
michael@0 1812
michael@0 1813 /*
michael@0 1814 * Due to the implicit declaration mechanism, 'arguments' will not
michael@0 1815 * have decls and, even if it did, they will not be noted as closed
michael@0 1816 * in the emitter. Thus, in the corner case of function statements
michael@0 1817 * overridding arguments, flag the whole scope as dynamic.
michael@0 1818 */
michael@0 1819 if (funName == context->names().arguments)
michael@0 1820 pc->sc->setBindingsAccessedDynamically();
michael@0 1821 }
michael@0 1822
michael@0 1823 /* No further binding (in BindNameToSlot) is needed for functions. */
michael@0 1824 pn->pn_dflags |= PND_BOUND;
michael@0 1825 } else {
michael@0 1826 /* A function expression does not introduce any binding. */
michael@0 1827 pn->setOp(kind == Arrow ? JSOP_LAMBDA_ARROW : JSOP_LAMBDA);
michael@0 1828 }
michael@0 1829
michael@0 1830 // When a lazily-parsed function is called, we only fully parse (and emit)
michael@0 1831 // that function, not any of its nested children. The initial syntax-only
michael@0 1832 // parse recorded the free variables of nested functions and their extents,
michael@0 1833 // so we can skip over them after accounting for their free variables.
michael@0 1834 if (LazyScript *lazyOuter = handler.lazyOuterFunction()) {
michael@0 1835 JSFunction *fun = handler.nextLazyInnerFunction();
michael@0 1836 JS_ASSERT(!fun->isLegacyGenerator());
michael@0 1837 FunctionBox *funbox = newFunctionBox(pn, fun, pc, Directives(/* strict = */ false),
michael@0 1838 fun->generatorKind());
michael@0 1839 if (!funbox)
michael@0 1840 return false;
michael@0 1841
michael@0 1842 if (!addFreeVariablesFromLazyFunction(fun, pc))
michael@0 1843 return false;
michael@0 1844
michael@0 1845 // The position passed to tokenStream.advance() is relative to
michael@0 1846 // userbuf.base() while LazyScript::{begin,end} offsets are relative to
michael@0 1847 // the outermost script source. N.B: userbuf.base() is initialized
michael@0 1848 // (in TokenStream()) to begin() - column() so that column numbers in
michael@0 1849 // the lazily parsed script are correct.
michael@0 1850 uint32_t userbufBase = lazyOuter->begin() - lazyOuter->column();
michael@0 1851 tokenStream.advance(fun->lazyScript()->end() - userbufBase);
michael@0 1852
michael@0 1853 *pbodyProcessed = true;
michael@0 1854 return true;
michael@0 1855 }
michael@0 1856
michael@0 1857 return true;
michael@0 1858 }
michael@0 1859
michael@0 1860 template <class T, class U>
michael@0 1861 static inline void
michael@0 1862 PropagateTransitiveParseFlags(const T *inner, U *outer)
michael@0 1863 {
michael@0 1864 if (inner->bindingsAccessedDynamically())
michael@0 1865 outer->setBindingsAccessedDynamically();
michael@0 1866 if (inner->hasDebuggerStatement())
michael@0 1867 outer->setHasDebuggerStatement();
michael@0 1868 }
michael@0 1869
michael@0 1870 template <typename ParseHandler>
michael@0 1871 bool
michael@0 1872 Parser<ParseHandler>::addFreeVariablesFromLazyFunction(JSFunction *fun,
michael@0 1873 ParseContext<ParseHandler> *pc)
michael@0 1874 {
michael@0 1875 // Update any definition nodes in this context according to free variables
michael@0 1876 // in a lazily parsed inner function.
michael@0 1877
michael@0 1878 LazyScript *lazy = fun->lazyScript();
michael@0 1879 HeapPtrAtom *freeVariables = lazy->freeVariables();
michael@0 1880 for (size_t i = 0; i < lazy->numFreeVariables(); i++) {
michael@0 1881 JSAtom *atom = freeVariables[i];
michael@0 1882
michael@0 1883 // 'arguments' will be implicitly bound within the inner function.
michael@0 1884 if (atom == context->names().arguments)
michael@0 1885 continue;
michael@0 1886
michael@0 1887 DefinitionNode dn = pc->decls().lookupFirst(atom);
michael@0 1888
michael@0 1889 if (!dn) {
michael@0 1890 dn = getOrCreateLexicalDependency(pc, atom);
michael@0 1891 if (!dn)
michael@0 1892 return false;
michael@0 1893 }
michael@0 1894
michael@0 1895 /* Mark the outer dn as escaping. */
michael@0 1896 handler.setFlag(handler.getDefinitionNode(dn), PND_CLOSED);
michael@0 1897 }
michael@0 1898
michael@0 1899 PropagateTransitiveParseFlags(lazy, pc->sc);
michael@0 1900 return true;
michael@0 1901 }
michael@0 1902
michael@0 1903 template <>
michael@0 1904 bool
michael@0 1905 Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
michael@0 1906 Node *pn, FunctionSyntaxKind kind,
michael@0 1907 bool *pbodyProcessed)
michael@0 1908 {
michael@0 1909 *pbodyProcessed = false;
michael@0 1910
michael@0 1911 /* Function statements add a binding to the enclosing scope. */
michael@0 1912 bool bodyLevel = pc->atBodyLevel();
michael@0 1913
michael@0 1914 if (kind == Statement) {
michael@0 1915 /*
michael@0 1916 * Handle redeclaration and optimize cases where we can statically bind the
michael@0 1917 * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup).
michael@0 1918 */
michael@0 1919 if (DefinitionNode dn = pc->decls().lookupFirst(funName)) {
michael@0 1920 if (dn == Definition::CONST) {
michael@0 1921 JSAutoByteString name;
michael@0 1922 if (!AtomToPrintableString(context, funName, &name) ||
michael@0 1923 !report(ParseError, false, null(), JSMSG_REDECLARED_VAR,
michael@0 1924 Definition::kindString(dn), name.ptr()))
michael@0 1925 {
michael@0 1926 return false;
michael@0 1927 }
michael@0 1928 }
michael@0 1929 } else if (bodyLevel) {
michael@0 1930 if (pc->lexdeps.lookupDefn<SyntaxParseHandler>(funName))
michael@0 1931 pc->lexdeps->remove(funName);
michael@0 1932
michael@0 1933 if (!pc->define(tokenStream, funName, *pn, Definition::VAR))
michael@0 1934 return false;
michael@0 1935 }
michael@0 1936
michael@0 1937 if (!bodyLevel && funName == context->names().arguments)
michael@0 1938 pc->sc->setBindingsAccessedDynamically();
michael@0 1939 }
michael@0 1940
michael@0 1941 if (kind == Arrow) {
michael@0 1942 /* Arrow functions cannot yet be parsed lazily. */
michael@0 1943 return abortIfSyntaxParser();
michael@0 1944 }
michael@0 1945
michael@0 1946 return true;
michael@0 1947 }
michael@0 1948
michael@0 1949 template <typename ParseHandler>
michael@0 1950 typename ParseHandler::Node
michael@0 1951 Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream::Position &start,
michael@0 1952 FunctionType type, FunctionSyntaxKind kind,
michael@0 1953 GeneratorKind generatorKind)
michael@0 1954 {
michael@0 1955 JS_ASSERT_IF(kind == Statement, funName);
michael@0 1956
michael@0 1957 /* Make a TOK_FUNCTION node. */
michael@0 1958 Node pn = handler.newFunctionDefinition();
michael@0 1959 if (!pn)
michael@0 1960 return null();
michael@0 1961
michael@0 1962 bool bodyProcessed;
michael@0 1963 if (!checkFunctionDefinition(funName, &pn, kind, &bodyProcessed))
michael@0 1964 return null();
michael@0 1965
michael@0 1966 if (bodyProcessed)
michael@0 1967 return pn;
michael@0 1968
michael@0 1969 RootedObject proto(context);
michael@0 1970 if (generatorKind == StarGenerator) {
michael@0 1971 // If we are off the main thread, the generator meta-objects have
michael@0 1972 // already been created by js::StartOffThreadParseScript, so cx will not
michael@0 1973 // be necessary.
michael@0 1974 JSContext *cx = context->maybeJSContext();
michael@0 1975 proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global());
michael@0 1976 if (!proto)
michael@0 1977 return null();
michael@0 1978 }
michael@0 1979 RootedFunction fun(context, newFunction(pc, funName, kind, proto));
michael@0 1980 if (!fun)
michael@0 1981 return null();
michael@0 1982
michael@0 1983 // Speculatively parse using the directives of the parent parsing context.
michael@0 1984 // If a directive is encountered (e.g., "use strict") that changes how the
michael@0 1985 // function should have been parsed, we backup and reparse with the new set
michael@0 1986 // of directives.
michael@0 1987 Directives directives(pc);
michael@0 1988 Directives newDirectives = directives;
michael@0 1989
michael@0 1990 while (true) {
michael@0 1991 if (functionArgsAndBody(pn, fun, type, kind, generatorKind, directives, &newDirectives))
michael@0 1992 break;
michael@0 1993 if (tokenStream.hadError() || directives == newDirectives)
michael@0 1994 return null();
michael@0 1995
michael@0 1996 // Assignment must be monotonic to prevent reparsing iloops
michael@0 1997 JS_ASSERT_IF(directives.strict(), newDirectives.strict());
michael@0 1998 JS_ASSERT_IF(directives.asmJS(), newDirectives.asmJS());
michael@0 1999 directives = newDirectives;
michael@0 2000
michael@0 2001 tokenStream.seek(start);
michael@0 2002 if (funName && tokenStream.getToken() == TOK_ERROR)
michael@0 2003 return null();
michael@0 2004
michael@0 2005 // functionArgsAndBody may have already set pn->pn_body before failing.
michael@0 2006 handler.setFunctionBody(pn, null());
michael@0 2007 }
michael@0 2008
michael@0 2009 return pn;
michael@0 2010 }
michael@0 2011
michael@0 2012 template <>
michael@0 2013 bool
michael@0 2014 Parser<FullParseHandler>::finishFunctionDefinition(ParseNode *pn, FunctionBox *funbox,
michael@0 2015 ParseNode *prelude, ParseNode *body)
michael@0 2016 {
michael@0 2017 pn->pn_pos.end = pos().end;
michael@0 2018
michael@0 2019 /*
michael@0 2020 * If there were destructuring formal parameters, prepend the initializing
michael@0 2021 * comma expression that we synthesized to body. If the body is a return
michael@0 2022 * node, we must make a special PNK_SEQ node, to prepend the destructuring
michael@0 2023 * code without bracing the decompilation of the function body.
michael@0 2024 */
michael@0 2025 if (prelude) {
michael@0 2026 if (!body->isArity(PN_LIST)) {
michael@0 2027 ParseNode *block;
michael@0 2028
michael@0 2029 block = ListNode::create(PNK_SEQ, &handler);
michael@0 2030 if (!block)
michael@0 2031 return false;
michael@0 2032 block->pn_pos = body->pn_pos;
michael@0 2033 block->initList(body);
michael@0 2034
michael@0 2035 body = block;
michael@0 2036 }
michael@0 2037
michael@0 2038 ParseNode *item = UnaryNode::create(PNK_SEMI, &handler);
michael@0 2039 if (!item)
michael@0 2040 return false;
michael@0 2041
michael@0 2042 item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
michael@0 2043 item->pn_kid = prelude;
michael@0 2044 item->pn_next = body->pn_head;
michael@0 2045 body->pn_head = item;
michael@0 2046 if (body->pn_tail == &body->pn_head)
michael@0 2047 body->pn_tail = &item->pn_next;
michael@0 2048 ++body->pn_count;
michael@0 2049 body->pn_xflags |= PNX_DESTRUCT;
michael@0 2050 }
michael@0 2051
michael@0 2052 JS_ASSERT(pn->pn_funbox == funbox);
michael@0 2053 JS_ASSERT(pn->pn_body->isKind(PNK_ARGSBODY));
michael@0 2054 pn->pn_body->append(body);
michael@0 2055 pn->pn_body->pn_pos = body->pn_pos;
michael@0 2056
michael@0 2057 return true;
michael@0 2058 }
michael@0 2059
michael@0 2060 template <>
michael@0 2061 bool
michael@0 2062 Parser<SyntaxParseHandler>::finishFunctionDefinition(Node pn, FunctionBox *funbox,
michael@0 2063 Node prelude, Node body)
michael@0 2064 {
michael@0 2065 // The LazyScript for a lazily parsed function needs to be constructed
michael@0 2066 // while its ParseContext and associated lexdeps and inner functions are
michael@0 2067 // still available.
michael@0 2068
michael@0 2069 if (funbox->inWith)
michael@0 2070 return abortIfSyntaxParser();
michael@0 2071
michael@0 2072 size_t numFreeVariables = pc->lexdeps->count();
michael@0 2073 size_t numInnerFunctions = pc->innerFunctions.length();
michael@0 2074
michael@0 2075 RootedFunction fun(context, funbox->function());
michael@0 2076 LazyScript *lazy = LazyScript::CreateRaw(context, fun, numFreeVariables, numInnerFunctions,
michael@0 2077 versionNumber(), funbox->bufStart, funbox->bufEnd,
michael@0 2078 funbox->startLine, funbox->startColumn);
michael@0 2079 if (!lazy)
michael@0 2080 return false;
michael@0 2081
michael@0 2082 HeapPtrAtom *freeVariables = lazy->freeVariables();
michael@0 2083 size_t i = 0;
michael@0 2084 for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront())
michael@0 2085 freeVariables[i++].init(r.front().key());
michael@0 2086 JS_ASSERT(i == numFreeVariables);
michael@0 2087
michael@0 2088 HeapPtrFunction *innerFunctions = lazy->innerFunctions();
michael@0 2089 for (size_t i = 0; i < numInnerFunctions; i++)
michael@0 2090 innerFunctions[i].init(pc->innerFunctions[i]);
michael@0 2091
michael@0 2092 if (pc->sc->strict)
michael@0 2093 lazy->setStrict();
michael@0 2094 lazy->setGeneratorKind(funbox->generatorKind());
michael@0 2095 if (funbox->usesArguments && funbox->usesApply)
michael@0 2096 lazy->setUsesArgumentsAndApply();
michael@0 2097 PropagateTransitiveParseFlags(funbox, lazy);
michael@0 2098
michael@0 2099 fun->initLazyScript(lazy);
michael@0 2100 return true;
michael@0 2101 }
michael@0 2102
michael@0 2103 template <>
michael@0 2104 bool
michael@0 2105 Parser<FullParseHandler>::functionArgsAndBody(ParseNode *pn, HandleFunction fun,
michael@0 2106 FunctionType type, FunctionSyntaxKind kind,
michael@0 2107 GeneratorKind generatorKind,
michael@0 2108 Directives inheritedDirectives,
michael@0 2109 Directives *newDirectives)
michael@0 2110 {
michael@0 2111 ParseContext<FullParseHandler> *outerpc = pc;
michael@0 2112
michael@0 2113 // Create box for fun->object early to protect against last-ditch GC.
michael@0 2114 FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives, generatorKind);
michael@0 2115 if (!funbox)
michael@0 2116 return false;
michael@0 2117
michael@0 2118 // Try a syntax parse for this inner function.
michael@0 2119 do {
michael@0 2120 Parser<SyntaxParseHandler> *parser = handler.syntaxParser;
michael@0 2121 if (!parser)
michael@0 2122 break;
michael@0 2123
michael@0 2124 {
michael@0 2125 // Move the syntax parser to the current position in the stream.
michael@0 2126 TokenStream::Position position(keepAtoms);
michael@0 2127 tokenStream.tell(&position);
michael@0 2128 if (!parser->tokenStream.seek(position, tokenStream))
michael@0 2129 return false;
michael@0 2130
michael@0 2131 ParseContext<SyntaxParseHandler> funpc(parser, outerpc, SyntaxParseHandler::null(), funbox,
michael@0 2132 newDirectives, outerpc->staticLevel + 1,
michael@0 2133 outerpc->blockidGen,
michael@0 2134 /* blockScopeDepth = */ 0);
michael@0 2135 if (!funpc.init(tokenStream))
michael@0 2136 return false;
michael@0 2137
michael@0 2138 if (!parser->functionArgsAndBodyGeneric(SyntaxParseHandler::NodeGeneric,
michael@0 2139 fun, type, kind, newDirectives))
michael@0 2140 {
michael@0 2141 if (parser->hadAbortedSyntaxParse()) {
michael@0 2142 // Try again with a full parse.
michael@0 2143 parser->clearAbortedSyntaxParse();
michael@0 2144 break;
michael@0 2145 }
michael@0 2146 return false;
michael@0 2147 }
michael@0 2148
michael@0 2149 outerpc->blockidGen = funpc.blockidGen;
michael@0 2150
michael@0 2151 // Advance this parser over tokens processed by the syntax parser.
michael@0 2152 parser->tokenStream.tell(&position);
michael@0 2153 if (!tokenStream.seek(position, parser->tokenStream))
michael@0 2154 return false;
michael@0 2155
michael@0 2156 // Update the end position of the parse node.
michael@0 2157 pn->pn_pos.end = tokenStream.currentToken().pos.end;
michael@0 2158 }
michael@0 2159
michael@0 2160 if (!addFreeVariablesFromLazyFunction(fun, pc))
michael@0 2161 return false;
michael@0 2162
michael@0 2163 pn->pn_blockid = outerpc->blockid();
michael@0 2164 PropagateTransitiveParseFlags(funbox, outerpc->sc);
michael@0 2165 return true;
michael@0 2166 } while (false);
michael@0 2167
michael@0 2168 // Continue doing a full parse for this inner function.
michael@0 2169 ParseContext<FullParseHandler> funpc(this, pc, pn, funbox, newDirectives,
michael@0 2170 outerpc->staticLevel + 1, outerpc->blockidGen,
michael@0 2171 /* blockScopeDepth = */ 0);
michael@0 2172 if (!funpc.init(tokenStream))
michael@0 2173 return false;
michael@0 2174
michael@0 2175 if (!functionArgsAndBodyGeneric(pn, fun, type, kind, newDirectives))
michael@0 2176 return false;
michael@0 2177
michael@0 2178 if (!leaveFunction(pn, outerpc, kind))
michael@0 2179 return false;
michael@0 2180
michael@0 2181 pn->pn_blockid = outerpc->blockid();
michael@0 2182
michael@0 2183 /*
michael@0 2184 * Fruit of the poisonous tree: if a closure contains a dynamic name access
michael@0 2185 * (eval, with, etc), we consider the parent to do the same. The reason is
michael@0 2186 * that the deoptimizing effects of dynamic name access apply equally to
michael@0 2187 * parents: any local can be read at runtime.
michael@0 2188 */
michael@0 2189 PropagateTransitiveParseFlags(funbox, outerpc->sc);
michael@0 2190 return true;
michael@0 2191 }
michael@0 2192
michael@0 2193 template <>
michael@0 2194 bool
michael@0 2195 Parser<SyntaxParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun,
michael@0 2196 FunctionType type, FunctionSyntaxKind kind,
michael@0 2197 GeneratorKind generatorKind,
michael@0 2198 Directives inheritedDirectives,
michael@0 2199 Directives *newDirectives)
michael@0 2200 {
michael@0 2201 ParseContext<SyntaxParseHandler> *outerpc = pc;
michael@0 2202
michael@0 2203 // Create box for fun->object early to protect against last-ditch GC.
michael@0 2204 FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives, generatorKind);
michael@0 2205 if (!funbox)
michael@0 2206 return false;
michael@0 2207
michael@0 2208 // Initialize early for possible flags mutation via destructuringExpr.
michael@0 2209 ParseContext<SyntaxParseHandler> funpc(this, pc, handler.null(), funbox, newDirectives,
michael@0 2210 outerpc->staticLevel + 1, outerpc->blockidGen,
michael@0 2211 /* blockScopeDepth = */ 0);
michael@0 2212 if (!funpc.init(tokenStream))
michael@0 2213 return false;
michael@0 2214
michael@0 2215 if (!functionArgsAndBodyGeneric(pn, fun, type, kind, newDirectives))
michael@0 2216 return false;
michael@0 2217
michael@0 2218 if (!leaveFunction(pn, outerpc, kind))
michael@0 2219 return false;
michael@0 2220
michael@0 2221 // This is a lazy function inner to another lazy function. Remember the
michael@0 2222 // inner function so that if the outer function is eventually parsed we do
michael@0 2223 // not need any further parsing or processing of the inner function.
michael@0 2224 JS_ASSERT(fun->lazyScript());
michael@0 2225 return outerpc->innerFunctions.append(fun);
michael@0 2226 }
michael@0 2227
michael@0 2228 template <>
michael@0 2229 ParseNode *
michael@0 2230 Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, unsigned staticLevel,
michael@0 2231 bool strict, GeneratorKind generatorKind)
michael@0 2232 {
michael@0 2233 Node pn = handler.newFunctionDefinition();
michael@0 2234 if (!pn)
michael@0 2235 return null();
michael@0 2236
michael@0 2237 Directives directives(/* strict = */ strict);
michael@0 2238 FunctionBox *funbox = newFunctionBox(pn, fun, /* outerpc = */ nullptr, directives,
michael@0 2239 generatorKind);
michael@0 2240 if (!funbox)
michael@0 2241 return null();
michael@0 2242 funbox->length = fun->nargs() - fun->hasRest();
michael@0 2243
michael@0 2244 Directives newDirectives = directives;
michael@0 2245 ParseContext<FullParseHandler> funpc(this, /* parent = */ nullptr, pn, funbox,
michael@0 2246 &newDirectives, staticLevel, /* bodyid = */ 0,
michael@0 2247 /* blockScopeDepth = */ 0);
michael@0 2248 if (!funpc.init(tokenStream))
michael@0 2249 return null();
michael@0 2250
michael@0 2251 if (!functionArgsAndBodyGeneric(pn, fun, Normal, Statement, &newDirectives)) {
michael@0 2252 JS_ASSERT(directives == newDirectives);
michael@0 2253 return null();
michael@0 2254 }
michael@0 2255
michael@0 2256 if (fun->isNamedLambda()) {
michael@0 2257 if (AtomDefnPtr p = pc->lexdeps->lookup(fun->name())) {
michael@0 2258 Definition *dn = p.value().get<FullParseHandler>();
michael@0 2259 if (!ConvertDefinitionToNamedLambdaUse(tokenStream, pc, funbox, dn))
michael@0 2260 return nullptr;
michael@0 2261 }
michael@0 2262 }
michael@0 2263
michael@0 2264 InternalHandle<Bindings*> bindings =
michael@0 2265 InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
michael@0 2266 if (!pc->generateFunctionBindings(context, tokenStream, alloc, bindings))
michael@0 2267 return null();
michael@0 2268
michael@0 2269 if (!FoldConstants(context, &pn, this))
michael@0 2270 return null();
michael@0 2271
michael@0 2272 return pn;
michael@0 2273 }
michael@0 2274
michael@0 2275 template <typename ParseHandler>
michael@0 2276 bool
michael@0 2277 Parser<ParseHandler>::functionArgsAndBodyGeneric(Node pn, HandleFunction fun, FunctionType type,
michael@0 2278 FunctionSyntaxKind kind,
michael@0 2279 Directives *newDirectives)
michael@0 2280 {
michael@0 2281 // Given a properly initialized parse context, try to parse an actual
michael@0 2282 // function without concern for conversion to strict mode, use of lazy
michael@0 2283 // parsing and such.
michael@0 2284
michael@0 2285 Node prelude = null();
michael@0 2286 bool hasRest;
michael@0 2287 if (!functionArguments(kind, &prelude, pn, &hasRest))
michael@0 2288 return false;
michael@0 2289
michael@0 2290 FunctionBox *funbox = pc->sc->asFunctionBox();
michael@0 2291
michael@0 2292 fun->setArgCount(pc->numArgs());
michael@0 2293 if (hasRest)
michael@0 2294 fun->setHasRest();
michael@0 2295
michael@0 2296 if (type == Getter && fun->nargs() > 0) {
michael@0 2297 report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "getter", "no", "s");
michael@0 2298 return false;
michael@0 2299 }
michael@0 2300 if (type == Setter && fun->nargs() != 1) {
michael@0 2301 report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
michael@0 2302 return false;
michael@0 2303 }
michael@0 2304
michael@0 2305 if (kind == Arrow && !tokenStream.matchToken(TOK_ARROW)) {
michael@0 2306 report(ParseError, false, null(), JSMSG_BAD_ARROW_ARGS);
michael@0 2307 return false;
michael@0 2308 }
michael@0 2309
michael@0 2310 // Parse the function body.
michael@0 2311 FunctionBodyType bodyType = StatementListBody;
michael@0 2312 if (tokenStream.getToken(TokenStream::Operand) != TOK_LC) {
michael@0 2313 if (funbox->isStarGenerator()) {
michael@0 2314 report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY);
michael@0 2315 return false;
michael@0 2316 }
michael@0 2317 tokenStream.ungetToken();
michael@0 2318 bodyType = ExpressionBody;
michael@0 2319 fun->setIsExprClosure();
michael@0 2320 }
michael@0 2321
michael@0 2322 Node body = functionBody(kind, bodyType);
michael@0 2323 if (!body)
michael@0 2324 return false;
michael@0 2325
michael@0 2326 if (fun->name() && !checkStrictBinding(fun->name(), pn))
michael@0 2327 return false;
michael@0 2328
michael@0 2329 #if JS_HAS_EXPR_CLOSURES
michael@0 2330 if (bodyType == StatementListBody) {
michael@0 2331 #endif
michael@0 2332 if (!tokenStream.matchToken(TOK_RC)) {
michael@0 2333 report(ParseError, false, null(), JSMSG_CURLY_AFTER_BODY);
michael@0 2334 return false;
michael@0 2335 }
michael@0 2336 funbox->bufEnd = pos().begin + 1;
michael@0 2337 #if JS_HAS_EXPR_CLOSURES
michael@0 2338 } else {
michael@0 2339 if (tokenStream.hadError())
michael@0 2340 return false;
michael@0 2341 funbox->bufEnd = pos().end;
michael@0 2342 if (kind == Statement && !MatchOrInsertSemicolon(tokenStream))
michael@0 2343 return false;
michael@0 2344 }
michael@0 2345 #endif
michael@0 2346
michael@0 2347 return finishFunctionDefinition(pn, funbox, prelude, body);
michael@0 2348 }
michael@0 2349
michael@0 2350 template <typename ParseHandler>
michael@0 2351 bool
michael@0 2352 Parser<ParseHandler>::checkYieldNameValidity()
michael@0 2353 {
michael@0 2354 // In star generators and in JS >= 1.7, yield is a keyword. Otherwise in
michael@0 2355 // strict mode, yield is a future reserved word.
michael@0 2356 if (pc->isStarGenerator() || versionNumber() >= JSVERSION_1_7 || pc->sc->strict) {
michael@0 2357 report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
michael@0 2358 return false;
michael@0 2359 }
michael@0 2360 return true;
michael@0 2361 }
michael@0 2362
michael@0 2363 template <typename ParseHandler>
michael@0 2364 typename ParseHandler::Node
michael@0 2365 Parser<ParseHandler>::functionStmt()
michael@0 2366 {
michael@0 2367 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
michael@0 2368
michael@0 2369 TokenStream::Position start(keepAtoms);
michael@0 2370 tokenStream.tell(&start);
michael@0 2371
michael@0 2372 RootedPropertyName name(context);
michael@0 2373 GeneratorKind generatorKind = NotGenerator;
michael@0 2374 TokenKind tt = tokenStream.getToken();
michael@0 2375
michael@0 2376 if (tt == TOK_MUL) {
michael@0 2377 tokenStream.tell(&start);
michael@0 2378 tt = tokenStream.getToken();
michael@0 2379 generatorKind = StarGenerator;
michael@0 2380 }
michael@0 2381
michael@0 2382 if (tt == TOK_NAME) {
michael@0 2383 name = tokenStream.currentName();
michael@0 2384 } else if (tt == TOK_YIELD) {
michael@0 2385 if (!checkYieldNameValidity())
michael@0 2386 return null();
michael@0 2387 name = tokenStream.currentName();
michael@0 2388 } else {
michael@0 2389 /* Unnamed function expressions are forbidden in statement context. */
michael@0 2390 report(ParseError, false, null(), JSMSG_UNNAMED_FUNCTION_STMT);
michael@0 2391 return null();
michael@0 2392 }
michael@0 2393
michael@0 2394 /* We forbid function statements in strict mode code. */
michael@0 2395 if (!pc->atBodyLevel() && pc->sc->needStrictChecks() &&
michael@0 2396 !report(ParseStrictError, pc->sc->strict, null(), JSMSG_STRICT_FUNCTION_STATEMENT))
michael@0 2397 return null();
michael@0 2398
michael@0 2399 return functionDef(name, start, Normal, Statement, generatorKind);
michael@0 2400 }
michael@0 2401
michael@0 2402 template <typename ParseHandler>
michael@0 2403 typename ParseHandler::Node
michael@0 2404 Parser<ParseHandler>::functionExpr()
michael@0 2405 {
michael@0 2406 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
michael@0 2407
michael@0 2408 TokenStream::Position start(keepAtoms);
michael@0 2409 tokenStream.tell(&start);
michael@0 2410
michael@0 2411 GeneratorKind generatorKind = NotGenerator;
michael@0 2412 TokenKind tt = tokenStream.getToken();
michael@0 2413
michael@0 2414 if (tt == TOK_MUL) {
michael@0 2415 tokenStream.tell(&start);
michael@0 2416 tt = tokenStream.getToken();
michael@0 2417 generatorKind = StarGenerator;
michael@0 2418 }
michael@0 2419
michael@0 2420 RootedPropertyName name(context);
michael@0 2421 if (tt == TOK_NAME) {
michael@0 2422 name = tokenStream.currentName();
michael@0 2423 } else if (tt == TOK_YIELD) {
michael@0 2424 if (!checkYieldNameValidity())
michael@0 2425 return null();
michael@0 2426 name = tokenStream.currentName();
michael@0 2427 } else {
michael@0 2428 tokenStream.ungetToken();
michael@0 2429 }
michael@0 2430
michael@0 2431 return functionDef(name, start, Normal, Expression, generatorKind);
michael@0 2432 }
michael@0 2433
michael@0 2434 /*
michael@0 2435 * Return true if this node, known to be an unparenthesized string literal,
michael@0 2436 * could be the string of a directive in a Directive Prologue. Directive
michael@0 2437 * strings never contain escape sequences or line continuations.
michael@0 2438 * isEscapeFreeStringLiteral, below, checks whether the node itself could be
michael@0 2439 * a directive.
michael@0 2440 */
michael@0 2441 static inline bool
michael@0 2442 IsEscapeFreeStringLiteral(const TokenPos &pos, JSAtom *str)
michael@0 2443 {
michael@0 2444 /*
michael@0 2445 * If the string's length in the source code is its length as a value,
michael@0 2446 * accounting for the quotes, then it must not contain any escape
michael@0 2447 * sequences or line continuations.
michael@0 2448 */
michael@0 2449 return pos.begin + str->length() + 2 == pos.end;
michael@0 2450 }
michael@0 2451
michael@0 2452 template <>
michael@0 2453 bool
michael@0 2454 Parser<SyntaxParseHandler>::asmJS(Node list)
michael@0 2455 {
michael@0 2456 // While asm.js could technically be validated and compiled during syntax
michael@0 2457 // parsing, we have no guarantee that some later JS wouldn't abort the
michael@0 2458 // syntax parse and cause us to re-parse (and re-compile) the asm.js module.
michael@0 2459 // For simplicity, unconditionally abort the syntax parse when "use asm" is
michael@0 2460 // encountered so that asm.js is always validated/compiled exactly once
michael@0 2461 // during a full parse.
michael@0 2462 JS_ALWAYS_FALSE(abortIfSyntaxParser());
michael@0 2463 return false;
michael@0 2464 }
michael@0 2465
michael@0 2466 template <>
michael@0 2467 bool
michael@0 2468 Parser<FullParseHandler>::asmJS(Node list)
michael@0 2469 {
michael@0 2470 // If we are already inside "use asm" that means we are either actively
michael@0 2471 // compiling or we are reparsing after asm.js validation failure. In either
michael@0 2472 // case, nothing to do here.
michael@0 2473 if (pc->useAsmOrInsideUseAsm())
michael@0 2474 return true;
michael@0 2475
michael@0 2476 // If there is no ScriptSource, then we are doing a non-compiling parse and
michael@0 2477 // so we shouldn't (and can't, without a ScriptSource) compile.
michael@0 2478 if (ss == nullptr)
michael@0 2479 return true;
michael@0 2480
michael@0 2481 pc->sc->asFunctionBox()->useAsm = true;
michael@0 2482
michael@0 2483 #ifdef JS_ION
michael@0 2484 // Attempt to validate and compile this asm.js module. On success, the
michael@0 2485 // tokenStream has been advanced to the closing }. On failure, the
michael@0 2486 // tokenStream is in an indeterminate state and we must reparse the
michael@0 2487 // function from the beginning. Reparsing is triggered by marking that a
michael@0 2488 // new directive has been encountered and returning 'false'.
michael@0 2489 bool validated;
michael@0 2490 if (!CompileAsmJS(context, *this, list, &validated))
michael@0 2491 return false;
michael@0 2492 if (!validated) {
michael@0 2493 pc->newDirectives->setAsmJS();
michael@0 2494 return false;
michael@0 2495 }
michael@0 2496 #endif
michael@0 2497
michael@0 2498 return true;
michael@0 2499 }
michael@0 2500
michael@0 2501 /*
michael@0 2502 * Recognize Directive Prologue members and directives. Assuming |pn| is a
michael@0 2503 * candidate for membership in a directive prologue, recognize directives and
michael@0 2504 * set |pc|'s flags accordingly. If |pn| is indeed part of a prologue, set its
michael@0 2505 * |pn_prologue| flag.
michael@0 2506 *
michael@0 2507 * Note that the following is a strict mode function:
michael@0 2508 *
michael@0 2509 * function foo() {
michael@0 2510 * "blah" // inserted semi colon
michael@0 2511 * "blurgh"
michael@0 2512 * "use\x20loose"
michael@0 2513 * "use strict"
michael@0 2514 * }
michael@0 2515 *
michael@0 2516 * That is, even though "use\x20loose" can never be a directive, now or in the
michael@0 2517 * future (because of the hex escape), the Directive Prologue extends through it
michael@0 2518 * to the "use strict" statement, which is indeed a directive.
michael@0 2519 */
michael@0 2520 template <typename ParseHandler>
michael@0 2521 bool
michael@0 2522 Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool *cont)
michael@0 2523 {
michael@0 2524 TokenPos directivePos;
michael@0 2525 JSAtom *directive = handler.isStringExprStatement(pn, &directivePos);
michael@0 2526
michael@0 2527 *cont = !!directive;
michael@0 2528 if (!*cont)
michael@0 2529 return true;
michael@0 2530
michael@0 2531 if (IsEscapeFreeStringLiteral(directivePos, directive)) {
michael@0 2532 // Mark this statement as being a possibly legitimate part of a
michael@0 2533 // directive prologue, so the bytecode emitter won't warn about it being
michael@0 2534 // useless code. (We mustn't just omit the statement entirely yet, as it
michael@0 2535 // could be producing the value of an eval or JSScript execution.)
michael@0 2536 //
michael@0 2537 // Note that even if the string isn't one we recognize as a directive,
michael@0 2538 // the emitter still shouldn't flag it as useless, as it could become a
michael@0 2539 // directive in the future. We don't want to interfere with people
michael@0 2540 // taking advantage of directive-prologue-enabled features that appear
michael@0 2541 // in other browsers first.
michael@0 2542 handler.setPrologue(pn);
michael@0 2543
michael@0 2544 if (directive == context->names().useStrict) {
michael@0 2545 // We're going to be in strict mode. Note that this scope explicitly
michael@0 2546 // had "use strict";
michael@0 2547 pc->sc->setExplicitUseStrict();
michael@0 2548 if (!pc->sc->strict) {
michael@0 2549 if (pc->sc->isFunctionBox()) {
michael@0 2550 // Request that this function be reparsed as strict.
michael@0 2551 pc->newDirectives->setStrict();
michael@0 2552 return false;
michael@0 2553 } else {
michael@0 2554 // We don't reparse global scopes, so we keep track of the
michael@0 2555 // one possible strict violation that could occur in the
michael@0 2556 // directive prologue -- octal escapes -- and complain now.
michael@0 2557 if (tokenStream.sawOctalEscape()) {
michael@0 2558 report(ParseError, false, null(), JSMSG_DEPRECATED_OCTAL);
michael@0 2559 return false;
michael@0 2560 }
michael@0 2561 pc->sc->strict = true;
michael@0 2562 }
michael@0 2563 }
michael@0 2564 } else if (directive == context->names().useAsm) {
michael@0 2565 if (pc->sc->isFunctionBox())
michael@0 2566 return asmJS(list);
michael@0 2567 return report(ParseWarning, false, pn, JSMSG_USE_ASM_DIRECTIVE_FAIL);
michael@0 2568 }
michael@0 2569 }
michael@0 2570 return true;
michael@0 2571 }
michael@0 2572
michael@0 2573 /*
michael@0 2574 * Parse the statements in a block, creating a StatementList node that lists
michael@0 2575 * the statements. If called from block-parsing code, the caller must match
michael@0 2576 * '{' before and '}' after.
michael@0 2577 */
michael@0 2578 template <typename ParseHandler>
michael@0 2579 typename ParseHandler::Node
michael@0 2580 Parser<ParseHandler>::statements()
michael@0 2581 {
michael@0 2582 JS_CHECK_RECURSION(context, return null());
michael@0 2583
michael@0 2584 Node pn = handler.newStatementList(pc->blockid(), pos());
michael@0 2585 if (!pn)
michael@0 2586 return null();
michael@0 2587
michael@0 2588 Node saveBlock = pc->blockNode;
michael@0 2589 pc->blockNode = pn;
michael@0 2590
michael@0 2591 bool canHaveDirectives = pc->atBodyLevel();
michael@0 2592 for (;;) {
michael@0 2593 TokenKind tt = tokenStream.peekToken(TokenStream::Operand);
michael@0 2594 if (tt <= TOK_EOF || tt == TOK_RC) {
michael@0 2595 if (tt == TOK_ERROR) {
michael@0 2596 if (tokenStream.isEOF())
michael@0 2597 isUnexpectedEOF_ = true;
michael@0 2598 return null();
michael@0 2599 }
michael@0 2600 break;
michael@0 2601 }
michael@0 2602 Node next = statement(canHaveDirectives);
michael@0 2603 if (!next) {
michael@0 2604 if (tokenStream.isEOF())
michael@0 2605 isUnexpectedEOF_ = true;
michael@0 2606 return null();
michael@0 2607 }
michael@0 2608
michael@0 2609 if (canHaveDirectives) {
michael@0 2610 if (!maybeParseDirective(pn, next, &canHaveDirectives))
michael@0 2611 return null();
michael@0 2612 }
michael@0 2613
michael@0 2614 handler.addStatementToList(pn, next, pc);
michael@0 2615 }
michael@0 2616
michael@0 2617 /*
michael@0 2618 * Handle the case where there was a let declaration under this block. If
michael@0 2619 * it replaced pc->blockNode with a new block node then we must refresh pn
michael@0 2620 * and then restore pc->blockNode.
michael@0 2621 */
michael@0 2622 if (pc->blockNode != pn)
michael@0 2623 pn = pc->blockNode;
michael@0 2624 pc->blockNode = saveBlock;
michael@0 2625 return pn;
michael@0 2626 }
michael@0 2627
michael@0 2628 template <typename ParseHandler>
michael@0 2629 typename ParseHandler::Node
michael@0 2630 Parser<ParseHandler>::condition()
michael@0 2631 {
michael@0 2632 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
michael@0 2633 Node pn = exprInParens();
michael@0 2634 if (!pn)
michael@0 2635 return null();
michael@0 2636 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
michael@0 2637
michael@0 2638 /* Check for (a = b) and warn about possible (a == b) mistype. */
michael@0 2639 if (handler.isOperationWithoutParens(pn, PNK_ASSIGN) &&
michael@0 2640 !report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
michael@0 2641 {
michael@0 2642 return null();
michael@0 2643 }
michael@0 2644 return pn;
michael@0 2645 }
michael@0 2646
michael@0 2647 template <typename ParseHandler>
michael@0 2648 bool
michael@0 2649 Parser<ParseHandler>::matchLabel(MutableHandle<PropertyName*> label)
michael@0 2650 {
michael@0 2651 TokenKind tt = tokenStream.peekTokenSameLine(TokenStream::Operand);
michael@0 2652 if (tt == TOK_ERROR)
michael@0 2653 return false;
michael@0 2654 if (tt == TOK_NAME) {
michael@0 2655 tokenStream.consumeKnownToken(TOK_NAME);
michael@0 2656 label.set(tokenStream.currentName());
michael@0 2657 } else if (tt == TOK_YIELD) {
michael@0 2658 tokenStream.consumeKnownToken(TOK_YIELD);
michael@0 2659 if (!checkYieldNameValidity())
michael@0 2660 return false;
michael@0 2661 label.set(tokenStream.currentName());
michael@0 2662 } else {
michael@0 2663 label.set(nullptr);
michael@0 2664 }
michael@0 2665 return true;
michael@0 2666 }
michael@0 2667
michael@0 2668 template <typename ParseHandler>
michael@0 2669 bool
michael@0 2670 Parser<ParseHandler>::reportRedeclaration(Node pn, bool isConst, JSAtom *atom)
michael@0 2671 {
michael@0 2672 JSAutoByteString name;
michael@0 2673 if (AtomToPrintableString(context, atom, &name))
michael@0 2674 report(ParseError, false, pn, JSMSG_REDECLARED_VAR, isConst ? "const" : "variable", name.ptr());
michael@0 2675 return false;
michael@0 2676 }
michael@0 2677
michael@0 2678 /*
michael@0 2679 * Define a let-variable in a block, let-expression, or comprehension scope. pc
michael@0 2680 * must already be in such a scope.
michael@0 2681 *
michael@0 2682 * Throw a SyntaxError if 'atom' is an invalid name. Otherwise create a
michael@0 2683 * property for the new variable on the block object, pc->staticScope;
michael@0 2684 * populate data->pn->pn_{op,cookie,defn,dflags}; and stash a pointer to
michael@0 2685 * data->pn in a slot of the block object.
michael@0 2686 */
michael@0 2687 template <>
michael@0 2688 /* static */ bool
michael@0 2689 Parser<FullParseHandler>::bindLet(BindData<FullParseHandler> *data,
michael@0 2690 HandlePropertyName name, Parser<FullParseHandler> *parser)
michael@0 2691 {
michael@0 2692 ParseContext<FullParseHandler> *pc = parser->pc;
michael@0 2693 ParseNode *pn = data->pn;
michael@0 2694 if (!parser->checkStrictBinding(name, pn))
michael@0 2695 return false;
michael@0 2696
michael@0 2697 ExclusiveContext *cx = parser->context;
michael@0 2698
michael@0 2699 Rooted<StaticBlockObject *> blockObj(cx, data->let.blockObj);
michael@0 2700 unsigned index = blockObj->numVariables();
michael@0 2701 if (index >= StaticBlockObject::LOCAL_INDEX_LIMIT) {
michael@0 2702 parser->report(ParseError, false, pn, data->let.overflow);
michael@0 2703 return false;
michael@0 2704 }
michael@0 2705
michael@0 2706 /*
michael@0 2707 * Assign block-local index to pn->pn_cookie right away, encoding it as an
michael@0 2708 * upvar cookie whose skip tells the current static level. The emitter will
michael@0 2709 * adjust the node's slot based on its stack depth model -- and, for global
michael@0 2710 * and eval code, js::frontend::CompileScript will adjust the slot
michael@0 2711 * again to include script->nfixed.
michael@0 2712 */
michael@0 2713 if (!pn->pn_cookie.set(parser->tokenStream, pc->staticLevel, index))
michael@0 2714 return false;
michael@0 2715
michael@0 2716 /*
michael@0 2717 * For bindings that are hoisted to the beginning of the block/function,
michael@0 2718 * define() right now. Otherwise, delay define until PushLetScope.
michael@0 2719 */
michael@0 2720 if (data->let.varContext == HoistVars) {
michael@0 2721 JS_ASSERT(!pc->atBodyLevel());
michael@0 2722 Definition *dn = pc->decls().lookupFirst(name);
michael@0 2723 if (dn && dn->pn_blockid == pc->blockid())
michael@0 2724 return parser->reportRedeclaration(pn, dn->isConst(), name);
michael@0 2725 if (!pc->define(parser->tokenStream, name, pn, Definition::LET))
michael@0 2726 return false;
michael@0 2727 }
michael@0 2728
michael@0 2729 bool redeclared;
michael@0 2730 RootedId id(cx, NameToId(name));
michael@0 2731 RootedShape shape(cx, StaticBlockObject::addVar(cx, blockObj, id, index, &redeclared));
michael@0 2732 if (!shape) {
michael@0 2733 if (redeclared)
michael@0 2734 parser->reportRedeclaration(pn, false, name);
michael@0 2735 return false;
michael@0 2736 }
michael@0 2737
michael@0 2738 /* Store pn in the static block object. */
michael@0 2739 blockObj->setDefinitionParseNode(index, reinterpret_cast<Definition *>(pn));
michael@0 2740 return true;
michael@0 2741 }
michael@0 2742
michael@0 2743 template <>
michael@0 2744 /* static */ bool
michael@0 2745 Parser<SyntaxParseHandler>::bindLet(BindData<SyntaxParseHandler> *data,
michael@0 2746 HandlePropertyName name, Parser<SyntaxParseHandler> *parser)
michael@0 2747 {
michael@0 2748 if (!parser->checkStrictBinding(name, data->pn))
michael@0 2749 return false;
michael@0 2750
michael@0 2751 return true;
michael@0 2752 }
michael@0 2753
michael@0 2754 template <typename ParseHandler, class Op>
michael@0 2755 static inline bool
michael@0 2756 ForEachLetDef(TokenStream &ts, ParseContext<ParseHandler> *pc,
michael@0 2757 HandleStaticBlockObject blockObj, Op op)
michael@0 2758 {
michael@0 2759 for (Shape::Range<CanGC> r(ts.context(), blockObj->lastProperty()); !r.empty(); r.popFront()) {
michael@0 2760 Shape &shape = r.front();
michael@0 2761
michael@0 2762 /* Beware the destructuring dummy slots. */
michael@0 2763 if (JSID_IS_INT(shape.propid()))
michael@0 2764 continue;
michael@0 2765
michael@0 2766 if (!op(ts, pc, blockObj, shape, JSID_TO_ATOM(shape.propid())))
michael@0 2767 return false;
michael@0 2768 }
michael@0 2769 return true;
michael@0 2770 }
michael@0 2771
michael@0 2772 template <typename ParseHandler>
michael@0 2773 struct PopLetDecl {
michael@0 2774 bool operator()(TokenStream &, ParseContext<ParseHandler> *pc, HandleStaticBlockObject,
michael@0 2775 const Shape &, JSAtom *atom)
michael@0 2776 {
michael@0 2777 pc->popLetDecl(atom);
michael@0 2778 return true;
michael@0 2779 }
michael@0 2780 };
michael@0 2781
michael@0 2782 // We compute the maximum block scope depth, in slots, of a compilation unit at
michael@0 2783 // parse-time. Each nested statement has a field indicating the maximum block
michael@0 2784 // scope depth that is nested inside it. When we leave a nested statement, we
michael@0 2785 // add the number of slots in the statement to the nested depth, and use that to
michael@0 2786 // update the maximum block scope depth of the outer statement or parse
michael@0 2787 // context. In the end, pc->blockScopeDepth will indicate the number of slots
michael@0 2788 // to reserve in the fixed part of a stack frame.
michael@0 2789 //
michael@0 2790 template <typename ParseHandler>
michael@0 2791 static void
michael@0 2792 AccumulateBlockScopeDepth(ParseContext<ParseHandler> *pc)
michael@0 2793 {
michael@0 2794 uint32_t innerDepth = pc->topStmt->innerBlockScopeDepth;
michael@0 2795 StmtInfoPC *outer = pc->topStmt->down;
michael@0 2796
michael@0 2797 if (pc->topStmt->isBlockScope)
michael@0 2798 innerDepth += pc->topStmt->staticScope->template as<StaticBlockObject>().numVariables();
michael@0 2799
michael@0 2800 if (outer) {
michael@0 2801 if (outer->innerBlockScopeDepth < innerDepth)
michael@0 2802 outer->innerBlockScopeDepth = innerDepth;
michael@0 2803 } else {
michael@0 2804 if (pc->blockScopeDepth < innerDepth)
michael@0 2805 pc->blockScopeDepth = innerDepth;
michael@0 2806 }
michael@0 2807 }
michael@0 2808
michael@0 2809 template <typename ParseHandler>
michael@0 2810 static void
michael@0 2811 PopStatementPC(TokenStream &ts, ParseContext<ParseHandler> *pc)
michael@0 2812 {
michael@0 2813 RootedNestedScopeObject scopeObj(ts.context(), pc->topStmt->staticScope);
michael@0 2814 JS_ASSERT(!!scopeObj == pc->topStmt->isNestedScope);
michael@0 2815
michael@0 2816 AccumulateBlockScopeDepth(pc);
michael@0 2817 FinishPopStatement(pc);
michael@0 2818
michael@0 2819 if (scopeObj) {
michael@0 2820 if (scopeObj->is<StaticBlockObject>()) {
michael@0 2821 RootedStaticBlockObject blockObj(ts.context(), &scopeObj->as<StaticBlockObject>());
michael@0 2822 JS_ASSERT(!blockObj->inDictionaryMode());
michael@0 2823 ForEachLetDef(ts, pc, blockObj, PopLetDecl<ParseHandler>());
michael@0 2824 }
michael@0 2825 scopeObj->resetEnclosingNestedScopeFromParser();
michael@0 2826 }
michael@0 2827 }
michael@0 2828
michael@0 2829 /*
michael@0 2830 * The function LexicalLookup searches a static binding for the given name in
michael@0 2831 * the stack of statements enclosing the statement currently being parsed. Each
michael@0 2832 * statement that introduces a new scope has a corresponding scope object, on
michael@0 2833 * which the bindings for that scope are stored. LexicalLookup either returns
michael@0 2834 * the innermost statement which has a scope object containing a binding with
michael@0 2835 * the given name, or nullptr.
michael@0 2836 */
michael@0 2837 template <class ContextT>
michael@0 2838 typename ContextT::StmtInfo *
michael@0 2839 LexicalLookup(ContextT *ct, HandleAtom atom, int *slotp, typename ContextT::StmtInfo *stmt)
michael@0 2840 {
michael@0 2841 RootedId id(ct->sc->context, AtomToId(atom));
michael@0 2842
michael@0 2843 if (!stmt)
michael@0 2844 stmt = ct->topScopeStmt;
michael@0 2845 for (; stmt; stmt = stmt->downScope) {
michael@0 2846 /*
michael@0 2847 * With-statements introduce dynamic bindings. Since dynamic bindings
michael@0 2848 * can potentially override any static bindings introduced by statements
michael@0 2849 * further up the stack, we have to abort the search.
michael@0 2850 */
michael@0 2851 if (stmt->type == STMT_WITH)
michael@0 2852 break;
michael@0 2853
michael@0 2854 // Skip statements that do not introduce a new scope
michael@0 2855 if (!stmt->isBlockScope)
michael@0 2856 continue;
michael@0 2857
michael@0 2858 StaticBlockObject &blockObj = stmt->staticBlock();
michael@0 2859 Shape *shape = blockObj.nativeLookup(ct->sc->context, id);
michael@0 2860 if (shape) {
michael@0 2861 if (slotp)
michael@0 2862 *slotp = blockObj.shapeToIndex(*shape);
michael@0 2863 return stmt;
michael@0 2864 }
michael@0 2865 }
michael@0 2866
michael@0 2867 if (slotp)
michael@0 2868 *slotp = -1;
michael@0 2869 return stmt;
michael@0 2870 }
michael@0 2871
michael@0 2872 template <typename ParseHandler>
michael@0 2873 static inline bool
michael@0 2874 OuterLet(ParseContext<ParseHandler> *pc, StmtInfoPC *stmt, HandleAtom atom)
michael@0 2875 {
michael@0 2876 while (stmt->downScope) {
michael@0 2877 stmt = LexicalLookup(pc, atom, nullptr, stmt->downScope);
michael@0 2878 if (!stmt)
michael@0 2879 return false;
michael@0 2880 if (stmt->type == STMT_BLOCK)
michael@0 2881 return true;
michael@0 2882 }
michael@0 2883 return false;
michael@0 2884 }
michael@0 2885
michael@0 2886 template <typename ParseHandler>
michael@0 2887 /* static */ bool
michael@0 2888 Parser<ParseHandler>::bindVarOrConst(BindData<ParseHandler> *data,
michael@0 2889 HandlePropertyName name, Parser<ParseHandler> *parser)
michael@0 2890 {
michael@0 2891 ExclusiveContext *cx = parser->context;
michael@0 2892 ParseContext<ParseHandler> *pc = parser->pc;
michael@0 2893 Node pn = data->pn;
michael@0 2894 bool isConstDecl = data->op == JSOP_DEFCONST;
michael@0 2895
michael@0 2896 /* Default best op for pn is JSOP_NAME; we'll try to improve below. */
michael@0 2897 parser->handler.setOp(pn, JSOP_NAME);
michael@0 2898
michael@0 2899 if (!parser->checkStrictBinding(name, pn))
michael@0 2900 return false;
michael@0 2901
michael@0 2902 StmtInfoPC *stmt = LexicalLookup(pc, name, nullptr, (StmtInfoPC *)nullptr);
michael@0 2903
michael@0 2904 if (stmt && stmt->type == STMT_WITH) {
michael@0 2905 parser->handler.setFlag(pn, PND_DEOPTIMIZED);
michael@0 2906 if (pc->sc->isFunctionBox()) {
michael@0 2907 FunctionBox *funbox = pc->sc->asFunctionBox();
michael@0 2908 funbox->setMightAliasLocals();
michael@0 2909 }
michael@0 2910
michael@0 2911 /*
michael@0 2912 * This definition isn't being added to the parse context's
michael@0 2913 * declarations, so make sure to indicate the need to deoptimize
michael@0 2914 * the script's arguments object. Mark the function as if it
michael@0 2915 * contained a debugger statement, which will deoptimize arguments
michael@0 2916 * as much as possible.
michael@0 2917 */
michael@0 2918 if (name == cx->names().arguments)
michael@0 2919 pc->sc->setHasDebuggerStatement();
michael@0 2920
michael@0 2921 return true;
michael@0 2922 }
michael@0 2923
michael@0 2924 DefinitionList::Range defs = pc->decls().lookupMulti(name);
michael@0 2925 JS_ASSERT_IF(stmt, !defs.empty());
michael@0 2926
michael@0 2927 if (defs.empty()) {
michael@0 2928 return pc->define(parser->tokenStream, name, pn,
michael@0 2929 isConstDecl ? Definition::CONST : Definition::VAR);
michael@0 2930 }
michael@0 2931
michael@0 2932 /*
michael@0 2933 * There was a previous declaration with the same name. The standard
michael@0 2934 * disallows several forms of redeclaration. Critically,
michael@0 2935 * let (x) { var x; } // error
michael@0 2936 * is not allowed which allows us to turn any non-error redeclaration
michael@0 2937 * into a use of the initial declaration.
michael@0 2938 */
michael@0 2939 DefinitionNode dn = defs.front<ParseHandler>();
michael@0 2940 Definition::Kind dn_kind = parser->handler.getDefinitionKind(dn);
michael@0 2941 if (dn_kind == Definition::ARG) {
michael@0 2942 JSAutoByteString bytes;
michael@0 2943 if (!AtomToPrintableString(cx, name, &bytes))
michael@0 2944 return false;
michael@0 2945
michael@0 2946 if (isConstDecl) {
michael@0 2947 parser->report(ParseError, false, pn, JSMSG_REDECLARED_PARAM, bytes.ptr());
michael@0 2948 return false;
michael@0 2949 }
michael@0 2950 if (!parser->report(ParseExtraWarning, false, pn, JSMSG_VAR_HIDES_ARG, bytes.ptr()))
michael@0 2951 return false;
michael@0 2952 } else {
michael@0 2953 bool error = (isConstDecl ||
michael@0 2954 dn_kind == Definition::CONST ||
michael@0 2955 (dn_kind == Definition::LET &&
michael@0 2956 (stmt->type != STMT_CATCH || OuterLet(pc, stmt, name))));
michael@0 2957
michael@0 2958 if (parser->options().extraWarningsOption
michael@0 2959 ? data->op != JSOP_DEFVAR || dn_kind != Definition::VAR
michael@0 2960 : error)
michael@0 2961 {
michael@0 2962 JSAutoByteString bytes;
michael@0 2963 ParseReportKind reporter = error ? ParseError : ParseExtraWarning;
michael@0 2964 if (!AtomToPrintableString(cx, name, &bytes) ||
michael@0 2965 !parser->report(reporter, false, pn, JSMSG_REDECLARED_VAR,
michael@0 2966 Definition::kindString(dn_kind), bytes.ptr()))
michael@0 2967 {
michael@0 2968 return false;
michael@0 2969 }
michael@0 2970 }
michael@0 2971 }
michael@0 2972
michael@0 2973 parser->handler.linkUseToDef(pn, dn);
michael@0 2974 return true;
michael@0 2975 }
michael@0 2976
michael@0 2977 template <>
michael@0 2978 bool
michael@0 2979 Parser<FullParseHandler>::makeSetCall(ParseNode *pn, unsigned msg)
michael@0 2980 {
michael@0 2981 JS_ASSERT(pn->isKind(PNK_CALL));
michael@0 2982 JS_ASSERT(pn->isArity(PN_LIST));
michael@0 2983 JS_ASSERT(pn->isOp(JSOP_CALL) || pn->isOp(JSOP_SPREADCALL) ||
michael@0 2984 pn->isOp(JSOP_EVAL) || pn->isOp(JSOP_SPREADEVAL) ||
michael@0 2985 pn->isOp(JSOP_FUNCALL) || pn->isOp(JSOP_FUNAPPLY));
michael@0 2986
michael@0 2987 if (!report(ParseStrictError, pc->sc->strict, pn, msg))
michael@0 2988 return false;
michael@0 2989 handler.markAsSetCall(pn);
michael@0 2990 return true;
michael@0 2991 }
michael@0 2992
michael@0 2993 template <typename ParseHandler>
michael@0 2994 bool
michael@0 2995 Parser<ParseHandler>::noteNameUse(HandlePropertyName name, Node pn)
michael@0 2996 {
michael@0 2997 StmtInfoPC *stmt = LexicalLookup(pc, name, nullptr, (StmtInfoPC *)nullptr);
michael@0 2998
michael@0 2999 DefinitionList::Range defs = pc->decls().lookupMulti(name);
michael@0 3000
michael@0 3001 DefinitionNode dn;
michael@0 3002 if (!defs.empty()) {
michael@0 3003 dn = defs.front<ParseHandler>();
michael@0 3004 } else {
michael@0 3005 /*
michael@0 3006 * No definition before this use in any lexical scope.
michael@0 3007 * Create a placeholder definition node to either:
michael@0 3008 * - Be adopted when we parse the real defining
michael@0 3009 * declaration, or
michael@0 3010 * - Be left as a free variable definition if we never
michael@0 3011 * see the real definition.
michael@0 3012 */
michael@0 3013 dn = getOrCreateLexicalDependency(pc, name);
michael@0 3014 if (!dn)
michael@0 3015 return false;
michael@0 3016 }
michael@0 3017
michael@0 3018 handler.linkUseToDef(pn, dn);
michael@0 3019
michael@0 3020 if (stmt && stmt->type == STMT_WITH)
michael@0 3021 handler.setFlag(pn, PND_DEOPTIMIZED);
michael@0 3022
michael@0 3023 return true;
michael@0 3024 }
michael@0 3025
michael@0 3026 template <>
michael@0 3027 bool
michael@0 3028 Parser<FullParseHandler>::bindDestructuringVar(BindData<FullParseHandler> *data, ParseNode *pn)
michael@0 3029 {
michael@0 3030 JS_ASSERT(pn->isKind(PNK_NAME));
michael@0 3031
michael@0 3032 RootedPropertyName name(context, pn->pn_atom->asPropertyName());
michael@0 3033
michael@0 3034 data->pn = pn;
michael@0 3035 if (!data->binder(data, name, this))
michael@0 3036 return false;
michael@0 3037
michael@0 3038 /*
michael@0 3039 * Select the appropriate name-setting opcode, respecting eager selection
michael@0 3040 * done by the data->binder function.
michael@0 3041 */
michael@0 3042 if (pn->pn_dflags & PND_BOUND)
michael@0 3043 pn->setOp(JSOP_SETLOCAL);
michael@0 3044 else if (data->op == JSOP_DEFCONST)
michael@0 3045 pn->setOp(JSOP_SETCONST);
michael@0 3046 else
michael@0 3047 pn->setOp(JSOP_SETNAME);
michael@0 3048
michael@0 3049 if (data->op == JSOP_DEFCONST)
michael@0 3050 pn->pn_dflags |= PND_CONST;
michael@0 3051
michael@0 3052 pn->markAsAssigned();
michael@0 3053 return true;
michael@0 3054 }
michael@0 3055
michael@0 3056 /*
michael@0 3057 * Destructuring patterns can appear in two kinds of contexts:
michael@0 3058 *
michael@0 3059 * - assignment-like: assignment expressions and |for| loop heads. In
michael@0 3060 * these cases, the patterns' property value positions can be
michael@0 3061 * arbitrary lvalue expressions; the destructuring is just a fancy
michael@0 3062 * assignment.
michael@0 3063 *
michael@0 3064 * - declaration-like: |var| and |let| declarations, functions' formal
michael@0 3065 * parameter lists, |catch| clauses, and comprehension tails. In
michael@0 3066 * these cases, the patterns' property value positions must be
michael@0 3067 * simple names; the destructuring defines them as new variables.
michael@0 3068 *
michael@0 3069 * In both cases, other code parses the pattern as an arbitrary
michael@0 3070 * primaryExpr, and then, here in CheckDestructuring, verify that the
michael@0 3071 * tree is a valid destructuring expression.
michael@0 3072 *
michael@0 3073 * In assignment-like contexts, we parse the pattern with
michael@0 3074 * pc->inDeclDestructuring clear, so the lvalue expressions in the
michael@0 3075 * pattern are parsed normally. primaryExpr links variable references
michael@0 3076 * into the appropriate use chains; creates placeholder definitions;
michael@0 3077 * and so on. CheckDestructuring is called with |data| nullptr (since
michael@0 3078 * we won't be binding any new names), and we specialize lvalues as
michael@0 3079 * appropriate.
michael@0 3080 *
michael@0 3081 * In declaration-like contexts, the normal variable reference
michael@0 3082 * processing would just be an obstruction, because we're going to
michael@0 3083 * define the names that appear in the property value positions as new
michael@0 3084 * variables anyway. In this case, we parse the pattern with
michael@0 3085 * pc->inDeclDestructuring set, which directs primaryExpr to leave
michael@0 3086 * whatever name nodes it creates unconnected. Then, here in
michael@0 3087 * CheckDestructuring, we require the pattern's property value
michael@0 3088 * positions to be simple names, and define them as appropriate to the
michael@0 3089 * context. For these calls, |data| points to the right sort of
michael@0 3090 * BindData.
michael@0 3091 *
michael@0 3092 * The 'toplevel' is a private detail of the recursive strategy used by
michael@0 3093 * CheckDestructuring and callers should use the default value.
michael@0 3094 */
michael@0 3095 template <>
michael@0 3096 bool
michael@0 3097 Parser<FullParseHandler>::checkDestructuring(BindData<FullParseHandler> *data,
michael@0 3098 ParseNode *left, bool toplevel)
michael@0 3099 {
michael@0 3100 bool ok;
michael@0 3101
michael@0 3102 if (left->isKind(PNK_ARRAYCOMP)) {
michael@0 3103 report(ParseError, false, left, JSMSG_ARRAY_COMP_LEFTSIDE);
michael@0 3104 return false;
michael@0 3105 }
michael@0 3106
michael@0 3107 Rooted<StaticBlockObject *> blockObj(context);
michael@0 3108 blockObj = data && data->binder == bindLet ? data->let.blockObj.get() : nullptr;
michael@0 3109
michael@0 3110 if (left->isKind(PNK_ARRAY)) {
michael@0 3111 for (ParseNode *pn = left->pn_head; pn; pn = pn->pn_next) {
michael@0 3112 if (!pn->isKind(PNK_ELISION)) {
michael@0 3113 if (pn->isKind(PNK_ARRAY) || pn->isKind(PNK_OBJECT)) {
michael@0 3114 ok = checkDestructuring(data, pn, false);
michael@0 3115 } else {
michael@0 3116 if (data) {
michael@0 3117 if (!pn->isKind(PNK_NAME)) {
michael@0 3118 report(ParseError, false, pn, JSMSG_NO_VARIABLE_NAME);
michael@0 3119 return false;
michael@0 3120 }
michael@0 3121 ok = bindDestructuringVar(data, pn);
michael@0 3122 } else {
michael@0 3123 ok = checkAndMarkAsAssignmentLhs(pn, KeyedDestructuringAssignment);
michael@0 3124 }
michael@0 3125 }
michael@0 3126 if (!ok)
michael@0 3127 return false;
michael@0 3128 }
michael@0 3129 }
michael@0 3130 } else {
michael@0 3131 JS_ASSERT(left->isKind(PNK_OBJECT));
michael@0 3132 for (ParseNode *member = left->pn_head; member; member = member->pn_next) {
michael@0 3133 MOZ_ASSERT(member->isKind(PNK_COLON));
michael@0 3134 ParseNode *expr = member->pn_right;
michael@0 3135
michael@0 3136 if (expr->isKind(PNK_ARRAY) || expr->isKind(PNK_OBJECT)) {
michael@0 3137 ok = checkDestructuring(data, expr, false);
michael@0 3138 } else if (data) {
michael@0 3139 if (!expr->isKind(PNK_NAME)) {
michael@0 3140 report(ParseError, false, expr, JSMSG_NO_VARIABLE_NAME);
michael@0 3141 return false;
michael@0 3142 }
michael@0 3143 ok = bindDestructuringVar(data, expr);
michael@0 3144 } else {
michael@0 3145 /*
michael@0 3146 * If this is a destructuring shorthand ({x} = ...), then
michael@0 3147 * identifierName wasn't used to parse |x|. As a result, |x|
michael@0 3148 * hasn't been officially linked to its def or registered in
michael@0 3149 * lexdeps. Do that now.
michael@0 3150 */
michael@0 3151 if (member->pn_right == member->pn_left) {
michael@0 3152 RootedPropertyName name(context, expr->pn_atom->asPropertyName());
michael@0 3153 if (!noteNameUse(name, expr))
michael@0 3154 return false;
michael@0 3155 }
michael@0 3156 ok = checkAndMarkAsAssignmentLhs(expr, KeyedDestructuringAssignment);
michael@0 3157 }
michael@0 3158 if (!ok)
michael@0 3159 return false;
michael@0 3160 }
michael@0 3161 }
michael@0 3162
michael@0 3163 return true;
michael@0 3164 }
michael@0 3165
michael@0 3166 template <>
michael@0 3167 bool
michael@0 3168 Parser<SyntaxParseHandler>::checkDestructuring(BindData<SyntaxParseHandler> *data,
michael@0 3169 Node left, bool toplevel)
michael@0 3170 {
michael@0 3171 return abortIfSyntaxParser();
michael@0 3172 }
michael@0 3173
michael@0 3174 template <typename ParseHandler>
michael@0 3175 typename ParseHandler::Node
michael@0 3176 Parser<ParseHandler>::destructuringExpr(BindData<ParseHandler> *data, TokenKind tt)
michael@0 3177 {
michael@0 3178 JS_ASSERT(tokenStream.isCurrentTokenType(tt));
michael@0 3179
michael@0 3180 pc->inDeclDestructuring = true;
michael@0 3181 Node pn = primaryExpr(tt);
michael@0 3182 pc->inDeclDestructuring = false;
michael@0 3183 if (!pn)
michael@0 3184 return null();
michael@0 3185 if (!checkDestructuring(data, pn))
michael@0 3186 return null();
michael@0 3187 return pn;
michael@0 3188 }
michael@0 3189
michael@0 3190 template <typename ParseHandler>
michael@0 3191 typename ParseHandler::Node
michael@0 3192 Parser<ParseHandler>::pushLexicalScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt)
michael@0 3193 {
michael@0 3194 JS_ASSERT(blockObj);
michael@0 3195
michael@0 3196 ObjectBox *blockbox = newObjectBox(blockObj);
michael@0 3197 if (!blockbox)
michael@0 3198 return null();
michael@0 3199
michael@0 3200 PushStatementPC(pc, stmt, STMT_BLOCK);
michael@0 3201 blockObj->initEnclosingNestedScopeFromParser(pc->staticScope);
michael@0 3202 FinishPushNestedScope(pc, stmt, *blockObj.get());
michael@0 3203 stmt->isBlockScope = true;
michael@0 3204
michael@0 3205 Node pn = handler.newLexicalScope(blockbox);
michael@0 3206 if (!pn)
michael@0 3207 return null();
michael@0 3208
michael@0 3209 if (!GenerateBlockId(tokenStream, pc, stmt->blockid))
michael@0 3210 return null();
michael@0 3211 handler.setBlockId(pn, stmt->blockid);
michael@0 3212 return pn;
michael@0 3213 }
michael@0 3214
michael@0 3215 template <typename ParseHandler>
michael@0 3216 typename ParseHandler::Node
michael@0 3217 Parser<ParseHandler>::pushLexicalScope(StmtInfoPC *stmt)
michael@0 3218 {
michael@0 3219 RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context));
michael@0 3220 if (!blockObj)
michael@0 3221 return null();
michael@0 3222
michael@0 3223 return pushLexicalScope(blockObj, stmt);
michael@0 3224 }
michael@0 3225
michael@0 3226 struct AddLetDecl
michael@0 3227 {
michael@0 3228 uint32_t blockid;
michael@0 3229
michael@0 3230 AddLetDecl(uint32_t blockid) : blockid(blockid) {}
michael@0 3231
michael@0 3232 bool operator()(TokenStream &ts, ParseContext<FullParseHandler> *pc,
michael@0 3233 HandleStaticBlockObject blockObj, const Shape &shape, JSAtom *)
michael@0 3234 {
michael@0 3235 ParseNode *def = (ParseNode *) blockObj->getSlot(shape.slot()).toPrivate();
michael@0 3236 def->pn_blockid = blockid;
michael@0 3237 RootedPropertyName name(ts.context(), def->name());
michael@0 3238 return pc->define(ts, name, def, Definition::LET);
michael@0 3239 }
michael@0 3240 };
michael@0 3241
michael@0 3242 template <>
michael@0 3243 ParseNode *
michael@0 3244 Parser<FullParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt)
michael@0 3245 {
michael@0 3246 JS_ASSERT(blockObj);
michael@0 3247 ParseNode *pn = pushLexicalScope(blockObj, stmt);
michael@0 3248 if (!pn)
michael@0 3249 return null();
michael@0 3250
michael@0 3251 pn->pn_dflags |= PND_LET;
michael@0 3252
michael@0 3253 /* Populate the new scope with decls found in the head with updated blockid. */
michael@0 3254 if (!ForEachLetDef(tokenStream, pc, blockObj, AddLetDecl(stmt->blockid)))
michael@0 3255 return null();
michael@0 3256
michael@0 3257 return pn;
michael@0 3258 }
michael@0 3259
michael@0 3260 template <>
michael@0 3261 SyntaxParseHandler::Node
michael@0 3262 Parser<SyntaxParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt)
michael@0 3263 {
michael@0 3264 JS_ALWAYS_FALSE(abortIfSyntaxParser());
michael@0 3265 return SyntaxParseHandler::NodeFailure;
michael@0 3266 }
michael@0 3267
michael@0 3268 /*
michael@0 3269 * Parse a let block statement or let expression (determined by 'letContext').
michael@0 3270 * In both cases, bindings are not hoisted to the top of the enclosing block
michael@0 3271 * and thus must be carefully injected between variables() and the let body.
michael@0 3272 */
michael@0 3273 template <typename ParseHandler>
michael@0 3274 typename ParseHandler::Node
michael@0 3275 Parser<ParseHandler>::letBlock(LetContext letContext)
michael@0 3276 {
michael@0 3277 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LET));
michael@0 3278
michael@0 3279 RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context));
michael@0 3280 if (!blockObj)
michael@0 3281 return null();
michael@0 3282
michael@0 3283 uint32_t begin = pos().begin;
michael@0 3284
michael@0 3285 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET);
michael@0 3286
michael@0 3287 Node vars = variables(PNK_LET, nullptr, blockObj, DontHoistVars);
michael@0 3288 if (!vars)
michael@0 3289 return null();
michael@0 3290
michael@0 3291 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET);
michael@0 3292
michael@0 3293 StmtInfoPC stmtInfo(context);
michael@0 3294 Node block = pushLetScope(blockObj, &stmtInfo);
michael@0 3295 if (!block)
michael@0 3296 return null();
michael@0 3297
michael@0 3298 Node pnlet = handler.newBinary(PNK_LET, vars, block);
michael@0 3299 if (!pnlet)
michael@0 3300 return null();
michael@0 3301 handler.setBeginPosition(pnlet, begin);
michael@0 3302
michael@0 3303 bool needExprStmt = false;
michael@0 3304 if (letContext == LetStatement && !tokenStream.matchToken(TOK_LC, TokenStream::Operand)) {
michael@0 3305 /*
michael@0 3306 * Strict mode eliminates a grammar ambiguity with unparenthesized
michael@0 3307 * LetExpressions in an ExpressionStatement. If followed immediately
michael@0 3308 * by an arguments list, it's ambiguous whether the let expression
michael@0 3309 * is the callee or the call is inside the let expression body.
michael@0 3310 *
michael@0 3311 * See bug 569464.
michael@0 3312 */
michael@0 3313 if (!report(ParseStrictError, pc->sc->strict, pnlet,
michael@0 3314 JSMSG_STRICT_CODE_LET_EXPR_STMT))
michael@0 3315 {
michael@0 3316 return null();
michael@0 3317 }
michael@0 3318
michael@0 3319 /*
michael@0 3320 * If this is really an expression in let statement guise, then we
michael@0 3321 * need to wrap the PNK_LET node in a PNK_SEMI node so that we pop
michael@0 3322 * the return value of the expression.
michael@0 3323 */
michael@0 3324 needExprStmt = true;
michael@0 3325 letContext = LetExpresion;
michael@0 3326 }
michael@0 3327
michael@0 3328 Node expr;
michael@0 3329 if (letContext == LetStatement) {
michael@0 3330 expr = statements();
michael@0 3331 if (!expr)
michael@0 3332 return null();
michael@0 3333 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
michael@0 3334 } else {
michael@0 3335 JS_ASSERT(letContext == LetExpresion);
michael@0 3336 expr = assignExpr();
michael@0 3337 if (!expr)
michael@0 3338 return null();
michael@0 3339 }
michael@0 3340 handler.setLexicalScopeBody(block, expr);
michael@0 3341 PopStatementPC(tokenStream, pc);
michael@0 3342
michael@0 3343 handler.setEndPosition(pnlet, pos().end);
michael@0 3344
michael@0 3345 if (needExprStmt) {
michael@0 3346 if (!MatchOrInsertSemicolon(tokenStream))
michael@0 3347 return null();
michael@0 3348 return handler.newExprStatement(pnlet, pos().end);
michael@0 3349 }
michael@0 3350 return pnlet;
michael@0 3351 }
michael@0 3352
michael@0 3353 template <typename ParseHandler>
michael@0 3354 static bool
michael@0 3355 PushBlocklikeStatement(TokenStream &ts, StmtInfoPC *stmt, StmtType type,
michael@0 3356 ParseContext<ParseHandler> *pc)
michael@0 3357 {
michael@0 3358 PushStatementPC(pc, stmt, type);
michael@0 3359 return GenerateBlockId(ts, pc, stmt->blockid);
michael@0 3360 }
michael@0 3361
michael@0 3362 template <typename ParseHandler>
michael@0 3363 typename ParseHandler::Node
michael@0 3364 Parser<ParseHandler>::blockStatement()
michael@0 3365 {
michael@0 3366 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
michael@0 3367
michael@0 3368 StmtInfoPC stmtInfo(context);
michael@0 3369 if (!PushBlocklikeStatement(tokenStream, &stmtInfo, STMT_BLOCK, pc))
michael@0 3370 return null();
michael@0 3371
michael@0 3372 Node list = statements();
michael@0 3373 if (!list)
michael@0 3374 return null();
michael@0 3375
michael@0 3376 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
michael@0 3377 PopStatementPC(tokenStream, pc);
michael@0 3378 return list;
michael@0 3379 }
michael@0 3380
michael@0 3381 template <typename ParseHandler>
michael@0 3382 typename ParseHandler::Node
michael@0 3383 Parser<ParseHandler>::newBindingNode(PropertyName *name, bool functionScope, VarContext varContext)
michael@0 3384 {
michael@0 3385 /*
michael@0 3386 * If this name is being injected into an existing block/function, see if
michael@0 3387 * it has already been declared or if it resolves an outstanding lexdep.
michael@0 3388 * Otherwise, this is a let block/expr that introduces a new scope and thus
michael@0 3389 * shadows existing decls and doesn't resolve existing lexdeps. Duplicate
michael@0 3390 * names are caught by bindLet.
michael@0 3391 */
michael@0 3392 if (varContext == HoistVars) {
michael@0 3393 if (AtomDefnPtr p = pc->lexdeps->lookup(name)) {
michael@0 3394 DefinitionNode lexdep = p.value().get<ParseHandler>();
michael@0 3395 JS_ASSERT(handler.getDefinitionKind(lexdep) == Definition::PLACEHOLDER);
michael@0 3396
michael@0 3397 Node pn = handler.getDefinitionNode(lexdep);
michael@0 3398 if (handler.dependencyCovered(pn, pc->blockid(), functionScope)) {
michael@0 3399 handler.setBlockId(pn, pc->blockid());
michael@0 3400 pc->lexdeps->remove(p);
michael@0 3401 handler.setPosition(pn, pos());
michael@0 3402 return pn;
michael@0 3403 }
michael@0 3404 }
michael@0 3405 }
michael@0 3406
michael@0 3407 /* Make a new node for this declarator name (or destructuring pattern). */
michael@0 3408 return newName(name);
michael@0 3409 }
michael@0 3410
michael@0 3411 /*
michael@0 3412 * The 'blockObj' parameter is non-null when parsing the 'vars' in a let
michael@0 3413 * expression, block statement, non-top-level let declaration in statement
michael@0 3414 * context, and the let-initializer of a for-statement.
michael@0 3415 */
michael@0 3416 template <typename ParseHandler>
michael@0 3417 typename ParseHandler::Node
michael@0 3418 Parser<ParseHandler>::variables(ParseNodeKind kind, bool *psimple,
michael@0 3419 StaticBlockObject *blockObj, VarContext varContext)
michael@0 3420 {
michael@0 3421 /*
michael@0 3422 * The four options here are:
michael@0 3423 * - PNK_VAR: We're parsing var declarations.
michael@0 3424 * - PNK_CONST: We're parsing const declarations.
michael@0 3425 * - PNK_LET: We are parsing a let declaration.
michael@0 3426 * - PNK_CALL: We are parsing the head of a let block.
michael@0 3427 */
michael@0 3428 JS_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET || kind == PNK_CALL);
michael@0 3429
michael@0 3430 /*
michael@0 3431 * The simple flag is set if the declaration has the form 'var x', with
michael@0 3432 * only one variable declared and no initializer expression.
michael@0 3433 */
michael@0 3434 JS_ASSERT_IF(psimple, *psimple);
michael@0 3435
michael@0 3436 JSOp op = blockObj ? JSOP_NOP : kind == PNK_VAR ? JSOP_DEFVAR : JSOP_DEFCONST;
michael@0 3437
michael@0 3438 Node pn = handler.newList(kind, null(), op);
michael@0 3439 if (!pn)
michael@0 3440 return null();
michael@0 3441
michael@0 3442 /*
michael@0 3443 * SpiderMonkey const is really "write once per initialization evaluation"
michael@0 3444 * var, whereas let is block scoped. ES-Harmony wants block-scoped const so
michael@0 3445 * this code will change soon.
michael@0 3446 */
michael@0 3447 BindData<ParseHandler> data(context);
michael@0 3448 if (blockObj)
michael@0 3449 data.initLet(varContext, *blockObj, JSMSG_TOO_MANY_LOCALS);
michael@0 3450 else
michael@0 3451 data.initVarOrConst(op);
michael@0 3452
michael@0 3453 bool first = true;
michael@0 3454 Node pn2;
michael@0 3455 do {
michael@0 3456 if (psimple && !first)
michael@0 3457 *psimple = false;
michael@0 3458 first = false;
michael@0 3459
michael@0 3460 TokenKind tt = tokenStream.getToken();
michael@0 3461 if (tt == TOK_LB || tt == TOK_LC) {
michael@0 3462 if (psimple)
michael@0 3463 *psimple = false;
michael@0 3464
michael@0 3465 pc->inDeclDestructuring = true;
michael@0 3466 pn2 = primaryExpr(tt);
michael@0 3467 pc->inDeclDestructuring = false;
michael@0 3468 if (!pn2)
michael@0 3469 return null();
michael@0 3470
michael@0 3471 if (!checkDestructuring(&data, pn2))
michael@0 3472 return null();
michael@0 3473 bool ignored;
michael@0 3474 if (pc->parsingForInit && matchInOrOf(&ignored)) {
michael@0 3475 tokenStream.ungetToken();
michael@0 3476 handler.addList(pn, pn2);
michael@0 3477 continue;
michael@0 3478 }
michael@0 3479
michael@0 3480 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
michael@0 3481
michael@0 3482 Node init = assignExpr();
michael@0 3483 if (!init)
michael@0 3484 return null();
michael@0 3485
michael@0 3486 pn2 = handler.newBinaryOrAppend(PNK_ASSIGN, pn2, init, pc);
michael@0 3487 if (!pn2)
michael@0 3488 return null();
michael@0 3489 handler.addList(pn, pn2);
michael@0 3490 continue;
michael@0 3491 }
michael@0 3492
michael@0 3493 if (tt != TOK_NAME) {
michael@0 3494 if (tt == TOK_YIELD) {
michael@0 3495 if (!checkYieldNameValidity())
michael@0 3496 return null();
michael@0 3497 } else {
michael@0 3498 if (tt != TOK_ERROR)
michael@0 3499 report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
michael@0 3500 return null();
michael@0 3501 }
michael@0 3502 }
michael@0 3503
michael@0 3504 RootedPropertyName name(context, tokenStream.currentName());
michael@0 3505 pn2 = newBindingNode(name, kind == PNK_VAR || kind == PNK_CONST, varContext);
michael@0 3506 if (!pn2)
michael@0 3507 return null();
michael@0 3508 if (data.op == JSOP_DEFCONST)
michael@0 3509 handler.setFlag(pn2, PND_CONST);
michael@0 3510 data.pn = pn2;
michael@0 3511 if (!data.binder(&data, name, this))
michael@0 3512 return null();
michael@0 3513 handler.addList(pn, pn2);
michael@0 3514
michael@0 3515 if (tokenStream.matchToken(TOK_ASSIGN)) {
michael@0 3516 if (psimple)
michael@0 3517 *psimple = false;
michael@0 3518
michael@0 3519 Node init = assignExpr();
michael@0 3520 if (!init)
michael@0 3521 return null();
michael@0 3522
michael@0 3523 if (!handler.finishInitializerAssignment(pn2, init, data.op))
michael@0 3524 return null();
michael@0 3525 }
michael@0 3526 } while (tokenStream.matchToken(TOK_COMMA));
michael@0 3527
michael@0 3528 return pn;
michael@0 3529 }
michael@0 3530
michael@0 3531 template <>
michael@0 3532 ParseNode *
michael@0 3533 Parser<FullParseHandler>::letDeclaration()
michael@0 3534 {
michael@0 3535 handler.disableSyntaxParser();
michael@0 3536
michael@0 3537 ParseNode *pn;
michael@0 3538
michael@0 3539 do {
michael@0 3540 /*
michael@0 3541 * This is a let declaration. We must be directly under a block per the
michael@0 3542 * proposed ES4 specs, but not an implicit block created due to
michael@0 3543 * 'for (let ...)'. If we pass this error test, make the enclosing
michael@0 3544 * StmtInfoPC be our scope. Further let declarations in this block will
michael@0 3545 * find this scope statement and use the same block object.
michael@0 3546 *
michael@0 3547 * If we are the first let declaration in this block (i.e., when the
michael@0 3548 * enclosing maybe-scope StmtInfoPC isn't yet a scope statement) then
michael@0 3549 * we also need to set pc->blockNode to be our PNK_LEXICALSCOPE.
michael@0 3550 */
michael@0 3551 StmtInfoPC *stmt = pc->topStmt;
michael@0 3552 if (stmt && (!stmt->maybeScope() || stmt->isForLetBlock)) {
michael@0 3553 report(ParseError, false, null(), JSMSG_LET_DECL_NOT_IN_BLOCK);
michael@0 3554 return null();
michael@0 3555 }
michael@0 3556
michael@0 3557 if (stmt && stmt->isBlockScope) {
michael@0 3558 JS_ASSERT(pc->staticScope == stmt->staticScope);
michael@0 3559 } else {
michael@0 3560 if (pc->atBodyLevel()) {
michael@0 3561 /*
michael@0 3562 * ES4 specifies that let at top level and at body-block scope
michael@0 3563 * does not shadow var, so convert back to var.
michael@0 3564 */
michael@0 3565 pn = variables(PNK_VAR);
michael@0 3566 if (!pn)
michael@0 3567 return null();
michael@0 3568 pn->pn_xflags |= PNX_POPVAR;
michael@0 3569 break;
michael@0 3570 }
michael@0 3571
michael@0 3572 /*
michael@0 3573 * Some obvious assertions here, but they may help clarify the
michael@0 3574 * situation. This stmt is not yet a scope, so it must not be a
michael@0 3575 * catch block (catch is a lexical scope by definition).
michael@0 3576 */
michael@0 3577 JS_ASSERT(!stmt->isBlockScope);
michael@0 3578 JS_ASSERT(stmt != pc->topScopeStmt);
michael@0 3579 JS_ASSERT(stmt->type == STMT_BLOCK ||
michael@0 3580 stmt->type == STMT_SWITCH ||
michael@0 3581 stmt->type == STMT_TRY ||
michael@0 3582 stmt->type == STMT_FINALLY);
michael@0 3583 JS_ASSERT(!stmt->downScope);
michael@0 3584
michael@0 3585 /* Convert the block statement into a scope statement. */
michael@0 3586 StaticBlockObject *blockObj = StaticBlockObject::create(context);
michael@0 3587 if (!blockObj)
michael@0 3588 return null();
michael@0 3589
michael@0 3590 ObjectBox *blockbox = newObjectBox(blockObj);
michael@0 3591 if (!blockbox)
michael@0 3592 return null();
michael@0 3593
michael@0 3594 /*
michael@0 3595 * Insert stmt on the pc->topScopeStmt/stmtInfo.downScope linked
michael@0 3596 * list stack, if it isn't already there. If it is there, but it
michael@0 3597 * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
michael@0 3598 * block.
michael@0 3599 */
michael@0 3600 stmt->isBlockScope = stmt->isNestedScope = true;
michael@0 3601 stmt->downScope = pc->topScopeStmt;
michael@0 3602 pc->topScopeStmt = stmt;
michael@0 3603
michael@0 3604 blockObj->initEnclosingNestedScopeFromParser(pc->staticScope);
michael@0 3605 pc->staticScope = blockObj;
michael@0 3606 stmt->staticScope = blockObj;
michael@0 3607
michael@0 3608 #ifdef DEBUG
michael@0 3609 ParseNode *tmp = pc->blockNode;
michael@0 3610 JS_ASSERT(!tmp || !tmp->isKind(PNK_LEXICALSCOPE));
michael@0 3611 #endif
michael@0 3612
michael@0 3613 /* Create a new lexical scope node for these statements. */
michael@0 3614 ParseNode *pn1 = LexicalScopeNode::create(PNK_LEXICALSCOPE, &handler);
michael@0 3615 if (!pn1)
michael@0 3616 return null();
michael@0 3617
michael@0 3618 pn1->pn_pos = pc->blockNode->pn_pos;
michael@0 3619 pn1->pn_objbox = blockbox;
michael@0 3620 pn1->pn_expr = pc->blockNode;
michael@0 3621 pn1->pn_blockid = pc->blockNode->pn_blockid;
michael@0 3622 pc->blockNode = pn1;
michael@0 3623 }
michael@0 3624
michael@0 3625 pn = variables(PNK_LET, nullptr, &pc->staticScope->as<StaticBlockObject>(), HoistVars);
michael@0 3626 if (!pn)
michael@0 3627 return null();
michael@0 3628 pn->pn_xflags = PNX_POPVAR;
michael@0 3629 } while (0);
michael@0 3630
michael@0 3631 return MatchOrInsertSemicolon(tokenStream) ? pn : nullptr;
michael@0 3632 }
michael@0 3633
michael@0 3634 template <>
michael@0 3635 SyntaxParseHandler::Node
michael@0 3636 Parser<SyntaxParseHandler>::letDeclaration()
michael@0 3637 {
michael@0 3638 JS_ALWAYS_FALSE(abortIfSyntaxParser());
michael@0 3639 return SyntaxParseHandler::NodeFailure;
michael@0 3640 }
michael@0 3641
michael@0 3642 template <>
michael@0 3643 ParseNode *
michael@0 3644 Parser<FullParseHandler>::letStatement()
michael@0 3645 {
michael@0 3646 handler.disableSyntaxParser();
michael@0 3647
michael@0 3648 /* Check for a let statement or let expression. */
michael@0 3649 ParseNode *pn;
michael@0 3650 if (tokenStream.peekToken() == TOK_LP) {
michael@0 3651 pn = letBlock(LetStatement);
michael@0 3652 JS_ASSERT_IF(pn, pn->isKind(PNK_LET) || pn->isKind(PNK_SEMI));
michael@0 3653 } else {
michael@0 3654 pn = letDeclaration();
michael@0 3655 }
michael@0 3656 return pn;
michael@0 3657 }
michael@0 3658
michael@0 3659 template <>
michael@0 3660 SyntaxParseHandler::Node
michael@0 3661 Parser<SyntaxParseHandler>::letStatement()
michael@0 3662 {
michael@0 3663 JS_ALWAYS_FALSE(abortIfSyntaxParser());
michael@0 3664 return SyntaxParseHandler::NodeFailure;
michael@0 3665 }
michael@0 3666
michael@0 3667 template<typename ParseHandler>
michael@0 3668 typename ParseHandler::Node
michael@0 3669 Parser<ParseHandler>::importDeclaration()
michael@0 3670 {
michael@0 3671 JS_ASSERT(tokenStream.currentToken().type == TOK_IMPORT);
michael@0 3672
michael@0 3673 if (pc->sc->isFunctionBox() || !pc->atBodyLevel()) {
michael@0 3674 report(ParseError, false, null(), JSMSG_IMPORT_DECL_AT_TOP_LEVEL);
michael@0 3675 return null();
michael@0 3676 }
michael@0 3677
michael@0 3678 uint32_t begin = pos().begin;
michael@0 3679 TokenKind tt = tokenStream.getToken();
michael@0 3680
michael@0 3681 Node importSpecSet = handler.newList(PNK_IMPORT_SPEC_LIST);
michael@0 3682 if (!importSpecSet)
michael@0 3683 return null();
michael@0 3684
michael@0 3685 if (tt == TOK_NAME || tt == TOK_LC) {
michael@0 3686 if (tt == TOK_NAME) {
michael@0 3687 // Handle the form |import a from 'b'|, by adding a single import
michael@0 3688 // specifier to the list, with 'default' as the import name and
michael@0 3689 // 'a' as the binding name. This is equivalent to
michael@0 3690 // |import { default as a } from 'b'|.
michael@0 3691 Node importName = newName(context->names().default_);
michael@0 3692 if (!importName)
michael@0 3693 return null();
michael@0 3694
michael@0 3695 Node bindingName = newName(tokenStream.currentName());
michael@0 3696 if (!bindingName)
michael@0 3697 return null();
michael@0 3698
michael@0 3699 Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName);
michael@0 3700 if (!importSpec)
michael@0 3701 return null();
michael@0 3702
michael@0 3703 handler.addList(importSpecSet, importSpec);
michael@0 3704 } else {
michael@0 3705 do {
michael@0 3706 // Handle the forms |import {} from 'a'| and
michael@0 3707 // |import { ..., } from 'a'| (where ... is non empty), by
michael@0 3708 // escaping the loop early if the next token is }.
michael@0 3709 tt = tokenStream.peekToken(TokenStream::KeywordIsName);
michael@0 3710 if (tt == TOK_ERROR)
michael@0 3711 return null();
michael@0 3712 if (tt == TOK_RC)
michael@0 3713 break;
michael@0 3714
michael@0 3715 // If the next token is a keyword, the previous call to
michael@0 3716 // peekToken matched it as a TOK_NAME, and put it in the
michael@0 3717 // lookahead buffer, so this call will match keywords as well.
michael@0 3718 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME);
michael@0 3719 Node importName = newName(tokenStream.currentName());
michael@0 3720 if (!importName)
michael@0 3721 return null();
michael@0 3722
michael@0 3723 if (tokenStream.getToken() == TOK_NAME &&
michael@0 3724 tokenStream.currentName() == context->names().as)
michael@0 3725 {
michael@0 3726 if (tokenStream.getToken() != TOK_NAME) {
michael@0 3727 report(ParseError, false, null(), JSMSG_NO_BINDING_NAME);
michael@0 3728 return null();
michael@0 3729 }
michael@0 3730 } else {
michael@0 3731 // Keywords cannot be bound to themselves, so an import name
michael@0 3732 // that is a keyword is a syntax error if it is not followed
michael@0 3733 // by the keyword 'as'.
michael@0 3734 if (IsKeyword(importName->name())) {
michael@0 3735 JSAutoByteString bytes;
michael@0 3736 if (!AtomToPrintableString(context, importName->name(), &bytes))
michael@0 3737 return null();
michael@0 3738 report(ParseError, false, null(), JSMSG_AS_AFTER_RESERVED_WORD, bytes.ptr());
michael@0 3739 return null();
michael@0 3740 }
michael@0 3741 tokenStream.ungetToken();
michael@0 3742 }
michael@0 3743 Node bindingName = newName(tokenStream.currentName());
michael@0 3744 if (!bindingName)
michael@0 3745 return null();
michael@0 3746
michael@0 3747 Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName);
michael@0 3748 if (!importSpec)
michael@0 3749 return null();
michael@0 3750
michael@0 3751 handler.addList(importSpecSet, importSpec);
michael@0 3752 } while (tokenStream.matchToken(TOK_COMMA));
michael@0 3753
michael@0 3754 MUST_MATCH_TOKEN(TOK_RC, JSMSG_RC_AFTER_IMPORT_SPEC_LIST);
michael@0 3755 }
michael@0 3756
michael@0 3757 if (tokenStream.getToken() != TOK_NAME ||
michael@0 3758 tokenStream.currentName() != context->names().from)
michael@0 3759 {
michael@0 3760 report(ParseError, false, null(), JSMSG_FROM_AFTER_IMPORT_SPEC_SET);
michael@0 3761 return null();
michael@0 3762 }
michael@0 3763
michael@0 3764 MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
michael@0 3765 } else {
michael@0 3766 if (tt != TOK_STRING) {
michael@0 3767 report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_IMPORT);
michael@0 3768 return null();
michael@0 3769 }
michael@0 3770
michael@0 3771 // Handle the form |import 'a'| by leaving the list empty. This is
michael@0 3772 // equivalent to |import {} from 'a'|.
michael@0 3773 importSpecSet->pn_pos.end = importSpecSet->pn_pos.begin;
michael@0 3774 }
michael@0 3775
michael@0 3776 Node moduleSpec = stringLiteral();
michael@0 3777 if (!moduleSpec)
michael@0 3778 return null();
michael@0 3779
michael@0 3780 if (!MatchOrInsertSemicolon(tokenStream))
michael@0 3781 return null();
michael@0 3782
michael@0 3783 return handler.newImportDeclaration(importSpecSet, moduleSpec,
michael@0 3784 TokenPos(begin, pos().end));
michael@0 3785 }
michael@0 3786
michael@0 3787 template<>
michael@0 3788 SyntaxParseHandler::Node
michael@0 3789 Parser<SyntaxParseHandler>::importDeclaration()
michael@0 3790 {
michael@0 3791 JS_ALWAYS_FALSE(abortIfSyntaxParser());
michael@0 3792 return SyntaxParseHandler::NodeFailure;
michael@0 3793 }
michael@0 3794
michael@0 3795 template<typename ParseHandler>
michael@0 3796 typename ParseHandler::Node
michael@0 3797 Parser<ParseHandler>::exportDeclaration()
michael@0 3798 {
michael@0 3799 JS_ASSERT(tokenStream.currentToken().type == TOK_EXPORT);
michael@0 3800
michael@0 3801 if (pc->sc->isFunctionBox() || !pc->atBodyLevel()) {
michael@0 3802 report(ParseError, false, null(), JSMSG_EXPORT_DECL_AT_TOP_LEVEL);
michael@0 3803 return null();
michael@0 3804 }
michael@0 3805
michael@0 3806 uint32_t begin = pos().begin;
michael@0 3807
michael@0 3808 Node kid;
michael@0 3809 switch (TokenKind tt = tokenStream.getToken()) {
michael@0 3810 case TOK_LC:
michael@0 3811 case TOK_MUL:
michael@0 3812 kid = handler.newList(PNK_EXPORT_SPEC_LIST);
michael@0 3813 if (!kid)
michael@0 3814 return null();
michael@0 3815
michael@0 3816 if (tt == TOK_LC) {
michael@0 3817 do {
michael@0 3818 // Handle the forms |export {}| and |export { ..., }| (where ...
michael@0 3819 // is non empty), by escaping the loop early if the next token
michael@0 3820 // is }.
michael@0 3821 tt = tokenStream.peekToken();
michael@0 3822 if (tt == TOK_ERROR)
michael@0 3823 return null();
michael@0 3824 if (tt == TOK_RC)
michael@0 3825 break;
michael@0 3826
michael@0 3827 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_BINDING_NAME);
michael@0 3828 Node bindingName = newName(tokenStream.currentName());
michael@0 3829 if (!bindingName)
michael@0 3830 return null();
michael@0 3831
michael@0 3832 if (tokenStream.getToken() == TOK_NAME &&
michael@0 3833 tokenStream.currentName() == context->names().as)
michael@0 3834 {
michael@0 3835 if (tokenStream.getToken(TokenStream::KeywordIsName) != TOK_NAME) {
michael@0 3836 report(ParseError, false, null(), JSMSG_NO_EXPORT_NAME);
michael@0 3837 return null();
michael@0 3838 }
michael@0 3839 } else {
michael@0 3840 tokenStream.ungetToken();
michael@0 3841 }
michael@0 3842 Node exportName = newName(tokenStream.currentName());
michael@0 3843 if (!exportName)
michael@0 3844 return null();
michael@0 3845
michael@0 3846 Node exportSpec = handler.newBinary(PNK_EXPORT_SPEC, bindingName, exportName);
michael@0 3847 if (!exportSpec)
michael@0 3848 return null();
michael@0 3849
michael@0 3850 handler.addList(kid, exportSpec);
michael@0 3851 } while (tokenStream.matchToken(TOK_COMMA));
michael@0 3852
michael@0 3853 MUST_MATCH_TOKEN(TOK_RC, JSMSG_RC_AFTER_EXPORT_SPEC_LIST);
michael@0 3854 } else {
michael@0 3855 // Handle the form |export *| by adding a special export batch
michael@0 3856 // specifier to the list.
michael@0 3857 Node exportSpec = handler.newNullary(PNK_EXPORT_BATCH_SPEC, JSOP_NOP, pos());
michael@0 3858 if (!kid)
michael@0 3859 return null();
michael@0 3860
michael@0 3861 handler.addList(kid, exportSpec);
michael@0 3862 }
michael@0 3863 if (tokenStream.getToken() == TOK_NAME &&
michael@0 3864 tokenStream.currentName() == context->names().from)
michael@0 3865 {
michael@0 3866 MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
michael@0 3867
michael@0 3868 Node moduleSpec = stringLiteral();
michael@0 3869 if (!moduleSpec)
michael@0 3870 return null();
michael@0 3871
michael@0 3872 if (!MatchOrInsertSemicolon(tokenStream))
michael@0 3873 return null();
michael@0 3874
michael@0 3875 return handler.newExportFromDeclaration(begin, kid, moduleSpec);
michael@0 3876 } else {
michael@0 3877 tokenStream.ungetToken();
michael@0 3878 }
michael@0 3879
michael@0 3880 kid = MatchOrInsertSemicolon(tokenStream) ? kid : nullptr;
michael@0 3881 if (!kid)
michael@0 3882 return null();
michael@0 3883 break;
michael@0 3884
michael@0 3885 case TOK_FUNCTION:
michael@0 3886 kid = functionStmt();
michael@0 3887 if (!kid)
michael@0 3888 return null();
michael@0 3889 break;
michael@0 3890
michael@0 3891 case TOK_VAR:
michael@0 3892 case TOK_CONST:
michael@0 3893 kid = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST);
michael@0 3894 if (!kid)
michael@0 3895 return null();
michael@0 3896 kid->pn_xflags = PNX_POPVAR;
michael@0 3897
michael@0 3898 kid = MatchOrInsertSemicolon(tokenStream) ? kid : nullptr;
michael@0 3899 if (!kid)
michael@0 3900 return null();
michael@0 3901 break;
michael@0 3902
michael@0 3903 case TOK_NAME:
michael@0 3904 // Handle the form |export a} in the same way as |export let a|, by
michael@0 3905 // acting as if we've just seen the let keyword. Simply unget the token
michael@0 3906 // and fall through.
michael@0 3907 tokenStream.ungetToken();
michael@0 3908 case TOK_LET:
michael@0 3909 kid = letDeclaration();
michael@0 3910 if (!kid)
michael@0 3911 return null();
michael@0 3912 break;
michael@0 3913
michael@0 3914 default:
michael@0 3915 report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_EXPORT);
michael@0 3916 return null();
michael@0 3917 }
michael@0 3918
michael@0 3919 return handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
michael@0 3920 }
michael@0 3921
michael@0 3922 template<>
michael@0 3923 SyntaxParseHandler::Node
michael@0 3924 Parser<SyntaxParseHandler>::exportDeclaration()
michael@0 3925 {
michael@0 3926 JS_ALWAYS_FALSE(abortIfSyntaxParser());
michael@0 3927 return SyntaxParseHandler::NodeFailure;
michael@0 3928 }
michael@0 3929
michael@0 3930
michael@0 3931 template <typename ParseHandler>
michael@0 3932 typename ParseHandler::Node
michael@0 3933 Parser<ParseHandler>::expressionStatement()
michael@0 3934 {
michael@0 3935 tokenStream.ungetToken();
michael@0 3936 Node pnexpr = expr();
michael@0 3937 if (!pnexpr)
michael@0 3938 return null();
michael@0 3939 if (!MatchOrInsertSemicolon(tokenStream))
michael@0 3940 return null();
michael@0 3941 return handler.newExprStatement(pnexpr, pos().end);
michael@0 3942 }
michael@0 3943
michael@0 3944 template <typename ParseHandler>
michael@0 3945 typename ParseHandler::Node
michael@0 3946 Parser<ParseHandler>::ifStatement()
michael@0 3947 {
michael@0 3948 uint32_t begin = pos().begin;
michael@0 3949
michael@0 3950 /* An IF node has three kids: condition, then, and optional else. */
michael@0 3951 Node cond = condition();
michael@0 3952 if (!cond)
michael@0 3953 return null();
michael@0 3954
michael@0 3955 if (tokenStream.peekToken(TokenStream::Operand) == TOK_SEMI &&
michael@0 3956 !report(ParseExtraWarning, false, null(), JSMSG_EMPTY_CONSEQUENT))
michael@0 3957 {
michael@0 3958 return null();
michael@0 3959 }
michael@0 3960
michael@0 3961 StmtInfoPC stmtInfo(context);
michael@0 3962 PushStatementPC(pc, &stmtInfo, STMT_IF);
michael@0 3963 Node thenBranch = statement();
michael@0 3964 if (!thenBranch)
michael@0 3965 return null();
michael@0 3966
michael@0 3967 Node elseBranch;
michael@0 3968 if (tokenStream.matchToken(TOK_ELSE, TokenStream::Operand)) {
michael@0 3969 stmtInfo.type = STMT_ELSE;
michael@0 3970 elseBranch = statement();
michael@0 3971 if (!elseBranch)
michael@0 3972 return null();
michael@0 3973 } else {
michael@0 3974 elseBranch = null();
michael@0 3975 }
michael@0 3976
michael@0 3977 PopStatementPC(tokenStream, pc);
michael@0 3978 return handler.newIfStatement(begin, cond, thenBranch, elseBranch);
michael@0 3979 }
michael@0 3980
michael@0 3981 template <typename ParseHandler>
michael@0 3982 typename ParseHandler::Node
michael@0 3983 Parser<ParseHandler>::doWhileStatement()
michael@0 3984 {
michael@0 3985 uint32_t begin = pos().begin;
michael@0 3986 StmtInfoPC stmtInfo(context);
michael@0 3987 PushStatementPC(pc, &stmtInfo, STMT_DO_LOOP);
michael@0 3988 Node body = statement();
michael@0 3989 if (!body)
michael@0 3990 return null();
michael@0 3991 MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
michael@0 3992 Node cond = condition();
michael@0 3993 if (!cond)
michael@0 3994 return null();
michael@0 3995 PopStatementPC(tokenStream, pc);
michael@0 3996
michael@0 3997 if (versionNumber() == JSVERSION_ECMA_3) {
michael@0 3998 // Pedantically require a semicolon or line break, following ES3.
michael@0 3999 // Bug 880329 proposes removing this case.
michael@0 4000 if (!MatchOrInsertSemicolon(tokenStream))
michael@0 4001 return null();
michael@0 4002 } else {
michael@0 4003 // The semicolon after do-while is even more optional than most
michael@0 4004 // semicolons in JS. Web compat required this by 2004:
michael@0 4005 // http://bugzilla.mozilla.org/show_bug.cgi?id=238945
michael@0 4006 // ES3 and ES5 disagreed, but ES6 conforms to Web reality:
michael@0 4007 // https://bugs.ecmascript.org/show_bug.cgi?id=157
michael@0 4008 (void) tokenStream.matchToken(TOK_SEMI);
michael@0 4009 }
michael@0 4010
michael@0 4011 return handler.newDoWhileStatement(body, cond, TokenPos(begin, pos().end));
michael@0 4012 }
michael@0 4013
michael@0 4014 template <typename ParseHandler>
michael@0 4015 typename ParseHandler::Node
michael@0 4016 Parser<ParseHandler>::whileStatement()
michael@0 4017 {
michael@0 4018 uint32_t begin = pos().begin;
michael@0 4019 StmtInfoPC stmtInfo(context);
michael@0 4020 PushStatementPC(pc, &stmtInfo, STMT_WHILE_LOOP);
michael@0 4021 Node cond = condition();
michael@0 4022 if (!cond)
michael@0 4023 return null();
michael@0 4024 Node body = statement();
michael@0 4025 if (!body)
michael@0 4026 return null();
michael@0 4027 PopStatementPC(tokenStream, pc);
michael@0 4028 return handler.newWhileStatement(begin, cond, body);
michael@0 4029 }
michael@0 4030
michael@0 4031 template <typename ParseHandler>
michael@0 4032 bool
michael@0 4033 Parser<ParseHandler>::matchInOrOf(bool *isForOfp)
michael@0 4034 {
michael@0 4035 if (tokenStream.matchToken(TOK_IN)) {
michael@0 4036 *isForOfp = false;
michael@0 4037 return true;
michael@0 4038 }
michael@0 4039 if (tokenStream.matchContextualKeyword(context->names().of)) {
michael@0 4040 *isForOfp = true;
michael@0 4041 return true;
michael@0 4042 }
michael@0 4043 return false;
michael@0 4044 }
michael@0 4045
michael@0 4046 template <>
michael@0 4047 bool
michael@0 4048 Parser<FullParseHandler>::isValidForStatementLHS(ParseNode *pn1, JSVersion version,
michael@0 4049 bool isForDecl, bool isForEach,
michael@0 4050 ParseNodeKind headKind)
michael@0 4051 {
michael@0 4052 if (isForDecl) {
michael@0 4053 if (pn1->pn_count > 1)
michael@0 4054 return false;
michael@0 4055 if (pn1->isOp(JSOP_DEFCONST))
michael@0 4056 return false;
michael@0 4057
michael@0 4058 // In JS 1.7 only, for (var [K, V] in EXPR) has a special meaning.
michael@0 4059 // Hence all other destructuring decls are banned there.
michael@0 4060 if (version == JSVERSION_1_7 && !isForEach && headKind == PNK_FORIN) {
michael@0 4061 ParseNode *lhs = pn1->pn_head;
michael@0 4062 if (lhs->isKind(PNK_ASSIGN))
michael@0 4063 lhs = lhs->pn_left;
michael@0 4064
michael@0 4065 if (lhs->isKind(PNK_OBJECT))
michael@0 4066 return false;
michael@0 4067 if (lhs->isKind(PNK_ARRAY) && lhs->pn_count != 2)
michael@0 4068 return false;
michael@0 4069 }
michael@0 4070 return true;
michael@0 4071 }
michael@0 4072
michael@0 4073 switch (pn1->getKind()) {
michael@0 4074 case PNK_NAME:
michael@0 4075 case PNK_DOT:
michael@0 4076 case PNK_CALL:
michael@0 4077 case PNK_ELEM:
michael@0 4078 return true;
michael@0 4079
michael@0 4080 case PNK_ARRAY:
michael@0 4081 case PNK_OBJECT:
michael@0 4082 // In JS 1.7 only, for ([K, V] in EXPR) has a special meaning.
michael@0 4083 // Hence all other destructuring left-hand sides are banned there.
michael@0 4084 if (version == JSVERSION_1_7 && !isForEach && headKind == PNK_FORIN)
michael@0 4085 return pn1->isKind(PNK_ARRAY) && pn1->pn_count == 2;
michael@0 4086 return true;
michael@0 4087
michael@0 4088 default:
michael@0 4089 return false;
michael@0 4090 }
michael@0 4091 }
michael@0 4092
michael@0 4093 template <>
michael@0 4094 ParseNode *
michael@0 4095 Parser<FullParseHandler>::forStatement()
michael@0 4096 {
michael@0 4097 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
michael@0 4098 uint32_t begin = pos().begin;
michael@0 4099
michael@0 4100 StmtInfoPC forStmt(context);
michael@0 4101 PushStatementPC(pc, &forStmt, STMT_FOR_LOOP);
michael@0 4102
michael@0 4103 bool isForEach = false;
michael@0 4104 unsigned iflags = 0;
michael@0 4105
michael@0 4106 if (allowsForEachIn() && tokenStream.matchContextualKeyword(context->names().each)) {
michael@0 4107 iflags = JSITER_FOREACH;
michael@0 4108 isForEach = true;
michael@0 4109 }
michael@0 4110
michael@0 4111 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
michael@0 4112
michael@0 4113 /*
michael@0 4114 * True if we have 'for (var/let/const ...)', except in the oddball case
michael@0 4115 * where 'let' begins a let-expression in 'for (let (...) ...)'.
michael@0 4116 */
michael@0 4117 bool isForDecl = false;
michael@0 4118
michael@0 4119 /* Non-null when isForDecl is true for a 'for (let ...)' statement. */
michael@0 4120 RootedStaticBlockObject blockObj(context);
michael@0 4121
michael@0 4122 /* Set to 'x' in 'for (x ;... ;...)' or 'for (x in ...)'. */
michael@0 4123 ParseNode *pn1;
michael@0 4124
michael@0 4125 {
michael@0 4126 TokenKind tt = tokenStream.peekToken(TokenStream::Operand);
michael@0 4127 if (tt == TOK_SEMI) {
michael@0 4128 pn1 = nullptr;
michael@0 4129 } else {
michael@0 4130 /*
michael@0 4131 * Set pn1 to a var list or an initializing expression.
michael@0 4132 *
michael@0 4133 * Set the parsingForInit flag during parsing of the first clause
michael@0 4134 * of the for statement. This flag will be used by the RelExpr
michael@0 4135 * production; if it is set, then the 'in' keyword will not be
michael@0 4136 * recognized as an operator, leaving it available to be parsed as
michael@0 4137 * part of a for/in loop.
michael@0 4138 *
michael@0 4139 * A side effect of this restriction is that (unparenthesized)
michael@0 4140 * expressions involving an 'in' operator are illegal in the init
michael@0 4141 * clause of an ordinary for loop.
michael@0 4142 */
michael@0 4143 pc->parsingForInit = true;
michael@0 4144 if (tt == TOK_VAR || tt == TOK_CONST) {
michael@0 4145 isForDecl = true;
michael@0 4146 tokenStream.consumeKnownToken(tt);
michael@0 4147 pn1 = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST);
michael@0 4148 }
michael@0 4149 else if (tt == TOK_LET) {
michael@0 4150 handler.disableSyntaxParser();
michael@0 4151 (void) tokenStream.getToken();
michael@0 4152 if (tokenStream.peekToken() == TOK_LP) {
michael@0 4153 pn1 = letBlock(LetExpresion);
michael@0 4154 } else {
michael@0 4155 isForDecl = true;
michael@0 4156 blockObj = StaticBlockObject::create(context);
michael@0 4157 if (!blockObj)
michael@0 4158 return null();
michael@0 4159 pn1 = variables(PNK_LET, nullptr, blockObj, DontHoistVars);
michael@0 4160 }
michael@0 4161 }
michael@0 4162 else {
michael@0 4163 pn1 = expr();
michael@0 4164 }
michael@0 4165 pc->parsingForInit = false;
michael@0 4166 if (!pn1)
michael@0 4167 return null();
michael@0 4168 }
michael@0 4169 }
michael@0 4170
michael@0 4171 JS_ASSERT_IF(isForDecl, pn1->isArity(PN_LIST));
michael@0 4172 JS_ASSERT(!!blockObj == (isForDecl && pn1->isOp(JSOP_NOP)));
michael@0 4173
michael@0 4174 // The form 'for (let <vars>; <expr2>; <expr3>) <stmt>' generates an
michael@0 4175 // implicit block even if stmt is not a BlockStatement.
michael@0 4176 // If the loop has that exact form, then:
michael@0 4177 // - forLetImpliedBlock is the node for the implicit block scope.
michael@0 4178 // - forLetDecl is the node for the decl 'let <vars>'.
michael@0 4179 // Otherwise both are null.
michael@0 4180 ParseNode *forLetImpliedBlock = nullptr;
michael@0 4181 ParseNode *forLetDecl = nullptr;
michael@0 4182
michael@0 4183 // If non-null, the node for the decl 'var v = expr1' in the weirdo form
michael@0 4184 // 'for (var v = expr1 in expr2) stmt'.
michael@0 4185 ParseNode *hoistedVar = nullptr;
michael@0 4186
michael@0 4187 /*
michael@0 4188 * We can be sure that it's a for/in loop if there's still an 'in'
michael@0 4189 * keyword here, even if JavaScript recognizes 'in' as an operator,
michael@0 4190 * as we've excluded 'in' from being parsed in RelExpr by setting
michael@0 4191 * pc->parsingForInit.
michael@0 4192 */
michael@0 4193 StmtInfoPC letStmt(context); /* used if blockObj != nullptr. */
michael@0 4194 ParseNode *pn2, *pn3; /* forHead->pn_kid2 and pn_kid3. */
michael@0 4195 ParseNodeKind headKind = PNK_FORHEAD;
michael@0 4196 if (pn1) {
michael@0 4197 bool isForOf;
michael@0 4198 if (matchInOrOf(&isForOf))
michael@0 4199 headKind = isForOf ? PNK_FOROF : PNK_FORIN;
michael@0 4200 }
michael@0 4201
michael@0 4202 if (headKind == PNK_FOROF || headKind == PNK_FORIN) {
michael@0 4203 /*
michael@0 4204 * Parse the rest of the for/in or for/of head.
michael@0 4205 *
michael@0 4206 * Here pn1 is everything to the left of 'in' or 'of'. At the end of
michael@0 4207 * this block, pn1 is a decl or nullptr, pn2 is the assignment target
michael@0 4208 * that receives the enumeration value each iteration, and pn3 is the
michael@0 4209 * rhs of 'in'.
michael@0 4210 */
michael@0 4211 if (headKind == PNK_FOROF) {
michael@0 4212 forStmt.type = STMT_FOR_OF_LOOP;
michael@0 4213 forStmt.type = (headKind == PNK_FOROF) ? STMT_FOR_OF_LOOP : STMT_FOR_IN_LOOP;
michael@0 4214 if (isForEach) {
michael@0 4215 report(ParseError, false, null(), JSMSG_BAD_FOR_EACH_LOOP);
michael@0 4216 return null();
michael@0 4217 }
michael@0 4218 } else {
michael@0 4219 forStmt.type = STMT_FOR_IN_LOOP;
michael@0 4220 iflags |= JSITER_ENUMERATE;
michael@0 4221 }
michael@0 4222
michael@0 4223 /* Check that the left side of the 'in' or 'of' is valid. */
michael@0 4224 if (!isValidForStatementLHS(pn1, versionNumber(), isForDecl, isForEach, headKind)) {
michael@0 4225 report(ParseError, false, pn1, JSMSG_BAD_FOR_LEFTSIDE);
michael@0 4226 return null();
michael@0 4227 }
michael@0 4228
michael@0 4229 /*
michael@0 4230 * After the following if-else, pn2 will point to the name or
michael@0 4231 * destructuring pattern on in's left. pn1 will point to the decl, if
michael@0 4232 * any, else nullptr. Note that the "declaration with initializer" case
michael@0 4233 * rewrites the loop-head, moving the decl and setting pn1 to nullptr.
michael@0 4234 */
michael@0 4235 if (isForDecl) {
michael@0 4236 pn2 = pn1->pn_head;
michael@0 4237 if ((pn2->isKind(PNK_NAME) && pn2->maybeExpr()) || pn2->isKind(PNK_ASSIGN)) {
michael@0 4238 /*
michael@0 4239 * Declaration with initializer.
michael@0 4240 *
michael@0 4241 * Rewrite 'for (<decl> x = i in o)' where <decl> is 'var' or
michael@0 4242 * 'const' to hoist the initializer or the entire decl out of
michael@0 4243 * the loop head.
michael@0 4244 */
michael@0 4245 if (headKind == PNK_FOROF) {
michael@0 4246 report(ParseError, false, pn2, JSMSG_INVALID_FOR_OF_INIT);
michael@0 4247 return null();
michael@0 4248 }
michael@0 4249 if (blockObj) {
michael@0 4250 report(ParseError, false, pn2, JSMSG_INVALID_FOR_IN_INIT);
michael@0 4251 return null();
michael@0 4252 }
michael@0 4253
michael@0 4254 hoistedVar = pn1;
michael@0 4255
michael@0 4256 /*
michael@0 4257 * All of 'var x = i' is hoisted above 'for (x in o)'.
michael@0 4258 *
michael@0 4259 * Request JSOP_POP here since the var is for a simple
michael@0 4260 * name (it is not a destructuring binding's left-hand
michael@0 4261 * side) and it has an initializer.
michael@0 4262 */
michael@0 4263 pn1->pn_xflags |= PNX_POPVAR;
michael@0 4264 pn1 = nullptr;
michael@0 4265
michael@0 4266 if (pn2->isKind(PNK_ASSIGN)) {
michael@0 4267 pn2 = pn2->pn_left;
michael@0 4268 JS_ASSERT(pn2->isKind(PNK_ARRAY) || pn2->isKind(PNK_OBJECT) ||
michael@0 4269 pn2->isKind(PNK_NAME));
michael@0 4270 }
michael@0 4271 }
michael@0 4272 } else {
michael@0 4273 /* Not a declaration. */
michael@0 4274 JS_ASSERT(!blockObj);
michael@0 4275 pn2 = pn1;
michael@0 4276 pn1 = nullptr;
michael@0 4277
michael@0 4278 if (!checkAndMarkAsAssignmentLhs(pn2, PlainAssignment))
michael@0 4279 return null();
michael@0 4280 }
michael@0 4281
michael@0 4282 pn3 = (headKind == PNK_FOROF) ? assignExpr() : expr();
michael@0 4283 if (!pn3)
michael@0 4284 return null();
michael@0 4285
michael@0 4286 if (blockObj) {
michael@0 4287 /*
michael@0 4288 * Now that the pn3 has been parsed, push the let scope. To hold
michael@0 4289 * the blockObj for the emitter, wrap the PNK_LEXICALSCOPE node
michael@0 4290 * created by PushLetScope around the for's initializer. This also
michael@0 4291 * serves to indicate the let-decl to the emitter.
michael@0 4292 */
michael@0 4293 ParseNode *block = pushLetScope(blockObj, &letStmt);
michael@0 4294 if (!block)
michael@0 4295 return null();
michael@0 4296 letStmt.isForLetBlock = true;
michael@0 4297 block->pn_expr = pn1;
michael@0 4298 block->pn_pos = pn1->pn_pos;
michael@0 4299 pn1 = block;
michael@0 4300 }
michael@0 4301
michael@0 4302 if (isForDecl) {
michael@0 4303 /*
michael@0 4304 * pn2 is part of a declaration. Make a copy that can be passed to
michael@0 4305 * EmitAssignment. Take care to do this after PushLetScope.
michael@0 4306 */
michael@0 4307 pn2 = cloneLeftHandSide(pn2);
michael@0 4308 if (!pn2)
michael@0 4309 return null();
michael@0 4310 }
michael@0 4311
michael@0 4312 switch (pn2->getKind()) {
michael@0 4313 case PNK_NAME:
michael@0 4314 /* Beware 'for (arguments in ...)' with or without a 'var'. */
michael@0 4315 pn2->markAsAssigned();
michael@0 4316 break;
michael@0 4317
michael@0 4318 case PNK_ASSIGN:
michael@0 4319 MOZ_ASSUME_UNREACHABLE("forStatement TOK_ASSIGN");
michael@0 4320
michael@0 4321 case PNK_ARRAY:
michael@0 4322 case PNK_OBJECT:
michael@0 4323 if (versionNumber() == JSVERSION_1_7) {
michael@0 4324 /*
michael@0 4325 * Destructuring for-in requires [key, value] enumeration
michael@0 4326 * in JS1.7.
michael@0 4327 */
michael@0 4328 if (!isForEach && headKind == PNK_FORIN)
michael@0 4329 iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
michael@0 4330 }
michael@0 4331 break;
michael@0 4332
michael@0 4333 default:;
michael@0 4334 }
michael@0 4335 } else {
michael@0 4336 if (isForEach) {
michael@0 4337 reportWithOffset(ParseError, false, begin, JSMSG_BAD_FOR_EACH_LOOP);
michael@0 4338 return null();
michael@0 4339 }
michael@0 4340
michael@0 4341 headKind = PNK_FORHEAD;
michael@0 4342
michael@0 4343 if (blockObj) {
michael@0 4344 /*
michael@0 4345 * Desugar 'for (let A; B; C) D' into 'let (A) { for (; B; C) D }'
michael@0 4346 * to induce the correct scoping for A.
michael@0 4347 */
michael@0 4348 forLetImpliedBlock = pushLetScope(blockObj, &letStmt);
michael@0 4349 if (!forLetImpliedBlock)
michael@0 4350 return null();
michael@0 4351 letStmt.isForLetBlock = true;
michael@0 4352
michael@0 4353 forLetDecl = pn1;
michael@0 4354 pn1 = nullptr;
michael@0 4355 }
michael@0 4356
michael@0 4357 /* Parse the loop condition or null into pn2. */
michael@0 4358 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
michael@0 4359 if (tokenStream.peekToken(TokenStream::Operand) == TOK_SEMI) {
michael@0 4360 pn2 = nullptr;
michael@0 4361 } else {
michael@0 4362 pn2 = expr();
michael@0 4363 if (!pn2)
michael@0 4364 return null();
michael@0 4365 }
michael@0 4366
michael@0 4367 /* Parse the update expression or null into pn3. */
michael@0 4368 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
michael@0 4369 if (tokenStream.peekToken(TokenStream::Operand) == TOK_RP) {
michael@0 4370 pn3 = nullptr;
michael@0 4371 } else {
michael@0 4372 pn3 = expr();
michael@0 4373 if (!pn3)
michael@0 4374 return null();
michael@0 4375 }
michael@0 4376 }
michael@0 4377
michael@0 4378 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
michael@0 4379
michael@0 4380 TokenPos headPos(begin, pos().end);
michael@0 4381 ParseNode *forHead = handler.newForHead(headKind, pn1, pn2, pn3, headPos);
michael@0 4382 if (!forHead)
michael@0 4383 return null();
michael@0 4384
michael@0 4385 /* Parse the loop body. */
michael@0 4386 ParseNode *body = statement();
michael@0 4387 if (!body)
michael@0 4388 return null();
michael@0 4389
michael@0 4390 if (blockObj)
michael@0 4391 PopStatementPC(tokenStream, pc);
michael@0 4392 PopStatementPC(tokenStream, pc);
michael@0 4393
michael@0 4394 ParseNode *forLoop = handler.newForStatement(begin, forHead, body, iflags);
michael@0 4395 if (!forLoop)
michael@0 4396 return null();
michael@0 4397
michael@0 4398 if (hoistedVar) {
michael@0 4399 ParseNode *pnseq = handler.newList(PNK_SEQ, hoistedVar);
michael@0 4400 if (!pnseq)
michael@0 4401 return null();
michael@0 4402 pnseq->pn_pos = forLoop->pn_pos;
michael@0 4403 pnseq->append(forLoop);
michael@0 4404 return pnseq;
michael@0 4405 }
michael@0 4406 if (forLetImpliedBlock) {
michael@0 4407 forLetImpliedBlock->pn_expr = forLoop;
michael@0 4408 forLetImpliedBlock->pn_pos = forLoop->pn_pos;
michael@0 4409 ParseNode *let = handler.newBinary(PNK_LET, forLetDecl, forLetImpliedBlock);
michael@0 4410 if (!let)
michael@0 4411 return null();
michael@0 4412 let->pn_pos = forLoop->pn_pos;
michael@0 4413 return let;
michael@0 4414 }
michael@0 4415 return forLoop;
michael@0 4416 }
michael@0 4417
michael@0 4418 template <>
michael@0 4419 SyntaxParseHandler::Node
michael@0 4420 Parser<SyntaxParseHandler>::forStatement()
michael@0 4421 {
michael@0 4422 /*
michael@0 4423 * 'for' statement parsing is fantastically complicated and requires being
michael@0 4424 * able to inspect the parse tree for previous parts of the 'for'. Syntax
michael@0 4425 * parsing of 'for' statements is thus done separately, and only handles
michael@0 4426 * the types of 'for' statements likely to be seen in web content.
michael@0 4427 */
michael@0 4428 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
michael@0 4429
michael@0 4430 StmtInfoPC forStmt(context);
michael@0 4431 PushStatementPC(pc, &forStmt, STMT_FOR_LOOP);
michael@0 4432
michael@0 4433 /* Don't parse 'for each' loops. */
michael@0 4434 if (allowsForEachIn()) {
michael@0 4435 TokenKind tt = tokenStream.peekToken();
michael@0 4436 // Not all "yield" tokens are names, but the ones that aren't names are
michael@0 4437 // invalid in this context anyway.
michael@0 4438 if (tt == TOK_NAME || tt == TOK_YIELD) {
michael@0 4439 JS_ALWAYS_FALSE(abortIfSyntaxParser());
michael@0 4440 return null();
michael@0 4441 }
michael@0 4442 }
michael@0 4443
michael@0 4444 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
michael@0 4445
michael@0 4446 /* True if we have 'for (var ...)'. */
michael@0 4447 bool isForDecl = false;
michael@0 4448 bool simpleForDecl = true;
michael@0 4449
michael@0 4450 /* Set to 'x' in 'for (x ;... ;...)' or 'for (x in ...)'. */
michael@0 4451 Node lhsNode;
michael@0 4452
michael@0 4453 {
michael@0 4454 TokenKind tt = tokenStream.peekToken(TokenStream::Operand);
michael@0 4455 if (tt == TOK_SEMI) {
michael@0 4456 lhsNode = null();
michael@0 4457 } else {
michael@0 4458 /* Set lhsNode to a var list or an initializing expression. */
michael@0 4459 pc->parsingForInit = true;
michael@0 4460 if (tt == TOK_VAR) {
michael@0 4461 isForDecl = true;
michael@0 4462 tokenStream.consumeKnownToken(tt);
michael@0 4463 lhsNode = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST, &simpleForDecl);
michael@0 4464 }
michael@0 4465 else if (tt == TOK_CONST || tt == TOK_LET) {
michael@0 4466 JS_ALWAYS_FALSE(abortIfSyntaxParser());
michael@0 4467 return null();
michael@0 4468 }
michael@0 4469 else {
michael@0 4470 lhsNode = expr();
michael@0 4471 }
michael@0 4472 if (!lhsNode)
michael@0 4473 return null();
michael@0 4474 pc->parsingForInit = false;
michael@0 4475 }
michael@0 4476 }
michael@0 4477
michael@0 4478 /*
michael@0 4479 * We can be sure that it's a for/in loop if there's still an 'in'
michael@0 4480 * keyword here, even if JavaScript recognizes 'in' as an operator,
michael@0 4481 * as we've excluded 'in' from being parsed in RelExpr by setting
michael@0 4482 * pc->parsingForInit.
michael@0 4483 */
michael@0 4484 bool isForOf;
michael@0 4485 if (lhsNode && matchInOrOf(&isForOf)) {
michael@0 4486 /* Parse the rest of the for/in or for/of head. */
michael@0 4487 forStmt.type = isForOf ? STMT_FOR_OF_LOOP : STMT_FOR_IN_LOOP;
michael@0 4488
michael@0 4489 /* Check that the left side of the 'in' or 'of' is valid. */
michael@0 4490 if (!isForDecl &&
michael@0 4491 lhsNode != SyntaxParseHandler::NodeName &&
michael@0 4492 lhsNode != SyntaxParseHandler::NodeGetProp &&
michael@0 4493 lhsNode != SyntaxParseHandler::NodeLValue)
michael@0 4494 {
michael@0 4495 JS_ALWAYS_FALSE(abortIfSyntaxParser());
michael@0 4496 return null();
michael@0 4497 }
michael@0 4498
michael@0 4499 if (!simpleForDecl) {
michael@0 4500 JS_ALWAYS_FALSE(abortIfSyntaxParser());
michael@0 4501 return null();
michael@0 4502 }
michael@0 4503
michael@0 4504 if (!isForDecl && !checkAndMarkAsAssignmentLhs(lhsNode, PlainAssignment))
michael@0 4505 return null();
michael@0 4506
michael@0 4507 if (!expr())
michael@0 4508 return null();
michael@0 4509 } else {
michael@0 4510 /* Parse the loop condition or null. */
michael@0 4511 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
michael@0 4512 if (tokenStream.peekToken(TokenStream::Operand) != TOK_SEMI) {
michael@0 4513 if (!expr())
michael@0 4514 return null();
michael@0 4515 }
michael@0 4516
michael@0 4517 /* Parse the update expression or null. */
michael@0 4518 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
michael@0 4519 if (tokenStream.peekToken(TokenStream::Operand) != TOK_RP) {
michael@0 4520 if (!expr())
michael@0 4521 return null();
michael@0 4522 }
michael@0 4523 }
michael@0 4524
michael@0 4525 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
michael@0 4526
michael@0 4527 /* Parse the loop body. */
michael@0 4528 if (!statement())
michael@0 4529 return null();
michael@0 4530
michael@0 4531 PopStatementPC(tokenStream, pc);
michael@0 4532 return SyntaxParseHandler::NodeGeneric;
michael@0 4533 }
michael@0 4534
michael@0 4535 template <typename ParseHandler>
michael@0 4536 typename ParseHandler::Node
michael@0 4537 Parser<ParseHandler>::switchStatement()
michael@0 4538 {
michael@0 4539 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_SWITCH));
michael@0 4540 uint32_t begin = pos().begin;
michael@0 4541
michael@0 4542 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
michael@0 4543
michael@0 4544 Node discriminant = exprInParens();
michael@0 4545 if (!discriminant)
michael@0 4546 return null();
michael@0 4547
michael@0 4548 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
michael@0 4549 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
michael@0 4550
michael@0 4551 StmtInfoPC stmtInfo(context);
michael@0 4552 PushStatementPC(pc, &stmtInfo, STMT_SWITCH);
michael@0 4553
michael@0 4554 if (!GenerateBlockId(tokenStream, pc, pc->topStmt->blockid))
michael@0 4555 return null();
michael@0 4556
michael@0 4557 Node caseList = handler.newStatementList(pc->blockid(), pos());
michael@0 4558 if (!caseList)
michael@0 4559 return null();
michael@0 4560
michael@0 4561 Node saveBlock = pc->blockNode;
michael@0 4562 pc->blockNode = caseList;
michael@0 4563
michael@0 4564 bool seenDefault = false;
michael@0 4565 TokenKind tt;
michael@0 4566 while ((tt = tokenStream.getToken()) != TOK_RC) {
michael@0 4567 uint32_t caseBegin = pos().begin;
michael@0 4568
michael@0 4569 Node caseExpr;
michael@0 4570 switch (tt) {
michael@0 4571 case TOK_DEFAULT:
michael@0 4572 if (seenDefault) {
michael@0 4573 report(ParseError, false, null(), JSMSG_TOO_MANY_DEFAULTS);
michael@0 4574 return null();
michael@0 4575 }
michael@0 4576 seenDefault = true;
michael@0 4577 caseExpr = null(); // The default case has pn_left == nullptr.
michael@0 4578 break;
michael@0 4579
michael@0 4580 case TOK_CASE:
michael@0 4581 caseExpr = expr();
michael@0 4582 if (!caseExpr)
michael@0 4583 return null();
michael@0 4584 break;
michael@0 4585
michael@0 4586 case TOK_ERROR:
michael@0 4587 return null();
michael@0 4588
michael@0 4589 default:
michael@0 4590 report(ParseError, false, null(), JSMSG_BAD_SWITCH);
michael@0 4591 return null();
michael@0 4592 }
michael@0 4593
michael@0 4594 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
michael@0 4595
michael@0 4596 Node body = handler.newStatementList(pc->blockid(), pos());
michael@0 4597 if (!body)
michael@0 4598 return null();
michael@0 4599
michael@0 4600 while ((tt = tokenStream.peekToken(TokenStream::Operand)) != TOK_RC &&
michael@0 4601 tt != TOK_CASE && tt != TOK_DEFAULT) {
michael@0 4602 if (tt == TOK_ERROR)
michael@0 4603 return null();
michael@0 4604 Node stmt = statement();
michael@0 4605 if (!stmt)
michael@0 4606 return null();
michael@0 4607 handler.addList(body, stmt);
michael@0 4608 }
michael@0 4609
michael@0 4610 Node casepn = handler.newCaseOrDefault(caseBegin, caseExpr, body);
michael@0 4611 if (!casepn)
michael@0 4612 return null();
michael@0 4613 handler.addList(caseList, casepn);
michael@0 4614 }
michael@0 4615
michael@0 4616 /*
michael@0 4617 * Handle the case where there was a let declaration in any case in
michael@0 4618 * the switch body, but not within an inner block. If it replaced
michael@0 4619 * pc->blockNode with a new block node then we must refresh caseList and
michael@0 4620 * then restore pc->blockNode.
michael@0 4621 */
michael@0 4622 if (pc->blockNode != caseList)
michael@0 4623 caseList = pc->blockNode;
michael@0 4624 pc->blockNode = saveBlock;
michael@0 4625
michael@0 4626 PopStatementPC(tokenStream, pc);
michael@0 4627
michael@0 4628 handler.setEndPosition(caseList, pos().end);
michael@0 4629
michael@0 4630 return handler.newSwitchStatement(begin, discriminant, caseList);
michael@0 4631 }
michael@0 4632
michael@0 4633 template <typename ParseHandler>
michael@0 4634 typename ParseHandler::Node
michael@0 4635 Parser<ParseHandler>::continueStatement()
michael@0 4636 {
michael@0 4637 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_CONTINUE));
michael@0 4638 uint32_t begin = pos().begin;
michael@0 4639
michael@0 4640 RootedPropertyName label(context);
michael@0 4641 if (!matchLabel(&label))
michael@0 4642 return null();
michael@0 4643
michael@0 4644 StmtInfoPC *stmt = pc->topStmt;
michael@0 4645 if (label) {
michael@0 4646 for (StmtInfoPC *stmt2 = nullptr; ; stmt = stmt->down) {
michael@0 4647 if (!stmt) {
michael@0 4648 report(ParseError, false, null(), JSMSG_LABEL_NOT_FOUND);
michael@0 4649 return null();
michael@0 4650 }
michael@0 4651 if (stmt->type == STMT_LABEL) {
michael@0 4652 if (stmt->label == label) {
michael@0 4653 if (!stmt2 || !stmt2->isLoop()) {
michael@0 4654 report(ParseError, false, null(), JSMSG_BAD_CONTINUE);
michael@0 4655 return null();
michael@0 4656 }
michael@0 4657 break;
michael@0 4658 }
michael@0 4659 } else {
michael@0 4660 stmt2 = stmt;
michael@0 4661 }
michael@0 4662 }
michael@0 4663 } else {
michael@0 4664 for (; ; stmt = stmt->down) {
michael@0 4665 if (!stmt) {
michael@0 4666 report(ParseError, false, null(), JSMSG_BAD_CONTINUE);
michael@0 4667 return null();
michael@0 4668 }
michael@0 4669 if (stmt->isLoop())
michael@0 4670 break;
michael@0 4671 }
michael@0 4672 }
michael@0 4673
michael@0 4674 if (!MatchOrInsertSemicolon(tokenStream))
michael@0 4675 return null();
michael@0 4676
michael@0 4677 return handler.newContinueStatement(label, TokenPos(begin, pos().end));
michael@0 4678 }
michael@0 4679
michael@0 4680 template <typename ParseHandler>
michael@0 4681 typename ParseHandler::Node
michael@0 4682 Parser<ParseHandler>::breakStatement()
michael@0 4683 {
michael@0 4684 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_BREAK));
michael@0 4685 uint32_t begin = pos().begin;
michael@0 4686
michael@0 4687 RootedPropertyName label(context);
michael@0 4688 if (!matchLabel(&label))
michael@0 4689 return null();
michael@0 4690 StmtInfoPC *stmt = pc->topStmt;
michael@0 4691 if (label) {
michael@0 4692 for (; ; stmt = stmt->down) {
michael@0 4693 if (!stmt) {
michael@0 4694 report(ParseError, false, null(), JSMSG_LABEL_NOT_FOUND);
michael@0 4695 return null();
michael@0 4696 }
michael@0 4697 if (stmt->type == STMT_LABEL && stmt->label == label)
michael@0 4698 break;
michael@0 4699 }
michael@0 4700 } else {
michael@0 4701 for (; ; stmt = stmt->down) {
michael@0 4702 if (!stmt) {
michael@0 4703 report(ParseError, false, null(), JSMSG_TOUGH_BREAK);
michael@0 4704 return null();
michael@0 4705 }
michael@0 4706 if (stmt->isLoop() || stmt->type == STMT_SWITCH)
michael@0 4707 break;
michael@0 4708 }
michael@0 4709 }
michael@0 4710
michael@0 4711 if (!MatchOrInsertSemicolon(tokenStream))
michael@0 4712 return null();
michael@0 4713
michael@0 4714 return handler.newBreakStatement(label, TokenPos(begin, pos().end));
michael@0 4715 }
michael@0 4716
michael@0 4717 template <typename ParseHandler>
michael@0 4718 typename ParseHandler::Node
michael@0 4719 Parser<ParseHandler>::returnStatement()
michael@0 4720 {
michael@0 4721 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_RETURN));
michael@0 4722 uint32_t begin = pos().begin;
michael@0 4723
michael@0 4724 if (!pc->sc->isFunctionBox()) {
michael@0 4725 report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
michael@0 4726 return null();
michael@0 4727 }
michael@0 4728
michael@0 4729 // Parse an optional operand.
michael@0 4730 //
michael@0 4731 // This is ugly, but we don't want to require a semicolon.
michael@0 4732 Node exprNode;
michael@0 4733 switch (tokenStream.peekTokenSameLine(TokenStream::Operand)) {
michael@0 4734 case TOK_ERROR:
michael@0 4735 return null();
michael@0 4736 case TOK_EOF:
michael@0 4737 case TOK_EOL:
michael@0 4738 case TOK_SEMI:
michael@0 4739 case TOK_RC:
michael@0 4740 exprNode = null();
michael@0 4741 pc->funHasReturnVoid = true;
michael@0 4742 break;
michael@0 4743 default: {
michael@0 4744 exprNode = expr();
michael@0 4745 if (!exprNode)
michael@0 4746 return null();
michael@0 4747 pc->funHasReturnExpr = true;
michael@0 4748 }
michael@0 4749 }
michael@0 4750
michael@0 4751 if (!MatchOrInsertSemicolon(tokenStream))
michael@0 4752 return null();
michael@0 4753
michael@0 4754 Node pn = handler.newReturnStatement(exprNode, TokenPos(begin, pos().end));
michael@0 4755 if (!pn)
michael@0 4756 return null();
michael@0 4757
michael@0 4758 if (options().extraWarningsOption && pc->funHasReturnExpr && pc->funHasReturnVoid &&
michael@0 4759 !reportBadReturn(pn, ParseExtraWarning,
michael@0 4760 JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE))
michael@0 4761 {
michael@0 4762 return null();
michael@0 4763 }
michael@0 4764
michael@0 4765 if (pc->isLegacyGenerator() && exprNode) {
michael@0 4766 /* Disallow "return v;" in legacy generators. */
michael@0 4767 reportBadReturn(pn, ParseError, JSMSG_BAD_GENERATOR_RETURN,
michael@0 4768 JSMSG_BAD_ANON_GENERATOR_RETURN);
michael@0 4769 return null();
michael@0 4770 }
michael@0 4771
michael@0 4772 return pn;
michael@0 4773 }
michael@0 4774
michael@0 4775 template <typename ParseHandler>
michael@0 4776 typename ParseHandler::Node
michael@0 4777 Parser<ParseHandler>::yieldExpression()
michael@0 4778 {
michael@0 4779 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_YIELD));
michael@0 4780 uint32_t begin = pos().begin;
michael@0 4781
michael@0 4782 switch (pc->generatorKind()) {
michael@0 4783 case StarGenerator:
michael@0 4784 {
michael@0 4785 JS_ASSERT(pc->sc->isFunctionBox());
michael@0 4786
michael@0 4787 pc->lastYieldOffset = begin;
michael@0 4788
michael@0 4789 ParseNodeKind kind = tokenStream.matchToken(TOK_MUL) ? PNK_YIELD_STAR : PNK_YIELD;
michael@0 4790
michael@0 4791 // ES6 generators require a value.
michael@0 4792 Node exprNode = assignExpr();
michael@0 4793 if (!exprNode)
michael@0 4794 return null();
michael@0 4795
michael@0 4796 return handler.newUnary(kind, JSOP_NOP, begin, exprNode);
michael@0 4797 }
michael@0 4798
michael@0 4799 case NotGenerator:
michael@0 4800 // We are in code that has not seen a yield, but we are in JS 1.7 or
michael@0 4801 // later. Try to transition to being a legacy generator.
michael@0 4802 JS_ASSERT(tokenStream.versionNumber() >= JSVERSION_1_7);
michael@0 4803 JS_ASSERT(pc->lastYieldOffset == ParseContext<ParseHandler>::NoYieldOffset);
michael@0 4804
michael@0 4805 if (!abortIfSyntaxParser())
michael@0 4806 return null();
michael@0 4807
michael@0 4808 if (!pc->sc->isFunctionBox()) {
michael@0 4809 report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_yield_str);
michael@0 4810 return null();
michael@0 4811 }
michael@0 4812
michael@0 4813 pc->sc->asFunctionBox()->setGeneratorKind(LegacyGenerator);
michael@0 4814
michael@0 4815 if (pc->funHasReturnExpr) {
michael@0 4816 /* As in Python (see PEP-255), disallow return v; in generators. */
michael@0 4817 reportBadReturn(null(), ParseError, JSMSG_BAD_GENERATOR_RETURN,
michael@0 4818 JSMSG_BAD_ANON_GENERATOR_RETURN);
michael@0 4819 return null();
michael@0 4820 }
michael@0 4821 // Fall through.
michael@0 4822
michael@0 4823 case LegacyGenerator:
michael@0 4824 {
michael@0 4825 // We are in a legacy generator: a function that has already seen a
michael@0 4826 // yield, or in a legacy generator comprehension.
michael@0 4827 JS_ASSERT(pc->sc->isFunctionBox());
michael@0 4828
michael@0 4829 pc->lastYieldOffset = begin;
michael@0 4830
michael@0 4831 // Legacy generators do not require a value.
michael@0 4832 Node exprNode;
michael@0 4833 switch (tokenStream.peekTokenSameLine(TokenStream::Operand)) {
michael@0 4834 case TOK_ERROR:
michael@0 4835 return null();
michael@0 4836 case TOK_EOF:
michael@0 4837 case TOK_EOL:
michael@0 4838 case TOK_SEMI:
michael@0 4839 case TOK_RC:
michael@0 4840 case TOK_RB:
michael@0 4841 case TOK_RP:
michael@0 4842 case TOK_COLON:
michael@0 4843 case TOK_COMMA:
michael@0 4844 // No value.
michael@0 4845 exprNode = null();
michael@0 4846 // ES6 does not permit yield without an operand. We should
michael@0 4847 // encourage users of yield expressions of this kind to pass an
michael@0 4848 // operand, to bring users closer to standard syntax.
michael@0 4849 if (!reportWithOffset(ParseWarning, false, pos().begin, JSMSG_YIELD_WITHOUT_OPERAND))
michael@0 4850 return null();
michael@0 4851 break;
michael@0 4852 default:
michael@0 4853 exprNode = assignExpr();
michael@0 4854 if (!exprNode)
michael@0 4855 return null();
michael@0 4856 }
michael@0 4857
michael@0 4858 return handler.newUnary(PNK_YIELD, JSOP_NOP, begin, exprNode);
michael@0 4859 }
michael@0 4860 }
michael@0 4861
michael@0 4862 MOZ_ASSUME_UNREACHABLE("yieldExpr");
michael@0 4863 }
michael@0 4864
michael@0 4865 template <>
michael@0 4866 ParseNode *
michael@0 4867 Parser<FullParseHandler>::withStatement()
michael@0 4868 {
michael@0 4869 // test262/ch12/12.10/12.10-0-1.js fails if we try to parse with-statements
michael@0 4870 // in syntax-parse mode. See bug 892583.
michael@0 4871 if (handler.syntaxParser) {
michael@0 4872 handler.disableSyntaxParser();
michael@0 4873 abortedSyntaxParse = true;
michael@0 4874 return null();
michael@0 4875 }
michael@0 4876
michael@0 4877 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_WITH));
michael@0 4878 uint32_t begin = pos().begin;
michael@0 4879
michael@0 4880 // In most cases, we want the constructs forbidden in strict mode code to be
michael@0 4881 // a subset of those that JSOPTION_EXTRA_WARNINGS warns about, and we should
michael@0 4882 // use reportStrictModeError. However, 'with' is the sole instance of a
michael@0 4883 // construct that is forbidden in strict mode code, but doesn't even merit a
michael@0 4884 // warning under JSOPTION_EXTRA_WARNINGS. See
michael@0 4885 // https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1.
michael@0 4886 if (pc->sc->strict && !report(ParseStrictError, true, null(), JSMSG_STRICT_CODE_WITH))
michael@0 4887 return null();
michael@0 4888
michael@0 4889 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
michael@0 4890 Node objectExpr = exprInParens();
michael@0 4891 if (!objectExpr)
michael@0 4892 return null();
michael@0 4893 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
michael@0 4894
michael@0 4895 bool oldParsingWith = pc->parsingWith;
michael@0 4896 pc->parsingWith = true;
michael@0 4897
michael@0 4898 StmtInfoPC stmtInfo(context);
michael@0 4899 PushStatementPC(pc, &stmtInfo, STMT_WITH);
michael@0 4900 Rooted<StaticWithObject *> staticWith(context, StaticWithObject::create(context));
michael@0 4901 if (!staticWith)
michael@0 4902 return null();
michael@0 4903 staticWith->initEnclosingNestedScopeFromParser(pc->staticScope);
michael@0 4904 FinishPushNestedScope(pc, &stmtInfo, *staticWith);
michael@0 4905
michael@0 4906 Node innerBlock = statement();
michael@0 4907 if (!innerBlock)
michael@0 4908 return null();
michael@0 4909
michael@0 4910 PopStatementPC(tokenStream, pc);
michael@0 4911
michael@0 4912 pc->sc->setBindingsAccessedDynamically();
michael@0 4913 pc->parsingWith = oldParsingWith;
michael@0 4914
michael@0 4915 /*
michael@0 4916 * Make sure to deoptimize lexical dependencies inside the |with|
michael@0 4917 * to safely optimize binding globals (see bug 561923).
michael@0 4918 */
michael@0 4919 for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) {
michael@0 4920 DefinitionNode defn = r.front().value().get<FullParseHandler>();
michael@0 4921 DefinitionNode lexdep = handler.resolve(defn);
michael@0 4922 handler.deoptimizeUsesWithin(lexdep, TokenPos(begin, pos().begin));
michael@0 4923 }
michael@0 4924
michael@0 4925 ObjectBox *staticWithBox = newObjectBox(staticWith);
michael@0 4926 if (!staticWithBox)
michael@0 4927 return null();
michael@0 4928 return handler.newWithStatement(begin, objectExpr, innerBlock, staticWithBox);
michael@0 4929 }
michael@0 4930
michael@0 4931 template <>
michael@0 4932 SyntaxParseHandler::Node
michael@0 4933 Parser<SyntaxParseHandler>::withStatement()
michael@0 4934 {
michael@0 4935 JS_ALWAYS_FALSE(abortIfSyntaxParser());
michael@0 4936 return null();
michael@0 4937 }
michael@0 4938
michael@0 4939 template <typename ParseHandler>
michael@0 4940 typename ParseHandler::Node
michael@0 4941 Parser<ParseHandler>::labeledStatement()
michael@0 4942 {
michael@0 4943 uint32_t begin = pos().begin;
michael@0 4944 RootedPropertyName label(context, tokenStream.currentName());
michael@0 4945 for (StmtInfoPC *stmt = pc->topStmt; stmt; stmt = stmt->down) {
michael@0 4946 if (stmt->type == STMT_LABEL && stmt->label == label) {
michael@0 4947 report(ParseError, false, null(), JSMSG_DUPLICATE_LABEL);
michael@0 4948 return null();
michael@0 4949 }
michael@0 4950 }
michael@0 4951
michael@0 4952 tokenStream.consumeKnownToken(TOK_COLON);
michael@0 4953
michael@0 4954 /* Push a label struct and parse the statement. */
michael@0 4955 StmtInfoPC stmtInfo(context);
michael@0 4956 PushStatementPC(pc, &stmtInfo, STMT_LABEL);
michael@0 4957 stmtInfo.label = label;
michael@0 4958 Node pn = statement();
michael@0 4959 if (!pn)
michael@0 4960 return null();
michael@0 4961
michael@0 4962 /* Pop the label, set pn_expr, and return early. */
michael@0 4963 PopStatementPC(tokenStream, pc);
michael@0 4964
michael@0 4965 return handler.newLabeledStatement(label, pn, begin);
michael@0 4966 }
michael@0 4967
michael@0 4968 template <typename ParseHandler>
michael@0 4969 typename ParseHandler::Node
michael@0 4970 Parser<ParseHandler>::throwStatement()
michael@0 4971 {
michael@0 4972 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_THROW));
michael@0 4973 uint32_t begin = pos().begin;
michael@0 4974
michael@0 4975 /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
michael@0 4976 TokenKind tt = tokenStream.peekTokenSameLine(TokenStream::Operand);
michael@0 4977 if (tt == TOK_ERROR)
michael@0 4978 return null();
michael@0 4979 if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) {
michael@0 4980 report(ParseError, false, null(), JSMSG_SYNTAX_ERROR);
michael@0 4981 return null();
michael@0 4982 }
michael@0 4983
michael@0 4984 Node throwExpr = expr();
michael@0 4985 if (!throwExpr)
michael@0 4986 return null();
michael@0 4987
michael@0 4988 if (!MatchOrInsertSemicolon(tokenStream))
michael@0 4989 return null();
michael@0 4990
michael@0 4991 return handler.newThrowStatement(throwExpr, TokenPos(begin, pos().end));
michael@0 4992 }
michael@0 4993
michael@0 4994 template <typename ParseHandler>
michael@0 4995 typename ParseHandler::Node
michael@0 4996 Parser<ParseHandler>::tryStatement()
michael@0 4997 {
michael@0 4998 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_TRY));
michael@0 4999 uint32_t begin = pos().begin;
michael@0 5000
michael@0 5001 /*
michael@0 5002 * try nodes are ternary.
michael@0 5003 * kid1 is the try statement
michael@0 5004 * kid2 is the catch node list or null
michael@0 5005 * kid3 is the finally statement
michael@0 5006 *
michael@0 5007 * catch nodes are ternary.
michael@0 5008 * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
michael@0 5009 * kid2 is the catch guard or null if no guard
michael@0 5010 * kid3 is the catch block
michael@0 5011 *
michael@0 5012 * catch lvalue nodes are either:
michael@0 5013 * TOK_NAME for a single identifier
michael@0 5014 * TOK_RB or TOK_RC for a destructuring left-hand side
michael@0 5015 *
michael@0 5016 * finally nodes are TOK_LC statement lists.
michael@0 5017 */
michael@0 5018
michael@0 5019 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
michael@0 5020 StmtInfoPC stmtInfo(context);
michael@0 5021 if (!PushBlocklikeStatement(tokenStream, &stmtInfo, STMT_TRY, pc))
michael@0 5022 return null();
michael@0 5023 Node innerBlock = statements();
michael@0 5024 if (!innerBlock)
michael@0 5025 return null();
michael@0 5026 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
michael@0 5027 PopStatementPC(tokenStream, pc);
michael@0 5028
michael@0 5029 bool hasUnconditionalCatch = false;
michael@0 5030 Node catchList = null();
michael@0 5031 TokenKind tt = tokenStream.getToken();
michael@0 5032 if (tt == TOK_CATCH) {
michael@0 5033 catchList = handler.newList(PNK_CATCH);
michael@0 5034 if (!catchList)
michael@0 5035 return null();
michael@0 5036
michael@0 5037 do {
michael@0 5038 Node pnblock;
michael@0 5039 BindData<ParseHandler> data(context);
michael@0 5040
michael@0 5041 /* Check for another catch after unconditional catch. */
michael@0 5042 if (hasUnconditionalCatch) {
michael@0 5043 report(ParseError, false, null(), JSMSG_CATCH_AFTER_GENERAL);
michael@0 5044 return null();
michael@0 5045 }
michael@0 5046
michael@0 5047 /*
michael@0 5048 * Create a lexical scope node around the whole catch clause,
michael@0 5049 * including the head.
michael@0 5050 */
michael@0 5051 pnblock = pushLexicalScope(&stmtInfo);
michael@0 5052 if (!pnblock)
michael@0 5053 return null();
michael@0 5054 stmtInfo.type = STMT_CATCH;
michael@0 5055
michael@0 5056 /*
michael@0 5057 * Legal catch forms are:
michael@0 5058 * catch (lhs)
michael@0 5059 * catch (lhs if <boolean_expression>)
michael@0 5060 * where lhs is a name or a destructuring left-hand side.
michael@0 5061 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
michael@0 5062 */
michael@0 5063 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
michael@0 5064
michael@0 5065 /*
michael@0 5066 * Contrary to ECMA Ed. 3, the catch variable is lexically
michael@0 5067 * scoped, not a property of a new Object instance. This is
michael@0 5068 * an intentional change that anticipates ECMA Ed. 4.
michael@0 5069 */
michael@0 5070 data.initLet(HoistVars, pc->staticScope->template as<StaticBlockObject>(),
michael@0 5071 JSMSG_TOO_MANY_CATCH_VARS);
michael@0 5072 JS_ASSERT(data.let.blockObj);
michael@0 5073
michael@0 5074 tt = tokenStream.getToken();
michael@0 5075 Node catchName;
michael@0 5076 switch (tt) {
michael@0 5077 case TOK_LB:
michael@0 5078 case TOK_LC:
michael@0 5079 catchName = destructuringExpr(&data, tt);
michael@0 5080 if (!catchName)
michael@0 5081 return null();
michael@0 5082 break;
michael@0 5083
michael@0 5084 case TOK_YIELD:
michael@0 5085 if (!checkYieldNameValidity())
michael@0 5086 return null();
michael@0 5087 // Fall through.
michael@0 5088 case TOK_NAME:
michael@0 5089 {
michael@0 5090 RootedPropertyName label(context, tokenStream.currentName());
michael@0 5091 catchName = newBindingNode(label, false);
michael@0 5092 if (!catchName)
michael@0 5093 return null();
michael@0 5094 data.pn = catchName;
michael@0 5095 if (!data.binder(&data, label, this))
michael@0 5096 return null();
michael@0 5097 break;
michael@0 5098 }
michael@0 5099
michael@0 5100 default:
michael@0 5101 report(ParseError, false, null(), JSMSG_CATCH_IDENTIFIER);
michael@0 5102 return null();
michael@0 5103 }
michael@0 5104
michael@0 5105 Node catchGuard = null();
michael@0 5106 #if JS_HAS_CATCH_GUARD
michael@0 5107 /*
michael@0 5108 * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
michael@0 5109 * to avoid conflicting with the JS2/ECMAv4 type annotation
michael@0 5110 * catchguard syntax.
michael@0 5111 */
michael@0 5112 if (tokenStream.matchToken(TOK_IF)) {
michael@0 5113 catchGuard = expr();
michael@0 5114 if (!catchGuard)
michael@0 5115 return null();
michael@0 5116 }
michael@0 5117 #endif
michael@0 5118 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
michael@0 5119
michael@0 5120 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
michael@0 5121 Node catchBody = statements();
michael@0 5122 if (!catchBody)
michael@0 5123 return null();
michael@0 5124 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
michael@0 5125 PopStatementPC(tokenStream, pc);
michael@0 5126
michael@0 5127 if (!catchGuard)
michael@0 5128 hasUnconditionalCatch = true;
michael@0 5129
michael@0 5130 if (!handler.addCatchBlock(catchList, pnblock, catchName, catchGuard, catchBody))
michael@0 5131 return null();
michael@0 5132 handler.setEndPosition(catchList, pos().end);
michael@0 5133 handler.setEndPosition(pnblock, pos().end);
michael@0 5134
michael@0 5135 tt = tokenStream.getToken(TokenStream::Operand);
michael@0 5136 } while (tt == TOK_CATCH);
michael@0 5137 }
michael@0 5138
michael@0 5139 Node finallyBlock = null();
michael@0 5140
michael@0 5141 if (tt == TOK_FINALLY) {
michael@0 5142 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
michael@0 5143 if (!PushBlocklikeStatement(tokenStream, &stmtInfo, STMT_FINALLY, pc))
michael@0 5144 return null();
michael@0 5145 finallyBlock = statements();
michael@0 5146 if (!finallyBlock)
michael@0 5147 return null();
michael@0 5148 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
michael@0 5149 PopStatementPC(tokenStream, pc);
michael@0 5150 } else {
michael@0 5151 tokenStream.ungetToken();
michael@0 5152 }
michael@0 5153 if (!catchList && !finallyBlock) {
michael@0 5154 report(ParseError, false, null(), JSMSG_CATCH_OR_FINALLY);
michael@0 5155 return null();
michael@0 5156 }
michael@0 5157
michael@0 5158 return handler.newTryStatement(begin, innerBlock, catchList, finallyBlock);
michael@0 5159 }
michael@0 5160
michael@0 5161 template <typename ParseHandler>
michael@0 5162 typename ParseHandler::Node
michael@0 5163 Parser<ParseHandler>::debuggerStatement()
michael@0 5164 {
michael@0 5165 TokenPos p;
michael@0 5166 p.begin = pos().begin;
michael@0 5167 if (!MatchOrInsertSemicolon(tokenStream))
michael@0 5168 return null();
michael@0 5169 p.end = pos().end;
michael@0 5170
michael@0 5171 pc->sc->setBindingsAccessedDynamically();
michael@0 5172 pc->sc->setHasDebuggerStatement();
michael@0 5173
michael@0 5174 return handler.newDebuggerStatement(p);
michael@0 5175 }
michael@0 5176
michael@0 5177 template <typename ParseHandler>
michael@0 5178 typename ParseHandler::Node
michael@0 5179 Parser<ParseHandler>::statement(bool canHaveDirectives)
michael@0 5180 {
michael@0 5181 JS_CHECK_RECURSION(context, return null());
michael@0 5182
michael@0 5183 switch (TokenKind tt = tokenStream.getToken(TokenStream::Operand)) {
michael@0 5184 case TOK_LC:
michael@0 5185 return blockStatement();
michael@0 5186
michael@0 5187 case TOK_CONST:
michael@0 5188 if (!abortIfSyntaxParser())
michael@0 5189 return null();
michael@0 5190 // FALL THROUGH
michael@0 5191 case TOK_VAR: {
michael@0 5192 Node pn = variables(tt == TOK_CONST ? PNK_CONST : PNK_VAR);
michael@0 5193 if (!pn)
michael@0 5194 return null();
michael@0 5195
michael@0 5196 // Tell js_EmitTree to generate a final POP.
michael@0 5197 handler.setListFlag(pn, PNX_POPVAR);
michael@0 5198
michael@0 5199 if (!MatchOrInsertSemicolon(tokenStream))
michael@0 5200 return null();
michael@0 5201 return pn;
michael@0 5202 }
michael@0 5203
michael@0 5204 case TOK_LET:
michael@0 5205 return letStatement();
michael@0 5206 case TOK_IMPORT:
michael@0 5207 return importDeclaration();
michael@0 5208 case TOK_EXPORT:
michael@0 5209 return exportDeclaration();
michael@0 5210 case TOK_SEMI:
michael@0 5211 return handler.newEmptyStatement(pos());
michael@0 5212 case TOK_IF:
michael@0 5213 return ifStatement();
michael@0 5214 case TOK_DO:
michael@0 5215 return doWhileStatement();
michael@0 5216 case TOK_WHILE:
michael@0 5217 return whileStatement();
michael@0 5218 case TOK_FOR:
michael@0 5219 return forStatement();
michael@0 5220 case TOK_SWITCH:
michael@0 5221 return switchStatement();
michael@0 5222 case TOK_CONTINUE:
michael@0 5223 return continueStatement();
michael@0 5224 case TOK_BREAK:
michael@0 5225 return breakStatement();
michael@0 5226 case TOK_RETURN:
michael@0 5227 return returnStatement();
michael@0 5228 case TOK_WITH:
michael@0 5229 return withStatement();
michael@0 5230 case TOK_THROW:
michael@0 5231 return throwStatement();
michael@0 5232 case TOK_TRY:
michael@0 5233 return tryStatement();
michael@0 5234 case TOK_FUNCTION:
michael@0 5235 return functionStmt();
michael@0 5236 case TOK_DEBUGGER:
michael@0 5237 return debuggerStatement();
michael@0 5238
michael@0 5239 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
michael@0 5240 case TOK_CATCH:
michael@0 5241 report(ParseError, false, null(), JSMSG_CATCH_WITHOUT_TRY);
michael@0 5242 return null();
michael@0 5243
michael@0 5244 case TOK_FINALLY:
michael@0 5245 report(ParseError, false, null(), JSMSG_FINALLY_WITHOUT_TRY);
michael@0 5246 return null();
michael@0 5247
michael@0 5248 case TOK_ERROR:
michael@0 5249 return null();
michael@0 5250
michael@0 5251 case TOK_STRING:
michael@0 5252 if (!canHaveDirectives && tokenStream.currentToken().atom() == context->names().useAsm) {
michael@0 5253 if (!abortIfSyntaxParser())
michael@0 5254 return null();
michael@0 5255 if (!report(ParseWarning, false, null(), JSMSG_USE_ASM_DIRECTIVE_FAIL))
michael@0 5256 return null();
michael@0 5257 }
michael@0 5258 return expressionStatement();
michael@0 5259
michael@0 5260 case TOK_YIELD:
michael@0 5261 if (tokenStream.peekToken() == TOK_COLON) {
michael@0 5262 if (!checkYieldNameValidity())
michael@0 5263 return null();
michael@0 5264 return labeledStatement();
michael@0 5265 }
michael@0 5266 return expressionStatement();
michael@0 5267
michael@0 5268 case TOK_NAME:
michael@0 5269 if (tokenStream.peekToken() == TOK_COLON)
michael@0 5270 return labeledStatement();
michael@0 5271 return expressionStatement();
michael@0 5272
michael@0 5273 default:
michael@0 5274 return expressionStatement();
michael@0 5275 }
michael@0 5276 }
michael@0 5277
michael@0 5278 template <typename ParseHandler>
michael@0 5279 typename ParseHandler::Node
michael@0 5280 Parser<ParseHandler>::expr()
michael@0 5281 {
michael@0 5282 Node pn = assignExpr();
michael@0 5283 if (pn && tokenStream.matchToken(TOK_COMMA)) {
michael@0 5284 Node seq = handler.newList(PNK_COMMA, pn);
michael@0 5285 if (!seq)
michael@0 5286 return null();
michael@0 5287 do {
michael@0 5288 if (handler.isUnparenthesizedYield(pn)) {
michael@0 5289 report(ParseError, false, pn, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
michael@0 5290 return null();
michael@0 5291 }
michael@0 5292
michael@0 5293 pn = assignExpr();
michael@0 5294 if (!pn)
michael@0 5295 return null();
michael@0 5296 handler.addList(seq, pn);
michael@0 5297 } while (tokenStream.matchToken(TOK_COMMA));
michael@0 5298 return seq;
michael@0 5299 }
michael@0 5300 return pn;
michael@0 5301 }
michael@0 5302
michael@0 5303 static const JSOp ParseNodeKindToJSOp[] = {
michael@0 5304 JSOP_OR,
michael@0 5305 JSOP_AND,
michael@0 5306 JSOP_BITOR,
michael@0 5307 JSOP_BITXOR,
michael@0 5308 JSOP_BITAND,
michael@0 5309 JSOP_STRICTEQ,
michael@0 5310 JSOP_EQ,
michael@0 5311 JSOP_STRICTNE,
michael@0 5312 JSOP_NE,
michael@0 5313 JSOP_LT,
michael@0 5314 JSOP_LE,
michael@0 5315 JSOP_GT,
michael@0 5316 JSOP_GE,
michael@0 5317 JSOP_INSTANCEOF,
michael@0 5318 JSOP_IN,
michael@0 5319 JSOP_LSH,
michael@0 5320 JSOP_RSH,
michael@0 5321 JSOP_URSH,
michael@0 5322 JSOP_ADD,
michael@0 5323 JSOP_SUB,
michael@0 5324 JSOP_MUL,
michael@0 5325 JSOP_DIV,
michael@0 5326 JSOP_MOD
michael@0 5327 };
michael@0 5328
michael@0 5329 static inline JSOp
michael@0 5330 BinaryOpParseNodeKindToJSOp(ParseNodeKind pnk)
michael@0 5331 {
michael@0 5332 JS_ASSERT(pnk >= PNK_BINOP_FIRST);
michael@0 5333 JS_ASSERT(pnk <= PNK_BINOP_LAST);
michael@0 5334 return ParseNodeKindToJSOp[pnk - PNK_BINOP_FIRST];
michael@0 5335 }
michael@0 5336
michael@0 5337 static bool
michael@0 5338 IsBinaryOpToken(TokenKind tok, bool parsingForInit)
michael@0 5339 {
michael@0 5340 return tok == TOK_IN ? !parsingForInit : TokenKindIsBinaryOp(tok);
michael@0 5341 }
michael@0 5342
michael@0 5343 static ParseNodeKind
michael@0 5344 BinaryOpTokenKindToParseNodeKind(TokenKind tok)
michael@0 5345 {
michael@0 5346 JS_ASSERT(TokenKindIsBinaryOp(tok));
michael@0 5347 return ParseNodeKind(PNK_BINOP_FIRST + (tok - TOK_BINOP_FIRST));
michael@0 5348 }
michael@0 5349
michael@0 5350 static const int PrecedenceTable[] = {
michael@0 5351 1, /* PNK_OR */
michael@0 5352 2, /* PNK_AND */
michael@0 5353 3, /* PNK_BITOR */
michael@0 5354 4, /* PNK_BITXOR */
michael@0 5355 5, /* PNK_BITAND */
michael@0 5356 6, /* PNK_STRICTEQ */
michael@0 5357 6, /* PNK_EQ */
michael@0 5358 6, /* PNK_STRICTNE */
michael@0 5359 6, /* PNK_NE */
michael@0 5360 7, /* PNK_LT */
michael@0 5361 7, /* PNK_LE */
michael@0 5362 7, /* PNK_GT */
michael@0 5363 7, /* PNK_GE */
michael@0 5364 7, /* PNK_INSTANCEOF */
michael@0 5365 7, /* PNK_IN */
michael@0 5366 8, /* PNK_LSH */
michael@0 5367 8, /* PNK_RSH */
michael@0 5368 8, /* PNK_URSH */
michael@0 5369 9, /* PNK_ADD */
michael@0 5370 9, /* PNK_SUB */
michael@0 5371 10, /* PNK_STAR */
michael@0 5372 10, /* PNK_DIV */
michael@0 5373 10 /* PNK_MOD */
michael@0 5374 };
michael@0 5375
michael@0 5376 static const int PRECEDENCE_CLASSES = 10;
michael@0 5377
michael@0 5378 static int
michael@0 5379 Precedence(ParseNodeKind pnk) {
michael@0 5380 // Everything binds tighter than PNK_LIMIT, because we want to reduce all
michael@0 5381 // nodes to a single node when we reach a token that is not another binary
michael@0 5382 // operator.
michael@0 5383 if (pnk == PNK_LIMIT)
michael@0 5384 return 0;
michael@0 5385
michael@0 5386 JS_ASSERT(pnk >= PNK_BINOP_FIRST);
michael@0 5387 JS_ASSERT(pnk <= PNK_BINOP_LAST);
michael@0 5388 return PrecedenceTable[pnk - PNK_BINOP_FIRST];
michael@0 5389 }
michael@0 5390
michael@0 5391 template <typename ParseHandler>
michael@0 5392 MOZ_ALWAYS_INLINE typename ParseHandler::Node
michael@0 5393 Parser<ParseHandler>::orExpr1()
michael@0 5394 {
michael@0 5395 // Shift-reduce parser for the left-associative binary operator part of
michael@0 5396 // the JS syntax.
michael@0 5397
michael@0 5398 // Conceptually there's just one stack, a stack of pairs (lhs, op).
michael@0 5399 // It's implemented using two separate arrays, though.
michael@0 5400 Node nodeStack[PRECEDENCE_CLASSES];
michael@0 5401 ParseNodeKind kindStack[PRECEDENCE_CLASSES];
michael@0 5402 int depth = 0;
michael@0 5403
michael@0 5404 bool oldParsingForInit = pc->parsingForInit;
michael@0 5405 pc->parsingForInit = false;
michael@0 5406
michael@0 5407 Node pn;
michael@0 5408 for (;;) {
michael@0 5409 pn = unaryExpr();
michael@0 5410 if (!pn)
michael@0 5411 return pn;
michael@0 5412
michael@0 5413 // If a binary operator follows, consume it and compute the
michael@0 5414 // corresponding operator.
michael@0 5415 TokenKind tok = tokenStream.getToken();
michael@0 5416 if (tok == TOK_ERROR)
michael@0 5417 return null();
michael@0 5418 ParseNodeKind pnk;
michael@0 5419 if (IsBinaryOpToken(tok, oldParsingForInit)) {
michael@0 5420 pnk = BinaryOpTokenKindToParseNodeKind(tok);
michael@0 5421 } else {
michael@0 5422 tok = TOK_EOF;
michael@0 5423 pnk = PNK_LIMIT;
michael@0 5424 }
michael@0 5425
michael@0 5426 // If pnk has precedence less than or equal to another operator on the
michael@0 5427 // stack, reduce. This combines nodes on the stack until we form the
michael@0 5428 // actual lhs of pnk.
michael@0 5429 //
michael@0 5430 // The >= in this condition works because all the operators in question
michael@0 5431 // are left-associative; if any were not, the case where two operators
michael@0 5432 // have equal precedence would need to be handled specially, and the
michael@0 5433 // stack would need to be a Vector.
michael@0 5434 while (depth > 0 && Precedence(kindStack[depth - 1]) >= Precedence(pnk)) {
michael@0 5435 depth--;
michael@0 5436 ParseNodeKind combiningPnk = kindStack[depth];
michael@0 5437 JSOp combiningOp = BinaryOpParseNodeKindToJSOp(combiningPnk);
michael@0 5438 pn = handler.newBinaryOrAppend(combiningPnk, nodeStack[depth], pn, pc, combiningOp);
michael@0 5439 if (!pn)
michael@0 5440 return pn;
michael@0 5441 }
michael@0 5442
michael@0 5443 if (pnk == PNK_LIMIT)
michael@0 5444 break;
michael@0 5445
michael@0 5446 nodeStack[depth] = pn;
michael@0 5447 kindStack[depth] = pnk;
michael@0 5448 depth++;
michael@0 5449 JS_ASSERT(depth <= PRECEDENCE_CLASSES);
michael@0 5450 }
michael@0 5451
michael@0 5452 JS_ASSERT(depth == 0);
michael@0 5453 pc->parsingForInit = oldParsingForInit;
michael@0 5454 return pn;
michael@0 5455 }
michael@0 5456
michael@0 5457 template <typename ParseHandler>
michael@0 5458 MOZ_ALWAYS_INLINE typename ParseHandler::Node
michael@0 5459 Parser<ParseHandler>::condExpr1()
michael@0 5460 {
michael@0 5461 Node condition = orExpr1();
michael@0 5462 if (!condition || !tokenStream.isCurrentTokenType(TOK_HOOK))
michael@0 5463 return condition;
michael@0 5464
michael@0 5465 /*
michael@0 5466 * Always accept the 'in' operator in the middle clause of a ternary,
michael@0 5467 * where it's unambiguous, even if we might be parsing the init of a
michael@0 5468 * for statement.
michael@0 5469 */
michael@0 5470 bool oldParsingForInit = pc->parsingForInit;
michael@0 5471 pc->parsingForInit = false;
michael@0 5472 Node thenExpr = assignExpr();
michael@0 5473 pc->parsingForInit = oldParsingForInit;
michael@0 5474 if (!thenExpr)
michael@0 5475 return null();
michael@0 5476
michael@0 5477 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
michael@0 5478
michael@0 5479 Node elseExpr = assignExpr();
michael@0 5480 if (!elseExpr)
michael@0 5481 return null();
michael@0 5482
michael@0 5483 tokenStream.getToken(); /* read one token past the end */
michael@0 5484 return handler.newConditional(condition, thenExpr, elseExpr);
michael@0 5485 }
michael@0 5486
michael@0 5487 template <>
michael@0 5488 bool
michael@0 5489 Parser<FullParseHandler>::checkAndMarkAsAssignmentLhs(ParseNode *pn, AssignmentFlavor flavor)
michael@0 5490 {
michael@0 5491 switch (pn->getKind()) {
michael@0 5492 case PNK_NAME:
michael@0 5493 if (!checkStrictAssignment(pn, flavor))
michael@0 5494 return false;
michael@0 5495 if (flavor == KeyedDestructuringAssignment) {
michael@0 5496 /*
michael@0 5497 * We may be called on a name node that has already been
michael@0 5498 * specialized, in the very weird "for (var [x] = i in o) ..."
michael@0 5499 * case. See bug 558633.
michael@0 5500 */
michael@0 5501 if (!(js_CodeSpec[pn->getOp()].format & JOF_SET))
michael@0 5502 pn->setOp(JSOP_SETNAME);
michael@0 5503 } else {
michael@0 5504 pn->setOp(pn->isOp(JSOP_GETLOCAL) ? JSOP_SETLOCAL : JSOP_SETNAME);
michael@0 5505 }
michael@0 5506 pn->markAsAssigned();
michael@0 5507 break;
michael@0 5508
michael@0 5509 case PNK_DOT:
michael@0 5510 case PNK_ELEM:
michael@0 5511 break;
michael@0 5512
michael@0 5513 case PNK_ARRAY:
michael@0 5514 case PNK_OBJECT:
michael@0 5515 if (flavor == CompoundAssignment) {
michael@0 5516 report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_ASS);
michael@0 5517 return false;
michael@0 5518 }
michael@0 5519 if (!checkDestructuring(nullptr, pn))
michael@0 5520 return false;
michael@0 5521 break;
michael@0 5522
michael@0 5523 case PNK_CALL:
michael@0 5524 if (!makeSetCall(pn, JSMSG_BAD_LEFTSIDE_OF_ASS))
michael@0 5525 return false;
michael@0 5526 break;
michael@0 5527
michael@0 5528 default:
michael@0 5529 report(ParseError, false, pn, JSMSG_BAD_LEFTSIDE_OF_ASS);
michael@0 5530 return false;
michael@0 5531 }
michael@0 5532 return true;
michael@0 5533 }
michael@0 5534
michael@0 5535 template <>
michael@0 5536 bool
michael@0 5537 Parser<SyntaxParseHandler>::checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor)
michael@0 5538 {
michael@0 5539 /* Full syntax checking of valid assignment LHS terms requires a parse tree. */
michael@0 5540 if (pn != SyntaxParseHandler::NodeName &&
michael@0 5541 pn != SyntaxParseHandler::NodeGetProp &&
michael@0 5542 pn != SyntaxParseHandler::NodeLValue)
michael@0 5543 {
michael@0 5544 return abortIfSyntaxParser();
michael@0 5545 }
michael@0 5546 return checkStrictAssignment(pn, flavor);
michael@0 5547 }
michael@0 5548
michael@0 5549 template <typename ParseHandler>
michael@0 5550 typename ParseHandler::Node
michael@0 5551 Parser<ParseHandler>::assignExpr()
michael@0 5552 {
michael@0 5553 JS_CHECK_RECURSION(context, return null());
michael@0 5554
michael@0 5555 // It's very common at this point to have a "detectably simple" expression,
michael@0 5556 // i.e. a name/number/string token followed by one of the following tokens
michael@0 5557 // that obviously isn't part of an expression: , ; : ) ] }
michael@0 5558 //
michael@0 5559 // (In Parsemark this happens 81.4% of the time; in code with large
michael@0 5560 // numeric arrays, such as some Kraken benchmarks, it happens more often.)
michael@0 5561 //
michael@0 5562 // In such cases, we can avoid the full expression parsing route through
michael@0 5563 // assignExpr(), condExpr1(), orExpr1(), unaryExpr(), memberExpr(), and
michael@0 5564 // primaryExpr().
michael@0 5565
michael@0 5566 TokenKind tt = tokenStream.getToken(TokenStream::Operand);
michael@0 5567
michael@0 5568 if (tt == TOK_NAME && tokenStream.nextTokenEndsExpr())
michael@0 5569 return identifierName();
michael@0 5570
michael@0 5571 if (tt == TOK_NUMBER && tokenStream.nextTokenEndsExpr())
michael@0 5572 return newNumber(tokenStream.currentToken());
michael@0 5573
michael@0 5574 if (tt == TOK_STRING && tokenStream.nextTokenEndsExpr())
michael@0 5575 return stringLiteral();
michael@0 5576
michael@0 5577 if (tt == TOK_YIELD && (versionNumber() >= JSVERSION_1_7 || pc->isGenerator()))
michael@0 5578 return yieldExpression();
michael@0 5579
michael@0 5580 tokenStream.ungetToken();
michael@0 5581
michael@0 5582 // Save the tokenizer state in case we find an arrow function and have to
michael@0 5583 // rewind.
michael@0 5584 TokenStream::Position start(keepAtoms);
michael@0 5585 tokenStream.tell(&start);
michael@0 5586
michael@0 5587 Node lhs = condExpr1();
michael@0 5588 if (!lhs)
michael@0 5589 return null();
michael@0 5590
michael@0 5591 ParseNodeKind kind;
michael@0 5592 JSOp op;
michael@0 5593 switch (tokenStream.currentToken().type) {
michael@0 5594 case TOK_ASSIGN: kind = PNK_ASSIGN; op = JSOP_NOP; break;
michael@0 5595 case TOK_ADDASSIGN: kind = PNK_ADDASSIGN; op = JSOP_ADD; break;
michael@0 5596 case TOK_SUBASSIGN: kind = PNK_SUBASSIGN; op = JSOP_SUB; break;
michael@0 5597 case TOK_BITORASSIGN: kind = PNK_BITORASSIGN; op = JSOP_BITOR; break;
michael@0 5598 case TOK_BITXORASSIGN: kind = PNK_BITXORASSIGN; op = JSOP_BITXOR; break;
michael@0 5599 case TOK_BITANDASSIGN: kind = PNK_BITANDASSIGN; op = JSOP_BITAND; break;
michael@0 5600 case TOK_LSHASSIGN: kind = PNK_LSHASSIGN; op = JSOP_LSH; break;
michael@0 5601 case TOK_RSHASSIGN: kind = PNK_RSHASSIGN; op = JSOP_RSH; break;
michael@0 5602 case TOK_URSHASSIGN: kind = PNK_URSHASSIGN; op = JSOP_URSH; break;
michael@0 5603 case TOK_MULASSIGN: kind = PNK_MULASSIGN; op = JSOP_MUL; break;
michael@0 5604 case TOK_DIVASSIGN: kind = PNK_DIVASSIGN; op = JSOP_DIV; break;
michael@0 5605 case TOK_MODASSIGN: kind = PNK_MODASSIGN; op = JSOP_MOD; break;
michael@0 5606
michael@0 5607 case TOK_ARROW: {
michael@0 5608 tokenStream.seek(start);
michael@0 5609 if (!abortIfSyntaxParser())
michael@0 5610 return null();
michael@0 5611
michael@0 5612 if (tokenStream.getToken() == TOK_ERROR)
michael@0 5613 return null();
michael@0 5614 tokenStream.ungetToken();
michael@0 5615
michael@0 5616 return functionDef(NullPtr(), start, Normal, Arrow, NotGenerator);
michael@0 5617 }
michael@0 5618
michael@0 5619 default:
michael@0 5620 JS_ASSERT(!tokenStream.isCurrentTokenAssignment());
michael@0 5621 tokenStream.ungetToken();
michael@0 5622 return lhs;
michael@0 5623 }
michael@0 5624
michael@0 5625 AssignmentFlavor flavor = kind == PNK_ASSIGN ? PlainAssignment : CompoundAssignment;
michael@0 5626 if (!checkAndMarkAsAssignmentLhs(lhs, flavor))
michael@0 5627 return null();
michael@0 5628
michael@0 5629 Node rhs = assignExpr();
michael@0 5630 if (!rhs)
michael@0 5631 return null();
michael@0 5632
michael@0 5633 return handler.newBinaryOrAppend(kind, lhs, rhs, pc, op);
michael@0 5634 }
michael@0 5635
michael@0 5636 static const char incop_name_str[][10] = {"increment", "decrement"};
michael@0 5637
michael@0 5638 template <>
michael@0 5639 bool
michael@0 5640 Parser<FullParseHandler>::checkAndMarkAsIncOperand(ParseNode *kid, TokenKind tt, bool preorder)
michael@0 5641 {
michael@0 5642 // Check.
michael@0 5643 if (!kid->isKind(PNK_NAME) &&
michael@0 5644 !kid->isKind(PNK_DOT) &&
michael@0 5645 !kid->isKind(PNK_ELEM) &&
michael@0 5646 !(kid->isKind(PNK_CALL) &&
michael@0 5647 (kid->isOp(JSOP_CALL) || kid->isOp(JSOP_SPREADCALL) ||
michael@0 5648 kid->isOp(JSOP_EVAL) || kid->isOp(JSOP_SPREADEVAL) ||
michael@0 5649 kid->isOp(JSOP_FUNCALL) ||
michael@0 5650 kid->isOp(JSOP_FUNAPPLY))))
michael@0 5651 {
michael@0 5652 report(ParseError, false, null(), JSMSG_BAD_OPERAND, incop_name_str[tt == TOK_DEC]);
michael@0 5653 return false;
michael@0 5654 }
michael@0 5655
michael@0 5656 if (!checkStrictAssignment(kid, IncDecAssignment))
michael@0 5657 return false;
michael@0 5658
michael@0 5659 // Mark.
michael@0 5660 if (kid->isKind(PNK_NAME)) {
michael@0 5661 kid->markAsAssigned();
michael@0 5662 } else if (kid->isKind(PNK_CALL)) {
michael@0 5663 if (!makeSetCall(kid, JSMSG_BAD_INCOP_OPERAND))
michael@0 5664 return false;
michael@0 5665 }
michael@0 5666 return true;
michael@0 5667 }
michael@0 5668
michael@0 5669 template <>
michael@0 5670 bool
michael@0 5671 Parser<SyntaxParseHandler>::checkAndMarkAsIncOperand(Node kid, TokenKind tt, bool preorder)
michael@0 5672 {
michael@0 5673 // To the extent of what we support in syntax-parse mode, the rules for
michael@0 5674 // inc/dec operands are the same as for assignment. There are differences,
michael@0 5675 // such as destructuring; but if we hit any of those cases, we'll abort and
michael@0 5676 // reparse in full mode.
michael@0 5677 return checkAndMarkAsAssignmentLhs(kid, IncDecAssignment);
michael@0 5678 }
michael@0 5679
michael@0 5680 template <typename ParseHandler>
michael@0 5681 typename ParseHandler::Node
michael@0 5682 Parser<ParseHandler>::unaryOpExpr(ParseNodeKind kind, JSOp op, uint32_t begin)
michael@0 5683 {
michael@0 5684 Node kid = unaryExpr();
michael@0 5685 if (!kid)
michael@0 5686 return null();
michael@0 5687 return handler.newUnary(kind, op, begin, kid);
michael@0 5688 }
michael@0 5689
michael@0 5690 template <typename ParseHandler>
michael@0 5691 typename ParseHandler::Node
michael@0 5692 Parser<ParseHandler>::unaryExpr()
michael@0 5693 {
michael@0 5694 Node pn, pn2;
michael@0 5695
michael@0 5696 JS_CHECK_RECURSION(context, return null());
michael@0 5697
michael@0 5698 TokenKind tt = tokenStream.getToken(TokenStream::Operand);
michael@0 5699 uint32_t begin = pos().begin;
michael@0 5700 switch (tt) {
michael@0 5701 case TOK_TYPEOF:
michael@0 5702 return unaryOpExpr(PNK_TYPEOF, JSOP_TYPEOF, begin);
michael@0 5703 case TOK_VOID:
michael@0 5704 return unaryOpExpr(PNK_VOID, JSOP_VOID, begin);
michael@0 5705 case TOK_NOT:
michael@0 5706 return unaryOpExpr(PNK_NOT, JSOP_NOT, begin);
michael@0 5707 case TOK_BITNOT:
michael@0 5708 return unaryOpExpr(PNK_BITNOT, JSOP_BITNOT, begin);
michael@0 5709 case TOK_ADD:
michael@0 5710 return unaryOpExpr(PNK_POS, JSOP_POS, begin);
michael@0 5711 case TOK_SUB:
michael@0 5712 return unaryOpExpr(PNK_NEG, JSOP_NEG, begin);
michael@0 5713
michael@0 5714 case TOK_INC:
michael@0 5715 case TOK_DEC:
michael@0 5716 {
michael@0 5717 TokenKind tt2 = tokenStream.getToken(TokenStream::Operand);
michael@0 5718 pn2 = memberExpr(tt2, true);
michael@0 5719 if (!pn2)
michael@0 5720 return null();
michael@0 5721 if (!checkAndMarkAsIncOperand(pn2, tt, true))
michael@0 5722 return null();
michael@0 5723 return handler.newUnary((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT,
michael@0 5724 JSOP_NOP,
michael@0 5725 begin,
michael@0 5726 pn2);
michael@0 5727 }
michael@0 5728
michael@0 5729 case TOK_DELETE: {
michael@0 5730 Node expr = unaryExpr();
michael@0 5731 if (!expr)
michael@0 5732 return null();
michael@0 5733
michael@0 5734 // Per spec, deleting any unary expression is valid -- it simply
michael@0 5735 // returns true -- except for one case that is illegal in strict mode.
michael@0 5736 if (handler.isName(expr)) {
michael@0 5737 if (!report(ParseStrictError, pc->sc->strict, expr, JSMSG_DEPRECATED_DELETE_OPERAND))
michael@0 5738 return null();
michael@0 5739 pc->sc->setBindingsAccessedDynamically();
michael@0 5740 }
michael@0 5741
michael@0 5742 return handler.newDelete(begin, expr);
michael@0 5743 }
michael@0 5744
michael@0 5745 case TOK_ERROR:
michael@0 5746 return null();
michael@0 5747
michael@0 5748 default:
michael@0 5749 pn = memberExpr(tt, true);
michael@0 5750 if (!pn)
michael@0 5751 return null();
michael@0 5752
michael@0 5753 /* Don't look across a newline boundary for a postfix incop. */
michael@0 5754 tt = tokenStream.peekTokenSameLine(TokenStream::Operand);
michael@0 5755 if (tt == TOK_INC || tt == TOK_DEC) {
michael@0 5756 tokenStream.consumeKnownToken(tt);
michael@0 5757 if (!checkAndMarkAsIncOperand(pn, tt, false))
michael@0 5758 return null();
michael@0 5759 return handler.newUnary((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT,
michael@0 5760 JSOP_NOP,
michael@0 5761 begin,
michael@0 5762 pn);
michael@0 5763 }
michael@0 5764 return pn;
michael@0 5765 }
michael@0 5766 MOZ_ASSUME_UNREACHABLE("unaryExpr");
michael@0 5767 }
michael@0 5768
michael@0 5769 /*
michael@0 5770 * A dedicated helper for transplanting the legacy comprehension expression E in
michael@0 5771 *
michael@0 5772 * [E for (V in I)] // legacy array comprehension
michael@0 5773 * (E for (V in I)) // legacy generator expression
michael@0 5774 *
michael@0 5775 * from its initial location in the AST, on the left of the 'for', to its final
michael@0 5776 * position on the right. To avoid a separate pass we do this by adjusting the
michael@0 5777 * blockids and name binding links that were established when E was parsed.
michael@0 5778 *
michael@0 5779 * A legacy generator expression desugars like so:
michael@0 5780 *
michael@0 5781 * (E for (V in I)) => (function () { for (var V in I) yield E; })()
michael@0 5782 *
michael@0 5783 * so the transplanter must adjust static level as well as blockid. E's source
michael@0 5784 * coordinates in root->pn_pos are critical to deciding which binding links to
michael@0 5785 * preserve and which to cut.
michael@0 5786 *
michael@0 5787 * NB: This is not a general tree transplanter -- it knows in particular that
michael@0 5788 * the one or more bindings induced by V have not yet been created.
michael@0 5789 */
michael@0 5790 class LegacyCompExprTransplanter
michael@0 5791 {
michael@0 5792 ParseNode *root;
michael@0 5793 Parser<FullParseHandler> *parser;
michael@0 5794 ParseContext<FullParseHandler> *outerpc;
michael@0 5795 GeneratorKind comprehensionKind;
michael@0 5796 unsigned adjust;
michael@0 5797 HashSet<Definition *> visitedImplicitArguments;
michael@0 5798
michael@0 5799 public:
michael@0 5800 LegacyCompExprTransplanter(ParseNode *pn, Parser<FullParseHandler> *parser,
michael@0 5801 ParseContext<FullParseHandler> *outerpc,
michael@0 5802 GeneratorKind kind, unsigned adj)
michael@0 5803 : root(pn), parser(parser), outerpc(outerpc), comprehensionKind(kind), adjust(adj),
michael@0 5804 visitedImplicitArguments(parser->context)
michael@0 5805 {}
michael@0 5806
michael@0 5807 bool init() {
michael@0 5808 return visitedImplicitArguments.init();
michael@0 5809 }
michael@0 5810
michael@0 5811 bool transplant(ParseNode *pn);
michael@0 5812 };
michael@0 5813
michael@0 5814 /*
michael@0 5815 * Any definitions nested within the legacy comprehension expression of a
michael@0 5816 * generator expression must move "down" one static level, which of course
michael@0 5817 * increases the upvar-frame-skip count.
michael@0 5818 */
michael@0 5819 template <typename ParseHandler>
michael@0 5820 static bool
michael@0 5821 BumpStaticLevel(TokenStream &ts, ParseNode *pn, ParseContext<ParseHandler> *pc)
michael@0 5822 {
michael@0 5823 if (pn->pn_cookie.isFree())
michael@0 5824 return true;
michael@0 5825
michael@0 5826 unsigned level = unsigned(pn->pn_cookie.level()) + 1;
michael@0 5827 JS_ASSERT(level >= pc->staticLevel);
michael@0 5828 return pn->pn_cookie.set(ts, level, pn->pn_cookie.slot());
michael@0 5829 }
michael@0 5830
michael@0 5831 template <typename ParseHandler>
michael@0 5832 static bool
michael@0 5833 AdjustBlockId(TokenStream &ts, ParseNode *pn, unsigned adjust, ParseContext<ParseHandler> *pc)
michael@0 5834 {
michael@0 5835 JS_ASSERT(pn->isArity(PN_LIST) || pn->isArity(PN_CODE) || pn->isArity(PN_NAME));
michael@0 5836 if (BlockIdLimit - pn->pn_blockid <= adjust + 1) {
michael@0 5837 ts.reportError(JSMSG_NEED_DIET, "program");
michael@0 5838 return false;
michael@0 5839 }
michael@0 5840 pn->pn_blockid += adjust;
michael@0 5841 if (pn->pn_blockid >= pc->blockidGen)
michael@0 5842 pc->blockidGen = pn->pn_blockid + 1;
michael@0 5843 return true;
michael@0 5844 }
michael@0 5845
michael@0 5846 bool
michael@0 5847 LegacyCompExprTransplanter::transplant(ParseNode *pn)
michael@0 5848 {
michael@0 5849 ParseContext<FullParseHandler> *pc = parser->pc;
michael@0 5850
michael@0 5851 bool isGenexp = comprehensionKind != NotGenerator;
michael@0 5852
michael@0 5853 if (!pn)
michael@0 5854 return true;
michael@0 5855
michael@0 5856 switch (pn->getArity()) {
michael@0 5857 case PN_LIST:
michael@0 5858 for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
michael@0 5859 if (!transplant(pn2))
michael@0 5860 return false;
michael@0 5861 }
michael@0 5862 if (pn->pn_pos >= root->pn_pos) {
michael@0 5863 if (!AdjustBlockId(parser->tokenStream, pn, adjust, pc))
michael@0 5864 return false;
michael@0 5865 }
michael@0 5866 break;
michael@0 5867
michael@0 5868 case PN_TERNARY:
michael@0 5869 if (!transplant(pn->pn_kid1) ||
michael@0 5870 !transplant(pn->pn_kid2) ||
michael@0 5871 !transplant(pn->pn_kid3))
michael@0 5872 return false;
michael@0 5873 break;
michael@0 5874
michael@0 5875 case PN_BINARY:
michael@0 5876 case PN_BINARY_OBJ:
michael@0 5877 if (!transplant(pn->pn_left))
michael@0 5878 return false;
michael@0 5879
michael@0 5880 /* Binary TOK_COLON nodes can have left == right. See bug 492714. */
michael@0 5881 if (pn->pn_right != pn->pn_left) {
michael@0 5882 if (!transplant(pn->pn_right))
michael@0 5883 return false;
michael@0 5884 }
michael@0 5885 break;
michael@0 5886
michael@0 5887 case PN_UNARY:
michael@0 5888 if (!transplant(pn->pn_kid))
michael@0 5889 return false;
michael@0 5890 break;
michael@0 5891
michael@0 5892 case PN_CODE:
michael@0 5893 case PN_NAME:
michael@0 5894 if (!transplant(pn->maybeExpr()))
michael@0 5895 return false;
michael@0 5896
michael@0 5897 if (pn->isDefn()) {
michael@0 5898 if (isGenexp && !BumpStaticLevel(parser->tokenStream, pn, pc))
michael@0 5899 return false;
michael@0 5900 } else if (pn->isUsed()) {
michael@0 5901 JS_ASSERT(pn->pn_cookie.isFree());
michael@0 5902
michael@0 5903 Definition *dn = pn->pn_lexdef;
michael@0 5904 JS_ASSERT(dn->isDefn());
michael@0 5905
michael@0 5906 /*
michael@0 5907 * Adjust the definition's block id only if it is a placeholder not
michael@0 5908 * to the left of the root node, and if pn is the last use visited
michael@0 5909 * in the legacy comprehension expression (to avoid adjusting the
michael@0 5910 * blockid multiple times).
michael@0 5911 *
michael@0 5912 * Non-placeholder definitions within the legacy comprehension
michael@0 5913 * expression will be visited further below.
michael@0 5914 */
michael@0 5915 if (dn->isPlaceholder() && dn->pn_pos >= root->pn_pos && dn->dn_uses == pn) {
michael@0 5916 if (isGenexp && !BumpStaticLevel(parser->tokenStream, dn, pc))
michael@0 5917 return false;
michael@0 5918 if (!AdjustBlockId(parser->tokenStream, dn, adjust, pc))
michael@0 5919 return false;
michael@0 5920 }
michael@0 5921
michael@0 5922 RootedAtom atom(parser->context, pn->pn_atom);
michael@0 5923 #ifdef DEBUG
michael@0 5924 StmtInfoPC *stmt = LexicalLookup(pc, atom, nullptr, (StmtInfoPC *)nullptr);
michael@0 5925 JS_ASSERT(!stmt || stmt != pc->topStmt);
michael@0 5926 #endif
michael@0 5927 if (isGenexp && !dn->isOp(JSOP_CALLEE)) {
michael@0 5928 JS_ASSERT(!pc->decls().lookupFirst(atom));
michael@0 5929
michael@0 5930 if (dn->pn_pos < root->pn_pos) {
michael@0 5931 /*
michael@0 5932 * The variable originally appeared to be a use of a
michael@0 5933 * definition or placeholder outside the generator, but now
michael@0 5934 * we know it is scoped within the legacy comprehension
michael@0 5935 * tail's clauses. Make it (along with any other uses within
michael@0 5936 * the generator) a use of a new placeholder in the
michael@0 5937 * generator's lexdeps.
michael@0 5938 */
michael@0 5939 Definition *dn2 = parser->handler.newPlaceholder(atom, parser->pc->blockid(),
michael@0 5940 parser->pos());
michael@0 5941 if (!dn2)
michael@0 5942 return false;
michael@0 5943 dn2->pn_pos = root->pn_pos;
michael@0 5944
michael@0 5945 /*
michael@0 5946 * Change all uses of |dn| that lie within the generator's
michael@0 5947 * |yield| expression into uses of dn2.
michael@0 5948 */
michael@0 5949 ParseNode **pnup = &dn->dn_uses;
michael@0 5950 ParseNode *pnu;
michael@0 5951 while ((pnu = *pnup) != nullptr && pnu->pn_pos >= root->pn_pos) {
michael@0 5952 pnu->pn_lexdef = dn2;
michael@0 5953 dn2->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
michael@0 5954 pnup = &pnu->pn_link;
michael@0 5955 }
michael@0 5956 dn2->dn_uses = dn->dn_uses;
michael@0 5957 dn->dn_uses = *pnup;
michael@0 5958 *pnup = nullptr;
michael@0 5959 DefinitionSingle def = DefinitionSingle::new_<FullParseHandler>(dn2);
michael@0 5960 if (!pc->lexdeps->put(atom, def))
michael@0 5961 return false;
michael@0 5962 if (dn->isClosed())
michael@0 5963 dn2->pn_dflags |= PND_CLOSED;
michael@0 5964 } else if (dn->isPlaceholder()) {
michael@0 5965 /*
michael@0 5966 * The variable first occurs free in the 'yield' expression;
michael@0 5967 * move the existing placeholder node (and all its uses)
michael@0 5968 * from the parent's lexdeps into the generator's lexdeps.
michael@0 5969 */
michael@0 5970 outerpc->lexdeps->remove(atom);
michael@0 5971 DefinitionSingle def = DefinitionSingle::new_<FullParseHandler>(dn);
michael@0 5972 if (!pc->lexdeps->put(atom, def))
michael@0 5973 return false;
michael@0 5974 } else if (dn->isImplicitArguments()) {
michael@0 5975 /*
michael@0 5976 * Implicit 'arguments' Definition nodes (see
michael@0 5977 * PND_IMPLICITARGUMENTS in Parser::functionBody) are only
michael@0 5978 * reachable via the lexdefs of their uses. Unfortunately,
michael@0 5979 * there may be multiple uses, so we need to maintain a set
michael@0 5980 * to only bump the definition once.
michael@0 5981 */
michael@0 5982 if (isGenexp && !visitedImplicitArguments.has(dn)) {
michael@0 5983 if (!BumpStaticLevel(parser->tokenStream, dn, pc))
michael@0 5984 return false;
michael@0 5985 if (!AdjustBlockId(parser->tokenStream, dn, adjust, pc))
michael@0 5986 return false;
michael@0 5987 if (!visitedImplicitArguments.put(dn))
michael@0 5988 return false;
michael@0 5989 }
michael@0 5990 }
michael@0 5991 }
michael@0 5992 }
michael@0 5993
michael@0 5994 if (pn->pn_pos >= root->pn_pos) {
michael@0 5995 if (!AdjustBlockId(parser->tokenStream, pn, adjust, pc))
michael@0 5996 return false;
michael@0 5997 }
michael@0 5998 break;
michael@0 5999
michael@0 6000 case PN_NULLARY:
michael@0 6001 /* Nothing. */
michael@0 6002 break;
michael@0 6003 }
michael@0 6004 return true;
michael@0 6005 }
michael@0 6006
michael@0 6007 // Parsing legacy (JS1.7-style) comprehensions is terrible: we parse the head
michael@0 6008 // expression as if it's part of a comma expression, then when we see the "for"
michael@0 6009 // we transplant the parsed expression into the inside of a constructed
michael@0 6010 // for-of/for-in/for-each tail. Transplanting an already-parsed expression is
michael@0 6011 // tricky, but the LegacyCompExprTransplanter handles most of that.
michael@0 6012 //
michael@0 6013 // The one remaining thing to patch up is the block scope depth. We need to
michael@0 6014 // compute the maximum block scope depth of a function, so we know how much
michael@0 6015 // space to reserve in the fixed part of a stack frame. Normally this is done
michael@0 6016 // whenever we leave a statement, via AccumulateBlockScopeDepth. However if the
michael@0 6017 // head has a let expression, we need to re-assign that depth to the tail of the
michael@0 6018 // comprehension.
michael@0 6019 //
michael@0 6020 // Thing is, we don't actually know what that depth is, because the only
michael@0 6021 // information we keep is the maximum nested depth within a statement, so we
michael@0 6022 // just conservatively propagate the maximum nested depth from the top statement
michael@0 6023 // to the comprehension tail.
michael@0 6024 //
michael@0 6025 template <typename ParseHandler>
michael@0 6026 static unsigned
michael@0 6027 LegacyComprehensionHeadBlockScopeDepth(ParseContext<ParseHandler> *pc)
michael@0 6028 {
michael@0 6029 return pc->topStmt ? pc->topStmt->innerBlockScopeDepth : pc->blockScopeDepth;
michael@0 6030 }
michael@0 6031
michael@0 6032 /*
michael@0 6033 * Starting from a |for| keyword after the first array initialiser element or
michael@0 6034 * an expression in an open parenthesis, parse the tail of the comprehension
michael@0 6035 * or generator expression signified by this |for| keyword in context.
michael@0 6036 *
michael@0 6037 * Return null on failure, else return the top-most parse node for the array
michael@0 6038 * comprehension or generator expression, with a unary node as the body of the
michael@0 6039 * (possibly nested) for-loop, initialized by |kind, op, kid|.
michael@0 6040 */
michael@0 6041 template <>
michael@0 6042 ParseNode *
michael@0 6043 Parser<FullParseHandler>::legacyComprehensionTail(ParseNode *bodyStmt, unsigned blockid,
michael@0 6044 GeneratorKind comprehensionKind,
michael@0 6045 ParseContext<FullParseHandler> *outerpc,
michael@0 6046 unsigned innerBlockScopeDepth)
michael@0 6047 {
michael@0 6048 /*
michael@0 6049 * If we saw any inner functions while processing the generator expression
michael@0 6050 * then they may have upvars referring to the let vars in this generator
michael@0 6051 * which were not correctly processed. Bail out and start over without
michael@0 6052 * allowing lazy parsing.
michael@0 6053 */
michael@0 6054 if (handler.syntaxParser) {
michael@0 6055 handler.disableSyntaxParser();
michael@0 6056 abortedSyntaxParse = true;
michael@0 6057 return nullptr;
michael@0 6058 }
michael@0 6059
michael@0 6060 unsigned adjust;
michael@0 6061 ParseNode *pn, *pn2, *pn3, **pnp;
michael@0 6062 StmtInfoPC stmtInfo(context);
michael@0 6063 BindData<FullParseHandler> data(context);
michael@0 6064 TokenKind tt;
michael@0 6065
michael@0 6066 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
michael@0 6067
michael@0 6068 bool isGenexp = comprehensionKind != NotGenerator;
michael@0 6069
michael@0 6070 if (isGenexp) {
michael@0 6071 JS_ASSERT(comprehensionKind == LegacyGenerator);
michael@0 6072 /*
michael@0 6073 * Generator expression desugars to an immediately applied lambda that
michael@0 6074 * yields the next value from a for-in loop (possibly nested, and with
michael@0 6075 * optional if guard). Make pn be the TOK_LC body node.
michael@0 6076 */
michael@0 6077 pn = pushLexicalScope(&stmtInfo);
michael@0 6078 if (!pn)
michael@0 6079 return null();
michael@0 6080 adjust = pn->pn_blockid - blockid;
michael@0 6081 } else {
michael@0 6082 /*
michael@0 6083 * Make a parse-node and literal object representing the block scope of
michael@0 6084 * this array comprehension. Our caller in primaryExpr, the TOK_LB case
michael@0 6085 * aka the array initialiser case, has passed the blockid to claim for
michael@0 6086 * the comprehension's block scope. We allocate that id or one above it
michael@0 6087 * here, by calling PushLexicalScope.
michael@0 6088 *
michael@0 6089 * In the case of a comprehension expression that has nested blocks
michael@0 6090 * (e.g., let expressions), we will allocate a higher blockid but then
michael@0 6091 * slide all blocks "to the right" to make room for the comprehension's
michael@0 6092 * block scope.
michael@0 6093 */
michael@0 6094 adjust = pc->blockid();
michael@0 6095 pn = pushLexicalScope(&stmtInfo);
michael@0 6096 if (!pn)
michael@0 6097 return null();
michael@0 6098
michael@0 6099 JS_ASSERT(blockid <= pn->pn_blockid);
michael@0 6100 JS_ASSERT(blockid < pc->blockidGen);
michael@0 6101 JS_ASSERT(pc->bodyid < blockid);
michael@0 6102 pn->pn_blockid = stmtInfo.blockid = blockid;
michael@0 6103 JS_ASSERT(adjust < blockid);
michael@0 6104 adjust = blockid - adjust;
michael@0 6105 }
michael@0 6106
michael@0 6107 handler.setBeginPosition(pn, bodyStmt);
michael@0 6108
michael@0 6109 pnp = &pn->pn_expr;
michael@0 6110
michael@0 6111 LegacyCompExprTransplanter transplanter(bodyStmt, this, outerpc, comprehensionKind, adjust);
michael@0 6112 if (!transplanter.init())
michael@0 6113 return null();
michael@0 6114
michael@0 6115 if (!transplanter.transplant(bodyStmt))
michael@0 6116 return null();
michael@0 6117
michael@0 6118 JS_ASSERT(pc->staticScope && pc->staticScope == pn->pn_objbox->object);
michael@0 6119 data.initLet(HoistVars, pc->staticScope->as<StaticBlockObject>(), JSMSG_ARRAY_INIT_TOO_BIG);
michael@0 6120
michael@0 6121 do {
michael@0 6122 /*
michael@0 6123 * FOR node is binary, left is loop control and right is body. Use
michael@0 6124 * index to count each block-local let-variable on the left-hand side
michael@0 6125 * of the in/of.
michael@0 6126 */
michael@0 6127 pn2 = BinaryNode::create(PNK_FOR, &handler);
michael@0 6128 if (!pn2)
michael@0 6129 return null();
michael@0 6130
michael@0 6131 pn2->setOp(JSOP_ITER);
michael@0 6132 pn2->pn_iflags = JSITER_ENUMERATE;
michael@0 6133 if (allowsForEachIn() && tokenStream.matchContextualKeyword(context->names().each))
michael@0 6134 pn2->pn_iflags |= JSITER_FOREACH;
michael@0 6135 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
michael@0 6136
michael@0 6137 uint32_t startYieldOffset = pc->lastYieldOffset;
michael@0 6138
michael@0 6139 RootedPropertyName name(context);
michael@0 6140 tt = tokenStream.getToken();
michael@0 6141 switch (tt) {
michael@0 6142 case TOK_LB:
michael@0 6143 case TOK_LC:
michael@0 6144 pc->inDeclDestructuring = true;
michael@0 6145 pn3 = primaryExpr(tt);
michael@0 6146 pc->inDeclDestructuring = false;
michael@0 6147 if (!pn3)
michael@0 6148 return null();
michael@0 6149 break;
michael@0 6150
michael@0 6151 case TOK_NAME:
michael@0 6152 name = tokenStream.currentName();
michael@0 6153
michael@0 6154 /*
michael@0 6155 * Create a name node with pn_op JSOP_NAME. We can't set pn_op to
michael@0 6156 * JSOP_GETLOCAL here, because we don't yet know the block's depth
michael@0 6157 * in the operand stack frame. The code generator computes that,
michael@0 6158 * and it tries to bind all names to slots, so we must let it do
michael@0 6159 * the deed.
michael@0 6160 */
michael@0 6161 pn3 = newBindingNode(name, false);
michael@0 6162 if (!pn3)
michael@0 6163 return null();
michael@0 6164 break;
michael@0 6165
michael@0 6166 default:
michael@0 6167 report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
michael@0 6168
michael@0 6169 case TOK_ERROR:
michael@0 6170 return null();
michael@0 6171 }
michael@0 6172
michael@0 6173 bool isForOf;
michael@0 6174 if (!matchInOrOf(&isForOf)) {
michael@0 6175 report(ParseError, false, null(), JSMSG_IN_AFTER_FOR_NAME);
michael@0 6176 return null();
michael@0 6177 }
michael@0 6178 ParseNodeKind headKind = PNK_FORIN;
michael@0 6179 if (isForOf) {
michael@0 6180 if (pn2->pn_iflags != JSITER_ENUMERATE) {
michael@0 6181 JS_ASSERT(pn2->pn_iflags == (JSITER_FOREACH | JSITER_ENUMERATE));
michael@0 6182 report(ParseError, false, null(), JSMSG_BAD_FOR_EACH_LOOP);
michael@0 6183 return null();
michael@0 6184 }
michael@0 6185 pn2->pn_iflags = 0;
michael@0 6186 headKind = PNK_FOROF;
michael@0 6187 }
michael@0 6188
michael@0 6189 ParseNode *pn4 = expr();
michael@0 6190 if (!pn4)
michael@0 6191 return null();
michael@0 6192 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
michael@0 6193
michael@0 6194 if (isGenexp && pc->lastYieldOffset != startYieldOffset) {
michael@0 6195 reportWithOffset(ParseError, false, pc->lastYieldOffset,
michael@0 6196 JSMSG_BAD_GENEXP_BODY, js_yield_str);
michael@0 6197 return null();
michael@0 6198 }
michael@0 6199
michael@0 6200 switch (tt) {
michael@0 6201 case TOK_LB:
michael@0 6202 case TOK_LC:
michael@0 6203 if (!checkDestructuring(&data, pn3))
michael@0 6204 return null();
michael@0 6205
michael@0 6206 if (versionNumber() == JSVERSION_1_7 &&
michael@0 6207 !(pn2->pn_iflags & JSITER_FOREACH) &&
michael@0 6208 !isForOf)
michael@0 6209 {
michael@0 6210 /* Destructuring requires [key, value] enumeration in JS1.7. */
michael@0 6211 if (!pn3->isKind(PNK_ARRAY) || pn3->pn_count != 2) {
michael@0 6212 report(ParseError, false, null(), JSMSG_BAD_FOR_LEFTSIDE);
michael@0 6213 return null();
michael@0 6214 }
michael@0 6215
michael@0 6216 JS_ASSERT(pn2->isOp(JSOP_ITER));
michael@0 6217 JS_ASSERT(pn2->pn_iflags & JSITER_ENUMERATE);
michael@0 6218 JS_ASSERT(headKind == PNK_FORIN);
michael@0 6219 pn2->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
michael@0 6220 }
michael@0 6221 break;
michael@0 6222
michael@0 6223 case TOK_NAME:
michael@0 6224 data.pn = pn3;
michael@0 6225 if (!data.binder(&data, name, this))
michael@0 6226 return null();
michael@0 6227 break;
michael@0 6228
michael@0 6229 default:;
michael@0 6230 }
michael@0 6231
michael@0 6232 /*
michael@0 6233 * Synthesize a declaration. Every definition must appear in the parse
michael@0 6234 * tree in order for ComprehensionTranslator to work.
michael@0 6235 */
michael@0 6236 ParseNode *vars = ListNode::create(PNK_VAR, &handler);
michael@0 6237 if (!vars)
michael@0 6238 return null();
michael@0 6239 vars->setOp(JSOP_NOP);
michael@0 6240 vars->pn_pos = pn3->pn_pos;
michael@0 6241 vars->makeEmpty();
michael@0 6242 vars->append(pn3);
michael@0 6243
michael@0 6244 /* Definitions can't be passed directly to EmitAssignment as lhs. */
michael@0 6245 pn3 = cloneLeftHandSide(pn3);
michael@0 6246 if (!pn3)
michael@0 6247 return null();
michael@0 6248
michael@0 6249 pn2->pn_left = handler.newTernary(headKind, vars, pn3, pn4);
michael@0 6250 if (!pn2->pn_left)
michael@0 6251 return null();
michael@0 6252 *pnp = pn2;
michael@0 6253 pnp = &pn2->pn_right;
michael@0 6254 } while (tokenStream.matchToken(TOK_FOR));
michael@0 6255
michael@0 6256 if (tokenStream.matchToken(TOK_IF)) {
michael@0 6257 pn2 = TernaryNode::create(PNK_IF, &handler);
michael@0 6258 if (!pn2)
michael@0 6259 return null();
michael@0 6260 pn2->pn_kid1 = condition();
michael@0 6261 if (!pn2->pn_kid1)
michael@0 6262 return null();
michael@0 6263 *pnp = pn2;
michael@0 6264 pnp = &pn2->pn_kid2;
michael@0 6265 }
michael@0 6266
michael@0 6267 *pnp = bodyStmt;
michael@0 6268
michael@0 6269 pc->topStmt->innerBlockScopeDepth += innerBlockScopeDepth;
michael@0 6270 PopStatementPC(tokenStream, pc);
michael@0 6271
michael@0 6272 handler.setEndPosition(pn, pos().end);
michael@0 6273
michael@0 6274 return pn;
michael@0 6275 }
michael@0 6276
michael@0 6277 template <>
michael@0 6278 SyntaxParseHandler::Node
michael@0 6279 Parser<SyntaxParseHandler>::legacyComprehensionTail(SyntaxParseHandler::Node bodyStmt,
michael@0 6280 unsigned blockid,
michael@0 6281 GeneratorKind comprehensionKind,
michael@0 6282 ParseContext<SyntaxParseHandler> *outerpc,
michael@0 6283 unsigned innerBlockScopeDepth)
michael@0 6284 {
michael@0 6285 abortIfSyntaxParser();
michael@0 6286 return null();
michael@0 6287 }
michael@0 6288
michael@0 6289 template <>
michael@0 6290 ParseNode*
michael@0 6291 Parser<FullParseHandler>::legacyArrayComprehension(ParseNode *array)
michael@0 6292 {
michael@0 6293 array->setKind(PNK_ARRAYCOMP);
michael@0 6294
michael@0 6295 // Remove the single element from array's linked list, leaving us with an
michael@0 6296 // empty array literal and a comprehension expression.
michael@0 6297 JS_ASSERT(array->pn_count == 1);
michael@0 6298 ParseNode *bodyExpr = array->last();
michael@0 6299 array->pn_count = 0;
michael@0 6300 array->pn_tail = &array->pn_head;
michael@0 6301 *array->pn_tail = nullptr;
michael@0 6302
michael@0 6303 ParseNode *arrayPush = handler.newUnary(PNK_ARRAYPUSH, JSOP_ARRAYPUSH,
michael@0 6304 bodyExpr->pn_pos.begin, bodyExpr);
michael@0 6305 if (!arrayPush)
michael@0 6306 return null();
michael@0 6307
michael@0 6308 ParseNode *comp = legacyComprehensionTail(arrayPush, array->pn_blockid, NotGenerator,
michael@0 6309 nullptr, LegacyComprehensionHeadBlockScopeDepth(pc));
michael@0 6310 if (!comp)
michael@0 6311 return null();
michael@0 6312
michael@0 6313 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION);
michael@0 6314
michael@0 6315 TokenPos p = handler.getPosition(array);
michael@0 6316 p.end = pos().end;
michael@0 6317 return handler.newArrayComprehension(comp, array->pn_blockid, p);
michael@0 6318 }
michael@0 6319
michael@0 6320 template <>
michael@0 6321 SyntaxParseHandler::Node
michael@0 6322 Parser<SyntaxParseHandler>::legacyArrayComprehension(Node array)
michael@0 6323 {
michael@0 6324 abortIfSyntaxParser();
michael@0 6325 return null();
michael@0 6326 }
michael@0 6327
michael@0 6328 template <typename ParseHandler>
michael@0 6329 typename ParseHandler::Node
michael@0 6330 Parser<ParseHandler>::generatorComprehensionLambda(GeneratorKind comprehensionKind,
michael@0 6331 unsigned begin, Node innerStmt)
michael@0 6332 {
michael@0 6333 JS_ASSERT(comprehensionKind == LegacyGenerator || comprehensionKind == StarGenerator);
michael@0 6334 JS_ASSERT(!!innerStmt == (comprehensionKind == LegacyGenerator));
michael@0 6335
michael@0 6336 Node genfn = handler.newFunctionDefinition();
michael@0 6337 if (!genfn)
michael@0 6338 return null();
michael@0 6339 handler.setOp(genfn, JSOP_LAMBDA);
michael@0 6340
michael@0 6341 ParseContext<ParseHandler> *outerpc = pc;
michael@0 6342
michael@0 6343 // If we are off the main thread, the generator meta-objects have
michael@0 6344 // already been created by js::StartOffThreadParseScript, so cx will not
michael@0 6345 // be necessary.
michael@0 6346 RootedObject proto(context);
michael@0 6347 if (comprehensionKind == StarGenerator) {
michael@0 6348 JSContext *cx = context->maybeJSContext();
michael@0 6349 proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global());
michael@0 6350 if (!proto)
michael@0 6351 return null();
michael@0 6352 }
michael@0 6353
michael@0 6354 RootedFunction fun(context, newFunction(outerpc, /* atom = */ NullPtr(), Expression, proto));
michael@0 6355 if (!fun)
michael@0 6356 return null();
michael@0 6357
michael@0 6358 // Create box for fun->object early to root it.
michael@0 6359 Directives directives(/* strict = */ outerpc->sc->strict);
michael@0 6360 FunctionBox *genFunbox = newFunctionBox(genfn, fun, outerpc, directives, comprehensionKind);
michael@0 6361 if (!genFunbox)
michael@0 6362 return null();
michael@0 6363
michael@0 6364 ParseContext<ParseHandler> genpc(this, outerpc, genfn, genFunbox,
michael@0 6365 /* newDirectives = */ nullptr,
michael@0 6366 outerpc->staticLevel + 1, outerpc->blockidGen,
michael@0 6367 /* blockScopeDepth = */ 0);
michael@0 6368 if (!genpc.init(tokenStream))
michael@0 6369 return null();
michael@0 6370
michael@0 6371 /*
michael@0 6372 * We assume conservatively that any deoptimization flags in pc->sc
michael@0 6373 * come from the kid. So we propagate these flags into genfn. For code
michael@0 6374 * simplicity we also do not detect if the flags were only set in the
michael@0 6375 * kid and could be removed from pc->sc.
michael@0 6376 */
michael@0 6377 genFunbox->anyCxFlags = outerpc->sc->anyCxFlags;
michael@0 6378 if (outerpc->sc->isFunctionBox())
michael@0 6379 genFunbox->funCxFlags = outerpc->sc->asFunctionBox()->funCxFlags;
michael@0 6380
michael@0 6381 JS_ASSERT(genFunbox->generatorKind() == comprehensionKind);
michael@0 6382 genFunbox->inGenexpLambda = true;
michael@0 6383 handler.setBlockId(genfn, genpc.bodyid);
michael@0 6384
michael@0 6385 Node body;
michael@0 6386
michael@0 6387 if (comprehensionKind == StarGenerator) {
michael@0 6388 body = comprehension(StarGenerator);
michael@0 6389 if (!body)
michael@0 6390 return null();
michael@0 6391 } else {
michael@0 6392 JS_ASSERT(comprehensionKind == LegacyGenerator);
michael@0 6393 body = legacyComprehensionTail(innerStmt, outerpc->blockid(), LegacyGenerator,
michael@0 6394 outerpc, LegacyComprehensionHeadBlockScopeDepth(outerpc));
michael@0 6395 if (!body)
michael@0 6396 return null();
michael@0 6397 }
michael@0 6398
michael@0 6399 if (comprehensionKind == StarGenerator)
michael@0 6400 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
michael@0 6401
michael@0 6402 handler.setBeginPosition(body, begin);
michael@0 6403 handler.setEndPosition(body, pos().end);
michael@0 6404
michael@0 6405 handler.setBeginPosition(genfn, begin);
michael@0 6406 handler.setEndPosition(genfn, pos().end);
michael@0 6407
michael@0 6408 // Note that if we ever start syntax-parsing generators, we will also
michael@0 6409 // need to propagate the closed-over variable set to the inner
michael@0 6410 // lazyscript, as in finishFunctionDefinition.
michael@0 6411 handler.setFunctionBody(genfn, body);
michael@0 6412
michael@0 6413 PropagateTransitiveParseFlags(genFunbox, outerpc->sc);
michael@0 6414
michael@0 6415 if (!leaveFunction(genfn, outerpc))
michael@0 6416 return null();
michael@0 6417
michael@0 6418 return genfn;
michael@0 6419 }
michael@0 6420
michael@0 6421 #if JS_HAS_GENERATOR_EXPRS
michael@0 6422
michael@0 6423 /*
michael@0 6424 * Starting from a |for| keyword after an expression, parse the comprehension
michael@0 6425 * tail completing this generator expression. Wrap the expression at kid in a
michael@0 6426 * generator function that is immediately called to evaluate to the generator
michael@0 6427 * iterator that is the value of this legacy generator expression.
michael@0 6428 *
michael@0 6429 * |kid| must be the expression before the |for| keyword; we return an
michael@0 6430 * application of a generator function that includes the |for| loops and
michael@0 6431 * |if| guards, with |kid| as the operand of a |yield| expression as the
michael@0 6432 * innermost loop body.
michael@0 6433 *
michael@0 6434 * Note how unlike Python, we do not evaluate the expression to the right of
michael@0 6435 * the first |in| in the chain of |for| heads. Instead, a generator expression
michael@0 6436 * is merely sugar for a generator function expression and its application.
michael@0 6437 */
michael@0 6438 template <>
michael@0 6439 ParseNode *
michael@0 6440 Parser<FullParseHandler>::legacyGeneratorExpr(ParseNode *expr)
michael@0 6441 {
michael@0 6442 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
michael@0 6443
michael@0 6444 /* Create a |yield| node for |kid|. */
michael@0 6445 ParseNode *yieldExpr = handler.newUnary(PNK_YIELD, JSOP_NOP, expr->pn_pos.begin, expr);
michael@0 6446 if (!yieldExpr)
michael@0 6447 return null();
michael@0 6448 yieldExpr->setInParens(true);
michael@0 6449
michael@0 6450 // A statement to wrap the yield expression.
michael@0 6451 ParseNode *yieldStmt = handler.newExprStatement(yieldExpr, expr->pn_pos.end);
michael@0 6452 if (!yieldStmt)
michael@0 6453 return null();
michael@0 6454
michael@0 6455 /* Make a new node for the desugared generator function. */
michael@0 6456 ParseNode *genfn = generatorComprehensionLambda(LegacyGenerator, expr->pn_pos.begin, yieldStmt);
michael@0 6457 if (!genfn)
michael@0 6458 return null();
michael@0 6459
michael@0 6460 /*
michael@0 6461 * Our result is a call expression that invokes the anonymous generator
michael@0 6462 * function object.
michael@0 6463 */
michael@0 6464 ParseNode *result = ListNode::create(PNK_GENEXP, &handler);
michael@0 6465 if (!result)
michael@0 6466 return null();
michael@0 6467 result->setOp(JSOP_CALL);
michael@0 6468 result->pn_pos.begin = genfn->pn_pos.begin;
michael@0 6469 result->initList(genfn);
michael@0 6470 return result;
michael@0 6471 }
michael@0 6472
michael@0 6473 template <>
michael@0 6474 SyntaxParseHandler::Node
michael@0 6475 Parser<SyntaxParseHandler>::legacyGeneratorExpr(Node kid)
michael@0 6476 {
michael@0 6477 JS_ALWAYS_FALSE(abortIfSyntaxParser());
michael@0 6478 return SyntaxParseHandler::NodeFailure;
michael@0 6479 }
michael@0 6480
michael@0 6481 static const char js_generator_str[] = "generator";
michael@0 6482
michael@0 6483 #endif /* JS_HAS_GENERATOR_EXPRS */
michael@0 6484
michael@0 6485 template <typename ParseHandler>
michael@0 6486 typename ParseHandler::Node
michael@0 6487 Parser<ParseHandler>::comprehensionFor(GeneratorKind comprehensionKind)
michael@0 6488 {
michael@0 6489 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
michael@0 6490
michael@0 6491 uint32_t begin = pos().begin;
michael@0 6492
michael@0 6493 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
michael@0 6494
michael@0 6495 // FIXME: Destructuring binding (bug 980828).
michael@0 6496
michael@0 6497 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME);
michael@0 6498 RootedPropertyName name(context, tokenStream.currentName());
michael@0 6499 if (name == context->names().let) {
michael@0 6500 report(ParseError, false, null(), JSMSG_LET_COMP_BINDING);
michael@0 6501 return null();
michael@0 6502 }
michael@0 6503 if (!tokenStream.matchContextualKeyword(context->names().of)) {
michael@0 6504 report(ParseError, false, null(), JSMSG_OF_AFTER_FOR_NAME);
michael@0 6505 return null();
michael@0 6506 }
michael@0 6507
michael@0 6508 Node rhs = assignExpr();
michael@0 6509 if (!rhs)
michael@0 6510 return null();
michael@0 6511
michael@0 6512 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_OF_ITERABLE);
michael@0 6513
michael@0 6514 TokenPos headPos(begin, pos().end);
michael@0 6515
michael@0 6516 StmtInfoPC stmtInfo(context);
michael@0 6517 BindData<ParseHandler> data(context);
michael@0 6518 RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context));
michael@0 6519 if (!blockObj)
michael@0 6520 return null();
michael@0 6521 data.initLet(DontHoistVars, *blockObj, JSMSG_TOO_MANY_LOCALS);
michael@0 6522 Node lhs = newName(name);
michael@0 6523 if (!lhs)
michael@0 6524 return null();
michael@0 6525 Node decls = handler.newList(PNK_LET, lhs, JSOP_NOP);
michael@0 6526 if (!decls)
michael@0 6527 return null();
michael@0 6528 data.pn = lhs;
michael@0 6529 if (!data.binder(&data, name, this))
michael@0 6530 return null();
michael@0 6531 Node letScope = pushLetScope(blockObj, &stmtInfo);
michael@0 6532 if (!letScope)
michael@0 6533 return null();
michael@0 6534 handler.setLexicalScopeBody(letScope, decls);
michael@0 6535
michael@0 6536 Node assignLhs = newName(name);
michael@0 6537 if (!assignLhs)
michael@0 6538 return null();
michael@0 6539 if (!noteNameUse(name, assignLhs))
michael@0 6540 return null();
michael@0 6541 handler.setOp(assignLhs, JSOP_SETNAME);
michael@0 6542
michael@0 6543 Node head = handler.newForHead(PNK_FOROF, letScope, assignLhs, rhs, headPos);
michael@0 6544 if (!head)
michael@0 6545 return null();
michael@0 6546
michael@0 6547 Node tail = comprehensionTail(comprehensionKind);
michael@0 6548 if (!tail)
michael@0 6549 return null();
michael@0 6550
michael@0 6551 PopStatementPC(tokenStream, pc);
michael@0 6552
michael@0 6553 return handler.newForStatement(begin, head, tail, JSOP_ITER);
michael@0 6554 }
michael@0 6555
michael@0 6556 template <typename ParseHandler>
michael@0 6557 typename ParseHandler::Node
michael@0 6558 Parser<ParseHandler>::comprehensionIf(GeneratorKind comprehensionKind)
michael@0 6559 {
michael@0 6560 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_IF));
michael@0 6561
michael@0 6562 uint32_t begin = pos().begin;
michael@0 6563
michael@0 6564 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
michael@0 6565 Node cond = assignExpr();
michael@0 6566 if (!cond)
michael@0 6567 return null();
michael@0 6568 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
michael@0 6569
michael@0 6570 /* Check for (a = b) and warn about possible (a == b) mistype. */
michael@0 6571 if (handler.isOperationWithoutParens(cond, PNK_ASSIGN) &&
michael@0 6572 !report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
michael@0 6573 {
michael@0 6574 return null();
michael@0 6575 }
michael@0 6576
michael@0 6577 Node then = comprehensionTail(comprehensionKind);
michael@0 6578 if (!then)
michael@0 6579 return null();
michael@0 6580
michael@0 6581 return handler.newIfStatement(begin, cond, then, null());
michael@0 6582 }
michael@0 6583
michael@0 6584 template <typename ParseHandler>
michael@0 6585 typename ParseHandler::Node
michael@0 6586 Parser<ParseHandler>::comprehensionTail(GeneratorKind comprehensionKind)
michael@0 6587 {
michael@0 6588 JS_CHECK_RECURSION(context, return null());
michael@0 6589
michael@0 6590 if (tokenStream.matchToken(TOK_FOR, TokenStream::Operand))
michael@0 6591 return comprehensionFor(comprehensionKind);
michael@0 6592
michael@0 6593 if (tokenStream.matchToken(TOK_IF, TokenStream::Operand))
michael@0 6594 return comprehensionIf(comprehensionKind);
michael@0 6595
michael@0 6596 uint32_t begin = pos().begin;
michael@0 6597
michael@0 6598 Node bodyExpr = assignExpr();
michael@0 6599 if (!bodyExpr)
michael@0 6600 return null();
michael@0 6601
michael@0 6602 if (comprehensionKind == NotGenerator)
michael@0 6603 return handler.newUnary(PNK_ARRAYPUSH, JSOP_ARRAYPUSH, begin, bodyExpr);
michael@0 6604
michael@0 6605 JS_ASSERT(comprehensionKind == StarGenerator);
michael@0 6606 Node yieldExpr = handler.newUnary(PNK_YIELD, JSOP_NOP, begin, bodyExpr);
michael@0 6607 if (!yieldExpr)
michael@0 6608 return null();
michael@0 6609 handler.setInParens(yieldExpr);
michael@0 6610
michael@0 6611 return handler.newExprStatement(yieldExpr, pos().end);
michael@0 6612 }
michael@0 6613
michael@0 6614 // Parse an ES6 generator or array comprehension, starting at the first 'for'.
michael@0 6615 // The caller is responsible for matching the ending TOK_RP or TOK_RB.
michael@0 6616 template <typename ParseHandler>
michael@0 6617 typename ParseHandler::Node
michael@0 6618 Parser<ParseHandler>::comprehension(GeneratorKind comprehensionKind)
michael@0 6619 {
michael@0 6620 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
michael@0 6621
michael@0 6622 uint32_t startYieldOffset = pc->lastYieldOffset;
michael@0 6623
michael@0 6624 Node body = comprehensionFor(comprehensionKind);
michael@0 6625 if (!body)
michael@0 6626 return null();
michael@0 6627
michael@0 6628 if (comprehensionKind != NotGenerator && pc->lastYieldOffset != startYieldOffset) {
michael@0 6629 reportWithOffset(ParseError, false, pc->lastYieldOffset,
michael@0 6630 JSMSG_BAD_GENEXP_BODY, js_yield_str);
michael@0 6631 return null();
michael@0 6632 }
michael@0 6633
michael@0 6634 return body;
michael@0 6635 }
michael@0 6636
michael@0 6637 template <typename ParseHandler>
michael@0 6638 typename ParseHandler::Node
michael@0 6639 Parser<ParseHandler>::arrayComprehension(uint32_t begin)
michael@0 6640 {
michael@0 6641 Node inner = comprehension(NotGenerator);
michael@0 6642 if (!inner)
michael@0 6643 return null();
michael@0 6644
michael@0 6645 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION);
michael@0 6646
michael@0 6647 Node comp = handler.newList(PNK_ARRAYCOMP, inner);
michael@0 6648 if (!comp)
michael@0 6649 return null();
michael@0 6650
michael@0 6651 handler.setBeginPosition(comp, begin);
michael@0 6652 handler.setEndPosition(comp, pos().end);
michael@0 6653
michael@0 6654 return comp;
michael@0 6655 }
michael@0 6656
michael@0 6657 template <typename ParseHandler>
michael@0 6658 typename ParseHandler::Node
michael@0 6659 Parser<ParseHandler>::generatorComprehension(uint32_t begin)
michael@0 6660 {
michael@0 6661 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
michael@0 6662
michael@0 6663 // We have no problem parsing generator comprehensions inside lazy
michael@0 6664 // functions, but the bytecode emitter currently can't handle them that way,
michael@0 6665 // because when it goes to emit the code for the inner generator function,
michael@0 6666 // it expects outer functions to have non-lazy scripts.
michael@0 6667 if (!abortIfSyntaxParser())
michael@0 6668 return null();
michael@0 6669
michael@0 6670 Node genfn = generatorComprehensionLambda(StarGenerator, begin, null());
michael@0 6671 if (!genfn)
michael@0 6672 return null();
michael@0 6673
michael@0 6674 Node result = handler.newList(PNK_GENEXP, genfn, JSOP_CALL);
michael@0 6675 if (!result)
michael@0 6676 return null();
michael@0 6677 handler.setBeginPosition(result, begin);
michael@0 6678 handler.setEndPosition(result, pos().end);
michael@0 6679
michael@0 6680 return result;
michael@0 6681 }
michael@0 6682
michael@0 6683 template <typename ParseHandler>
michael@0 6684 typename ParseHandler::Node
michael@0 6685 Parser<ParseHandler>::assignExprWithoutYield(unsigned msg)
michael@0 6686 {
michael@0 6687 uint32_t startYieldOffset = pc->lastYieldOffset;
michael@0 6688 Node res = assignExpr();
michael@0 6689 if (res && pc->lastYieldOffset != startYieldOffset) {
michael@0 6690 reportWithOffset(ParseError, false, pc->lastYieldOffset,
michael@0 6691 msg, js_yield_str);
michael@0 6692 return null();
michael@0 6693 }
michael@0 6694 return res;
michael@0 6695 }
michael@0 6696
michael@0 6697 template <typename ParseHandler>
michael@0 6698 bool
michael@0 6699 Parser<ParseHandler>::argumentList(Node listNode, bool *isSpread)
michael@0 6700 {
michael@0 6701 if (tokenStream.matchToken(TOK_RP, TokenStream::Operand)) {
michael@0 6702 handler.setEndPosition(listNode, pos().end);
michael@0 6703 return true;
michael@0 6704 }
michael@0 6705
michael@0 6706 uint32_t startYieldOffset = pc->lastYieldOffset;
michael@0 6707 bool arg0 = true;
michael@0 6708
michael@0 6709 do {
michael@0 6710 bool spread = false;
michael@0 6711 uint32_t begin = 0;
michael@0 6712 if (tokenStream.matchToken(TOK_TRIPLEDOT, TokenStream::Operand)) {
michael@0 6713 spread = true;
michael@0 6714 begin = pos().begin;
michael@0 6715 *isSpread = true;
michael@0 6716 }
michael@0 6717
michael@0 6718 Node argNode = assignExpr();
michael@0 6719 if (!argNode)
michael@0 6720 return false;
michael@0 6721 if (spread) {
michael@0 6722 argNode = handler.newUnary(PNK_SPREAD, JSOP_NOP, begin, argNode);
michael@0 6723 if (!argNode)
michael@0 6724 return null();
michael@0 6725 }
michael@0 6726
michael@0 6727 if (handler.isOperationWithoutParens(argNode, PNK_YIELD) &&
michael@0 6728 tokenStream.peekToken() == TOK_COMMA) {
michael@0 6729 report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
michael@0 6730 return false;
michael@0 6731 }
michael@0 6732 #if JS_HAS_GENERATOR_EXPRS
michael@0 6733 if (!spread && tokenStream.matchToken(TOK_FOR)) {
michael@0 6734 if (pc->lastYieldOffset != startYieldOffset) {
michael@0 6735 reportWithOffset(ParseError, false, pc->lastYieldOffset,
michael@0 6736 JSMSG_BAD_GENEXP_BODY, js_yield_str);
michael@0 6737 return false;
michael@0 6738 }
michael@0 6739 argNode = legacyGeneratorExpr(argNode);
michael@0 6740 if (!argNode)
michael@0 6741 return false;
michael@0 6742 if (!arg0 || tokenStream.peekToken() == TOK_COMMA) {
michael@0 6743 report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
michael@0 6744 return false;
michael@0 6745 }
michael@0 6746 }
michael@0 6747 #endif
michael@0 6748 arg0 = false;
michael@0 6749
michael@0 6750 handler.addList(listNode, argNode);
michael@0 6751 } while (tokenStream.matchToken(TOK_COMMA));
michael@0 6752
michael@0 6753 if (tokenStream.getToken() != TOK_RP) {
michael@0 6754 report(ParseError, false, null(), JSMSG_PAREN_AFTER_ARGS);
michael@0 6755 return false;
michael@0 6756 }
michael@0 6757 handler.setEndPosition(listNode, pos().end);
michael@0 6758 return true;
michael@0 6759 }
michael@0 6760
michael@0 6761 template <typename ParseHandler>
michael@0 6762 typename ParseHandler::Node
michael@0 6763 Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax)
michael@0 6764 {
michael@0 6765 JS_ASSERT(tokenStream.isCurrentTokenType(tt));
michael@0 6766
michael@0 6767 Node lhs;
michael@0 6768
michael@0 6769 JS_CHECK_RECURSION(context, return null());
michael@0 6770
michael@0 6771 /* Check for new expression first. */
michael@0 6772 if (tt == TOK_NEW) {
michael@0 6773 lhs = handler.newList(PNK_NEW, null(), JSOP_NEW);
michael@0 6774 if (!lhs)
michael@0 6775 return null();
michael@0 6776
michael@0 6777 tt = tokenStream.getToken(TokenStream::Operand);
michael@0 6778 Node ctorExpr = memberExpr(tt, false);
michael@0 6779 if (!ctorExpr)
michael@0 6780 return null();
michael@0 6781
michael@0 6782 handler.addList(lhs, ctorExpr);
michael@0 6783
michael@0 6784 if (tokenStream.matchToken(TOK_LP)) {
michael@0 6785 bool isSpread = false;
michael@0 6786 if (!argumentList(lhs, &isSpread))
michael@0 6787 return null();
michael@0 6788 if (isSpread)
michael@0 6789 handler.setOp(lhs, JSOP_SPREADNEW);
michael@0 6790 }
michael@0 6791 } else {
michael@0 6792 lhs = primaryExpr(tt);
michael@0 6793 if (!lhs)
michael@0 6794 return null();
michael@0 6795 }
michael@0 6796
michael@0 6797 while ((tt = tokenStream.getToken()) > TOK_EOF) {
michael@0 6798 Node nextMember;
michael@0 6799 if (tt == TOK_DOT) {
michael@0 6800 tt = tokenStream.getToken(TokenStream::KeywordIsName);
michael@0 6801 if (tt == TOK_ERROR)
michael@0 6802 return null();
michael@0 6803 if (tt == TOK_NAME) {
michael@0 6804 PropertyName *field = tokenStream.currentName();
michael@0 6805 nextMember = handler.newPropertyAccess(lhs, field, pos().end);
michael@0 6806 if (!nextMember)
michael@0 6807 return null();
michael@0 6808 } else {
michael@0 6809 report(ParseError, false, null(), JSMSG_NAME_AFTER_DOT);
michael@0 6810 return null();
michael@0 6811 }
michael@0 6812 } else if (tt == TOK_LB) {
michael@0 6813 Node propExpr = expr();
michael@0 6814 if (!propExpr)
michael@0 6815 return null();
michael@0 6816
michael@0 6817 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
michael@0 6818
michael@0 6819 nextMember = handler.newPropertyByValue(lhs, propExpr, pos().end);
michael@0 6820 if (!nextMember)
michael@0 6821 return null();
michael@0 6822 } else if (allowCallSyntax && tt == TOK_LP) {
michael@0 6823 JSOp op = JSOP_CALL;
michael@0 6824 nextMember = handler.newList(PNK_CALL, null(), JSOP_CALL);
michael@0 6825 if (!nextMember)
michael@0 6826 return null();
michael@0 6827
michael@0 6828 if (JSAtom *atom = handler.isName(lhs)) {
michael@0 6829 if (atom == context->names().eval) {
michael@0 6830 /* Select JSOP_EVAL and flag pc as heavyweight. */
michael@0 6831 op = JSOP_EVAL;
michael@0 6832 pc->sc->setBindingsAccessedDynamically();
michael@0 6833
michael@0 6834 /*
michael@0 6835 * In non-strict mode code, direct calls to eval can add
michael@0 6836 * variables to the call object.
michael@0 6837 */
michael@0 6838 if (pc->sc->isFunctionBox() && !pc->sc->strict)
michael@0 6839 pc->sc->asFunctionBox()->setHasExtensibleScope();
michael@0 6840 }
michael@0 6841 } else if (JSAtom *atom = handler.isGetProp(lhs)) {
michael@0 6842 /* Select JSOP_FUNAPPLY given foo.apply(...). */
michael@0 6843 if (atom == context->names().apply) {
michael@0 6844 op = JSOP_FUNAPPLY;
michael@0 6845 if (pc->sc->isFunctionBox())
michael@0 6846 pc->sc->asFunctionBox()->usesApply = true;
michael@0 6847 } else if (atom == context->names().call) {
michael@0 6848 op = JSOP_FUNCALL;
michael@0 6849 }
michael@0 6850 }
michael@0 6851
michael@0 6852 handler.setBeginPosition(nextMember, lhs);
michael@0 6853 handler.addList(nextMember, lhs);
michael@0 6854
michael@0 6855 bool isSpread = false;
michael@0 6856 if (!argumentList(nextMember, &isSpread))
michael@0 6857 return null();
michael@0 6858 if (isSpread)
michael@0 6859 op = (op == JSOP_EVAL ? JSOP_SPREADEVAL : JSOP_SPREADCALL);
michael@0 6860 handler.setOp(nextMember, op);
michael@0 6861 } else {
michael@0 6862 tokenStream.ungetToken();
michael@0 6863 return lhs;
michael@0 6864 }
michael@0 6865
michael@0 6866 lhs = nextMember;
michael@0 6867 }
michael@0 6868 if (tt == TOK_ERROR)
michael@0 6869 return null();
michael@0 6870 return lhs;
michael@0 6871 }
michael@0 6872
michael@0 6873 template <typename ParseHandler>
michael@0 6874 typename ParseHandler::Node
michael@0 6875 Parser<ParseHandler>::newName(PropertyName *name)
michael@0 6876 {
michael@0 6877 return handler.newName(name, pc->blockid(), pos());
michael@0 6878 }
michael@0 6879
michael@0 6880 template <typename ParseHandler>
michael@0 6881 typename ParseHandler::Node
michael@0 6882 Parser<ParseHandler>::identifierName()
michael@0 6883 {
michael@0 6884 RootedPropertyName name(context, tokenStream.currentName());
michael@0 6885 Node pn = newName(name);
michael@0 6886 if (!pn)
michael@0 6887 return null();
michael@0 6888
michael@0 6889 if (!pc->inDeclDestructuring && !noteNameUse(name, pn))
michael@0 6890 return null();
michael@0 6891
michael@0 6892 return pn;
michael@0 6893 }
michael@0 6894
michael@0 6895 template <typename ParseHandler>
michael@0 6896 typename ParseHandler::Node
michael@0 6897 Parser<ParseHandler>::stringLiteral()
michael@0 6898 {
michael@0 6899 JSAtom *atom = tokenStream.currentToken().atom();
michael@0 6900
michael@0 6901 // Large strings are fast to parse but slow to compress. Stop compression on
michael@0 6902 // them, so we don't wait for a long time for compression to finish at the
michael@0 6903 // end of compilation.
michael@0 6904 const size_t HUGE_STRING = 50000;
michael@0 6905 if (sct && sct->active() && atom->length() >= HUGE_STRING)
michael@0 6906 sct->abort();
michael@0 6907
michael@0 6908 return handler.newStringLiteral(atom, pos());
michael@0 6909 }
michael@0 6910
michael@0 6911 template <typename ParseHandler>
michael@0 6912 typename ParseHandler::Node
michael@0 6913 Parser<ParseHandler>::newRegExp()
michael@0 6914 {
michael@0 6915 // Create the regexp even when doing a syntax parse, to check the regexp's syntax.
michael@0 6916 const jschar *chars = tokenStream.getTokenbuf().begin();
michael@0 6917 size_t length = tokenStream.getTokenbuf().length();
michael@0 6918 RegExpFlag flags = tokenStream.currentToken().regExpFlags();
michael@0 6919
michael@0 6920 Rooted<RegExpObject*> reobj(context);
michael@0 6921 if (RegExpStatics *res = context->global()->getRegExpStatics())
michael@0 6922 reobj = RegExpObject::create(context, res, chars, length, flags, &tokenStream);
michael@0 6923 else
michael@0 6924 reobj = RegExpObject::createNoStatics(context, chars, length, flags, &tokenStream);
michael@0 6925
michael@0 6926 if (!reobj)
michael@0 6927 return null();
michael@0 6928
michael@0 6929 return handler.newRegExp(reobj, pos(), *this);
michael@0 6930 }
michael@0 6931
michael@0 6932 template <typename ParseHandler>
michael@0 6933 typename ParseHandler::Node
michael@0 6934 Parser<ParseHandler>::arrayInitializer()
michael@0 6935 {
michael@0 6936 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LB));
michael@0 6937
michael@0 6938 uint32_t begin = pos().begin;
michael@0 6939 Node literal = handler.newArrayLiteral(begin, pc->blockidGen);
michael@0 6940 if (!literal)
michael@0 6941 return null();
michael@0 6942
michael@0 6943 if (tokenStream.matchToken(TOK_RB, TokenStream::Operand)) {
michael@0 6944 /*
michael@0 6945 * Mark empty arrays as non-constant, since we cannot easily
michael@0 6946 * determine their type.
michael@0 6947 */
michael@0 6948 handler.setListFlag(literal, PNX_NONCONST);
michael@0 6949 } else if (tokenStream.matchToken(TOK_FOR, TokenStream::Operand)) {
michael@0 6950 // ES6 array comprehension.
michael@0 6951 return arrayComprehension(begin);
michael@0 6952 } else {
michael@0 6953 bool spread = false, missingTrailingComma = false;
michael@0 6954 uint32_t index = 0;
michael@0 6955 for (; ; index++) {
michael@0 6956 if (index == JSObject::NELEMENTS_LIMIT) {
michael@0 6957 report(ParseError, false, null(), JSMSG_ARRAY_INIT_TOO_BIG);
michael@0 6958 return null();
michael@0 6959 }
michael@0 6960
michael@0 6961 TokenKind tt = tokenStream.peekToken(TokenStream::Operand);
michael@0 6962 if (tt == TOK_RB)
michael@0 6963 break;
michael@0 6964
michael@0 6965 if (tt == TOK_COMMA) {
michael@0 6966 tokenStream.consumeKnownToken(TOK_COMMA);
michael@0 6967 if (!handler.addElision(literal, pos()))
michael@0 6968 return null();
michael@0 6969 } else if (tt == TOK_TRIPLEDOT) {
michael@0 6970 spread = true;
michael@0 6971 tokenStream.consumeKnownToken(TOK_TRIPLEDOT);
michael@0 6972 uint32_t begin = pos().begin;
michael@0 6973 Node inner = assignExpr();
michael@0 6974 if (!inner)
michael@0 6975 return null();
michael@0 6976 if (!handler.addSpreadElement(literal, begin, inner))
michael@0 6977 return null();
michael@0 6978 } else {
michael@0 6979 Node element = assignExpr();
michael@0 6980 if (!element)
michael@0 6981 return null();
michael@0 6982 if (foldConstants && !FoldConstants(context, &element, this))
michael@0 6983 return null();
michael@0 6984 if (!handler.addArrayElement(literal, element))
michael@0 6985 return null();
michael@0 6986 }
michael@0 6987
michael@0 6988 if (tt != TOK_COMMA) {
michael@0 6989 /* If we didn't already match TOK_COMMA in above case. */
michael@0 6990 if (!tokenStream.matchToken(TOK_COMMA)) {
michael@0 6991 missingTrailingComma = true;
michael@0 6992 break;
michael@0 6993 }
michael@0 6994 }
michael@0 6995 }
michael@0 6996
michael@0 6997 /*
michael@0 6998 * At this point, (index == 0 && missingTrailingComma) implies one
michael@0 6999 * element initialiser was parsed.
michael@0 7000 *
michael@0 7001 * A legacy array comprehension of the form:
michael@0 7002 *
michael@0 7003 * [i * j for (i in o) for (j in p) if (i != j)]
michael@0 7004 *
michael@0 7005 * translates to roughly the following let expression:
michael@0 7006 *
michael@0 7007 * let (array = new Array, i, j) {
michael@0 7008 * for (i in o) let {
michael@0 7009 * for (j in p)
michael@0 7010 * if (i != j)
michael@0 7011 * array.push(i * j)
michael@0 7012 * }
michael@0 7013 * array
michael@0 7014 * }
michael@0 7015 *
michael@0 7016 * where array is a nameless block-local variable. The "roughly" means
michael@0 7017 * that an implementation may optimize away the array.push. A legacy
michael@0 7018 * array comprehension opens exactly one block scope, no matter how many
michael@0 7019 * for heads it contains.
michael@0 7020 *
michael@0 7021 * Each let () {...} or for (let ...) ... compiles to:
michael@0 7022 *
michael@0 7023 * JSOP_PUSHN <N> // Push space for block-scoped locals.
michael@0 7024 * (JSOP_PUSHBLOCKSCOPE <O>) // If a local is aliased, push on scope
michael@0 7025 * // chain.
michael@0 7026 * ...
michael@0 7027 * JSOP_DEBUGLEAVEBLOCK // Invalidate any DebugScope proxies.
michael@0 7028 * JSOP_POPBLOCKSCOPE? // Pop off scope chain, if needed.
michael@0 7029 * JSOP_POPN <N> // Pop space for block-scoped locals.
michael@0 7030 *
michael@0 7031 * where <o> is a literal object representing the block scope,
michael@0 7032 * with <n> properties, naming each var declared in the block.
michael@0 7033 *
michael@0 7034 * Each var declaration in a let-block binds a name in <o> at compile
michael@0 7035 * time. A block-local var is accessed by the JSOP_GETLOCAL and
michael@0 7036 * JSOP_SETLOCAL ops. These ops have an immediate operand, the local
michael@0 7037 * slot's stack index from fp->spbase.
michael@0 7038 *
michael@0 7039 * The legacy array comprehension iteration step, array.push(i * j) in
michael@0 7040 * the example above, is done by <i * j>; JSOP_ARRAYPUSH <array>, where
michael@0 7041 * <array> is the index of array's stack slot.
michael@0 7042 */
michael@0 7043 if (index == 0 && !spread && tokenStream.matchToken(TOK_FOR) && missingTrailingComma)
michael@0 7044 return legacyArrayComprehension(literal);
michael@0 7045
michael@0 7046 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
michael@0 7047 }
michael@0 7048 handler.setEndPosition(literal, pos().end);
michael@0 7049 return literal;
michael@0 7050 }
michael@0 7051
michael@0 7052 static JSAtom*
michael@0 7053 DoubleToAtom(ExclusiveContext *cx, double value)
michael@0 7054 {
michael@0 7055 // This is safe because doubles can not be moved.
michael@0 7056 Value tmp = DoubleValue(value);
michael@0 7057 return ToAtom<CanGC>(cx, HandleValue::fromMarkedLocation(&tmp));
michael@0 7058 }
michael@0 7059
michael@0 7060 template <typename ParseHandler>
michael@0 7061 typename ParseHandler::Node
michael@0 7062 Parser<ParseHandler>::objectLiteral()
michael@0 7063 {
michael@0 7064 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
michael@0 7065
michael@0 7066 /*
michael@0 7067 * A map from property names we've seen thus far to a mask of property
michael@0 7068 * assignment types.
michael@0 7069 */
michael@0 7070 AtomIndexMap seen;
michael@0 7071
michael@0 7072 enum AssignmentType {
michael@0 7073 GET = 0x1,
michael@0 7074 SET = 0x2,
michael@0 7075 VALUE = 0x4 | GET | SET
michael@0 7076 };
michael@0 7077
michael@0 7078 Node literal = handler.newObjectLiteral(pos().begin);
michael@0 7079 if (!literal)
michael@0 7080 return null();
michael@0 7081
michael@0 7082 RootedAtom atom(context);
michael@0 7083 for (;;) {
michael@0 7084 TokenKind ltok = tokenStream.getToken(TokenStream::KeywordIsName);
michael@0 7085 if (ltok == TOK_RC)
michael@0 7086 break;
michael@0 7087
michael@0 7088 JSOp op = JSOP_INITPROP;
michael@0 7089 Node propname;
michael@0 7090 switch (ltok) {
michael@0 7091 case TOK_NUMBER:
michael@0 7092 atom = DoubleToAtom(context, tokenStream.currentToken().number());
michael@0 7093 if (!atom)
michael@0 7094 return null();
michael@0 7095 propname = newNumber(tokenStream.currentToken());
michael@0 7096 break;
michael@0 7097
michael@0 7098 case TOK_NAME: {
michael@0 7099 atom = tokenStream.currentName();
michael@0 7100 if (atom == context->names().get) {
michael@0 7101 op = JSOP_INITPROP_GETTER;
michael@0 7102 } else if (atom == context->names().set) {
michael@0 7103 op = JSOP_INITPROP_SETTER;
michael@0 7104 } else {
michael@0 7105 propname = handler.newIdentifier(atom, pos());
michael@0 7106 if (!propname)
michael@0 7107 return null();
michael@0 7108 break;
michael@0 7109 }
michael@0 7110
michael@0 7111 // We have parsed |get| or |set|. Look for an accessor property
michael@0 7112 // name next.
michael@0 7113 TokenKind tt = tokenStream.getToken(TokenStream::KeywordIsName);
michael@0 7114 if (tt == TOK_NAME) {
michael@0 7115 atom = tokenStream.currentName();
michael@0 7116 propname = newName(atom->asPropertyName());
michael@0 7117 if (!propname)
michael@0 7118 return null();
michael@0 7119 } else if (tt == TOK_STRING) {
michael@0 7120 atom = tokenStream.currentToken().atom();
michael@0 7121
michael@0 7122 uint32_t index;
michael@0 7123 if (atom->isIndex(&index)) {
michael@0 7124 propname = handler.newNumber(index, NoDecimal, pos());
michael@0 7125 if (!propname)
michael@0 7126 return null();
michael@0 7127 atom = DoubleToAtom(context, index);
michael@0 7128 if (!atom)
michael@0 7129 return null();
michael@0 7130 } else {
michael@0 7131 propname = stringLiteral();
michael@0 7132 if (!propname)
michael@0 7133 return null();
michael@0 7134 }
michael@0 7135 } else if (tt == TOK_NUMBER) {
michael@0 7136 atom = DoubleToAtom(context, tokenStream.currentToken().number());
michael@0 7137 if (!atom)
michael@0 7138 return null();
michael@0 7139 propname = newNumber(tokenStream.currentToken());
michael@0 7140 if (!propname)
michael@0 7141 return null();
michael@0 7142 } else {
michael@0 7143 // Not an accessor property after all.
michael@0 7144 tokenStream.ungetToken();
michael@0 7145 propname = handler.newIdentifier(atom, pos());
michael@0 7146 if (!propname)
michael@0 7147 return null();
michael@0 7148 op = JSOP_INITPROP;
michael@0 7149 break;
michael@0 7150 }
michael@0 7151
michael@0 7152 JS_ASSERT(op == JSOP_INITPROP_GETTER || op == JSOP_INITPROP_SETTER);
michael@0 7153 break;
michael@0 7154 }
michael@0 7155
michael@0 7156 case TOK_STRING: {
michael@0 7157 atom = tokenStream.currentToken().atom();
michael@0 7158 uint32_t index;
michael@0 7159 if (atom->isIndex(&index)) {
michael@0 7160 propname = handler.newNumber(index, NoDecimal, pos());
michael@0 7161 if (!propname)
michael@0 7162 return null();
michael@0 7163 } else {
michael@0 7164 propname = stringLiteral();
michael@0 7165 if (!propname)
michael@0 7166 return null();
michael@0 7167 }
michael@0 7168 break;
michael@0 7169 }
michael@0 7170
michael@0 7171 default:
michael@0 7172 report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
michael@0 7173 return null();
michael@0 7174 }
michael@0 7175
michael@0 7176 if (op == JSOP_INITPROP) {
michael@0 7177 TokenKind tt = tokenStream.getToken();
michael@0 7178 Node propexpr;
michael@0 7179 if (tt == TOK_COLON) {
michael@0 7180 propexpr = assignExpr();
michael@0 7181 if (!propexpr)
michael@0 7182 return null();
michael@0 7183
michael@0 7184 if (foldConstants && !FoldConstants(context, &propexpr, this))
michael@0 7185 return null();
michael@0 7186
michael@0 7187 /*
michael@0 7188 * Treat initializers which mutate __proto__ as non-constant,
michael@0 7189 * so that we can later assume singleton objects delegate to
michael@0 7190 * the default Object.prototype.
michael@0 7191 */
michael@0 7192 if (!handler.isConstant(propexpr) || atom == context->names().proto)
michael@0 7193 handler.setListFlag(literal, PNX_NONCONST);
michael@0 7194
michael@0 7195 if (!handler.addPropertyDefinition(literal, propname, propexpr))
michael@0 7196 return null();
michael@0 7197 }
michael@0 7198 else if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC)) {
michael@0 7199 /*
michael@0 7200 * Support, e.g., |var {x, y} = o| as destructuring shorthand
michael@0 7201 * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
michael@0 7202 */
michael@0 7203 if (!abortIfSyntaxParser())
michael@0 7204 return null();
michael@0 7205 tokenStream.ungetToken();
michael@0 7206 if (!tokenStream.checkForKeyword(atom->charsZ(), atom->length(), nullptr))
michael@0 7207 return null();
michael@0 7208 PropertyName *name = handler.isName(propname);
michael@0 7209 JS_ASSERT(atom);
michael@0 7210 propname = newName(name);
michael@0 7211 if (!propname)
michael@0 7212 return null();
michael@0 7213 if (!handler.addShorthandPropertyDefinition(literal, propname))
michael@0 7214 return null();
michael@0 7215 }
michael@0 7216 else {
michael@0 7217 report(ParseError, false, null(), JSMSG_COLON_AFTER_ID);
michael@0 7218 return null();
michael@0 7219 }
michael@0 7220 } else {
michael@0 7221 /* NB: Getter function in { get x(){} } is unnamed. */
michael@0 7222 Rooted<PropertyName*> funName(context, nullptr);
michael@0 7223 TokenStream::Position start(keepAtoms);
michael@0 7224 tokenStream.tell(&start);
michael@0 7225 Node accessor = functionDef(funName, start, op == JSOP_INITPROP_GETTER ? Getter : Setter,
michael@0 7226 Expression, NotGenerator);
michael@0 7227 if (!accessor)
michael@0 7228 return null();
michael@0 7229 if (!handler.addAccessorPropertyDefinition(literal, propname, accessor, op))
michael@0 7230 return null();
michael@0 7231 }
michael@0 7232
michael@0 7233 /*
michael@0 7234 * Check for duplicate property names. Duplicate data properties
michael@0 7235 * only conflict in strict mode. Duplicate getter or duplicate
michael@0 7236 * setter halves always conflict. A data property conflicts with
michael@0 7237 * any part of an accessor property.
michael@0 7238 */
michael@0 7239 AssignmentType assignType;
michael@0 7240 if (op == JSOP_INITPROP)
michael@0 7241 assignType = VALUE;
michael@0 7242 else if (op == JSOP_INITPROP_GETTER)
michael@0 7243 assignType = GET;
michael@0 7244 else if (op == JSOP_INITPROP_SETTER)
michael@0 7245 assignType = SET;
michael@0 7246 else
michael@0 7247 MOZ_ASSUME_UNREACHABLE("bad opcode in object initializer");
michael@0 7248
michael@0 7249 AtomIndexAddPtr p = seen.lookupForAdd(atom);
michael@0 7250 if (p) {
michael@0 7251 jsatomid index = p.value();
michael@0 7252 AssignmentType oldAssignType = AssignmentType(index);
michael@0 7253 if ((oldAssignType & assignType) &&
michael@0 7254 (oldAssignType != VALUE || assignType != VALUE || pc->sc->needStrictChecks()))
michael@0 7255 {
michael@0 7256 JSAutoByteString name;
michael@0 7257 if (!AtomToPrintableString(context, atom, &name))
michael@0 7258 return null();
michael@0 7259
michael@0 7260 ParseReportKind reportKind =
michael@0 7261 (oldAssignType == VALUE && assignType == VALUE && !pc->sc->needStrictChecks())
michael@0 7262 ? ParseWarning
michael@0 7263 : (pc->sc->needStrictChecks() ? ParseStrictError : ParseError);
michael@0 7264 if (!report(reportKind, pc->sc->strict, null(),
michael@0 7265 JSMSG_DUPLICATE_PROPERTY, name.ptr()))
michael@0 7266 {
michael@0 7267 return null();
michael@0 7268 }
michael@0 7269 }
michael@0 7270 p.value() = assignType | oldAssignType;
michael@0 7271 } else {
michael@0 7272 if (!seen.add(p, atom, assignType))
michael@0 7273 return null();
michael@0 7274 }
michael@0 7275
michael@0 7276 TokenKind tt = tokenStream.getToken();
michael@0 7277 if (tt == TOK_RC)
michael@0 7278 break;
michael@0 7279 if (tt != TOK_COMMA) {
michael@0 7280 report(ParseError, false, null(), JSMSG_CURLY_AFTER_LIST);
michael@0 7281 return null();
michael@0 7282 }
michael@0 7283 }
michael@0 7284
michael@0 7285 handler.setEndPosition(literal, pos().end);
michael@0 7286 return literal;
michael@0 7287 }
michael@0 7288
michael@0 7289 template <typename ParseHandler>
michael@0 7290 typename ParseHandler::Node
michael@0 7291 Parser<ParseHandler>::primaryExpr(TokenKind tt)
michael@0 7292 {
michael@0 7293 JS_ASSERT(tokenStream.isCurrentTokenType(tt));
michael@0 7294 JS_CHECK_RECURSION(context, return null());
michael@0 7295
michael@0 7296 switch (tt) {
michael@0 7297 case TOK_FUNCTION:
michael@0 7298 return functionExpr();
michael@0 7299
michael@0 7300 case TOK_LB:
michael@0 7301 return arrayInitializer();
michael@0 7302
michael@0 7303 case TOK_LC:
michael@0 7304 return objectLiteral();
michael@0 7305
michael@0 7306 case TOK_LET:
michael@0 7307 return letBlock(LetExpresion);
michael@0 7308
michael@0 7309 case TOK_LP:
michael@0 7310 return parenExprOrGeneratorComprehension();
michael@0 7311
michael@0 7312 case TOK_STRING:
michael@0 7313 return stringLiteral();
michael@0 7314
michael@0 7315 case TOK_YIELD:
michael@0 7316 if (!checkYieldNameValidity())
michael@0 7317 return null();
michael@0 7318 // Fall through.
michael@0 7319 case TOK_NAME:
michael@0 7320 return identifierName();
michael@0 7321
michael@0 7322 case TOK_REGEXP:
michael@0 7323 return newRegExp();
michael@0 7324
michael@0 7325 case TOK_NUMBER:
michael@0 7326 return newNumber(tokenStream.currentToken());
michael@0 7327
michael@0 7328 case TOK_TRUE:
michael@0 7329 return handler.newBooleanLiteral(true, pos());
michael@0 7330 case TOK_FALSE:
michael@0 7331 return handler.newBooleanLiteral(false, pos());
michael@0 7332 case TOK_THIS:
michael@0 7333 return handler.newThisLiteral(pos());
michael@0 7334 case TOK_NULL:
michael@0 7335 return handler.newNullLiteral(pos());
michael@0 7336
michael@0 7337 case TOK_RP:
michael@0 7338 // Not valid expression syntax, but this is valid in an arrow function
michael@0 7339 // with no params: `() => body`.
michael@0 7340 if (tokenStream.peekToken() == TOK_ARROW) {
michael@0 7341 tokenStream.ungetToken(); // put back right paren
michael@0 7342
michael@0 7343 // Now just return something that will allow parsing to continue.
michael@0 7344 // It doesn't matter what; when we reach the =>, we will rewind and
michael@0 7345 // reparse the whole arrow function. See Parser::assignExpr.
michael@0 7346 return handler.newNullLiteral(pos());
michael@0 7347 }
michael@0 7348 report(ParseError, false, null(), JSMSG_SYNTAX_ERROR);
michael@0 7349 return null();
michael@0 7350
michael@0 7351 case TOK_TRIPLEDOT:
michael@0 7352 // Not valid expression syntax, but this is valid in an arrow function
michael@0 7353 // with a rest param: `(a, b, ...rest) => body`.
michael@0 7354 if (tokenStream.matchToken(TOK_NAME) &&
michael@0 7355 tokenStream.matchToken(TOK_RP) &&
michael@0 7356 tokenStream.peekToken() == TOK_ARROW)
michael@0 7357 {
michael@0 7358 tokenStream.ungetToken(); // put back right paren
michael@0 7359
michael@0 7360 // Return an arbitrary expression node. See case TOK_RP above.
michael@0 7361 return handler.newNullLiteral(pos());
michael@0 7362 }
michael@0 7363 report(ParseError, false, null(), JSMSG_SYNTAX_ERROR);
michael@0 7364 return null();
michael@0 7365
michael@0 7366 case TOK_ERROR:
michael@0 7367 /* The scanner or one of its subroutines reported the error. */
michael@0 7368 return null();
michael@0 7369
michael@0 7370 default:
michael@0 7371 report(ParseError, false, null(), JSMSG_SYNTAX_ERROR);
michael@0 7372 return null();
michael@0 7373 }
michael@0 7374 }
michael@0 7375
michael@0 7376 template <typename ParseHandler>
michael@0 7377 typename ParseHandler::Node
michael@0 7378 Parser<ParseHandler>::parenExprOrGeneratorComprehension()
michael@0 7379 {
michael@0 7380 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
michael@0 7381 uint32_t begin = pos().begin;
michael@0 7382 uint32_t startYieldOffset = pc->lastYieldOffset;
michael@0 7383
michael@0 7384 if (tokenStream.matchToken(TOK_FOR, TokenStream::Operand))
michael@0 7385 return generatorComprehension(begin);
michael@0 7386
michael@0 7387 /*
michael@0 7388 * Always accept the 'in' operator in a parenthesized expression,
michael@0 7389 * where it's unambiguous, even if we might be parsing the init of a
michael@0 7390 * for statement.
michael@0 7391 */
michael@0 7392 bool oldParsingForInit = pc->parsingForInit;
michael@0 7393 pc->parsingForInit = false;
michael@0 7394 Node pn = expr();
michael@0 7395 pc->parsingForInit = oldParsingForInit;
michael@0 7396
michael@0 7397 if (!pn)
michael@0 7398 return null();
michael@0 7399
michael@0 7400 #if JS_HAS_GENERATOR_EXPRS
michael@0 7401 if (tokenStream.matchToken(TOK_FOR)) {
michael@0 7402 if (pc->lastYieldOffset != startYieldOffset) {
michael@0 7403 reportWithOffset(ParseError, false, pc->lastYieldOffset,
michael@0 7404 JSMSG_BAD_GENEXP_BODY, js_yield_str);
michael@0 7405 return null();
michael@0 7406 }
michael@0 7407 if (handler.isOperationWithoutParens(pn, PNK_COMMA)) {
michael@0 7408 report(ParseError, false, null(),
michael@0 7409 JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
michael@0 7410 return null();
michael@0 7411 }
michael@0 7412 pn = legacyGeneratorExpr(pn);
michael@0 7413 if (!pn)
michael@0 7414 return null();
michael@0 7415 handler.setBeginPosition(pn, begin);
michael@0 7416 if (tokenStream.getToken() != TOK_RP) {
michael@0 7417 report(ParseError, false, null(),
michael@0 7418 JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
michael@0 7419 return null();
michael@0 7420 }
michael@0 7421 handler.setEndPosition(pn, pos().end);
michael@0 7422 handler.setInParens(pn);
michael@0 7423 return pn;
michael@0 7424 }
michael@0 7425 #endif /* JS_HAS_GENERATOR_EXPRS */
michael@0 7426
michael@0 7427 pn = handler.setInParens(pn);
michael@0 7428
michael@0 7429 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
michael@0 7430
michael@0 7431 return pn;
michael@0 7432 }
michael@0 7433
michael@0 7434 // Legacy generator comprehensions can sometimes appear without parentheses.
michael@0 7435 // For example:
michael@0 7436 //
michael@0 7437 // foo(x for (x in bar))
michael@0 7438 //
michael@0 7439 // In this case the parens are part of the call, and not part of the generator
michael@0 7440 // comprehension. This can happen in these contexts:
michael@0 7441 //
michael@0 7442 // if (_)
michael@0 7443 // while (_) {}
michael@0 7444 // do {} while (_)
michael@0 7445 // switch (_) {}
michael@0 7446 // with (_) {}
michael@0 7447 // foo(_) // must be first and only argument
michael@0 7448 //
michael@0 7449 // This is not the case for ES6 generator comprehensions; they must always be in
michael@0 7450 // parentheses.
michael@0 7451
michael@0 7452 template <typename ParseHandler>
michael@0 7453 typename ParseHandler::Node
michael@0 7454 Parser<ParseHandler>::exprInParens()
michael@0 7455 {
michael@0 7456 JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
michael@0 7457 uint32_t begin = pos().begin;
michael@0 7458 uint32_t startYieldOffset = pc->lastYieldOffset;
michael@0 7459
michael@0 7460 /*
michael@0 7461 * Always accept the 'in' operator in a parenthesized expression,
michael@0 7462 * where it's unambiguous, even if we might be parsing the init of a
michael@0 7463 * for statement.
michael@0 7464 */
michael@0 7465 bool oldParsingForInit = pc->parsingForInit;
michael@0 7466 pc->parsingForInit = false;
michael@0 7467 Node pn = expr();
michael@0 7468 pc->parsingForInit = oldParsingForInit;
michael@0 7469
michael@0 7470 if (!pn)
michael@0 7471 return null();
michael@0 7472
michael@0 7473 #if JS_HAS_GENERATOR_EXPRS
michael@0 7474 if (tokenStream.matchToken(TOK_FOR)) {
michael@0 7475 if (pc->lastYieldOffset != startYieldOffset) {
michael@0 7476 reportWithOffset(ParseError, false, pc->lastYieldOffset,
michael@0 7477 JSMSG_BAD_GENEXP_BODY, js_yield_str);
michael@0 7478 return null();
michael@0 7479 }
michael@0 7480 if (handler.isOperationWithoutParens(pn, PNK_COMMA)) {
michael@0 7481 report(ParseError, false, null(),
michael@0 7482 JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
michael@0 7483 return null();
michael@0 7484 }
michael@0 7485 pn = legacyGeneratorExpr(pn);
michael@0 7486 if (!pn)
michael@0 7487 return null();
michael@0 7488 handler.setBeginPosition(pn, begin);
michael@0 7489 }
michael@0 7490 #endif /* JS_HAS_GENERATOR_EXPRS */
michael@0 7491
michael@0 7492 return pn;
michael@0 7493 }
michael@0 7494
michael@0 7495 template class Parser<FullParseHandler>;
michael@0 7496 template class Parser<SyntaxParseHandler>;
michael@0 7497
michael@0 7498 } /* namespace frontend */
michael@0 7499 } /* namespace js */

mercurial