js/src/frontend/BytecodeEmitter.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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 bytecode generation.
michael@0 9 */
michael@0 10
michael@0 11 #include "frontend/BytecodeEmitter.h"
michael@0 12
michael@0 13 #include "mozilla/DebugOnly.h"
michael@0 14 #include "mozilla/FloatingPoint.h"
michael@0 15 #include "mozilla/PodOperations.h"
michael@0 16
michael@0 17 #include <string.h>
michael@0 18
michael@0 19 #include "jsapi.h"
michael@0 20 #include "jsatom.h"
michael@0 21 #include "jscntxt.h"
michael@0 22 #include "jsfun.h"
michael@0 23 #include "jsnum.h"
michael@0 24 #include "jsopcode.h"
michael@0 25 #include "jsscript.h"
michael@0 26 #include "jstypes.h"
michael@0 27 #include "jsutil.h"
michael@0 28
michael@0 29 #include "frontend/Parser.h"
michael@0 30 #include "frontend/TokenStream.h"
michael@0 31 #include "jit/AsmJSLink.h"
michael@0 32 #include "vm/Debugger.h"
michael@0 33
michael@0 34 #include "jsatominlines.h"
michael@0 35 #include "jsobjinlines.h"
michael@0 36 #include "jsscriptinlines.h"
michael@0 37
michael@0 38 #include "frontend/ParseMaps-inl.h"
michael@0 39 #include "frontend/ParseNode-inl.h"
michael@0 40 #include "vm/ScopeObject-inl.h"
michael@0 41
michael@0 42 using namespace js;
michael@0 43 using namespace js::gc;
michael@0 44 using namespace js::frontend;
michael@0 45
michael@0 46 using mozilla::DebugOnly;
michael@0 47 using mozilla::NumberIsInt32;
michael@0 48 using mozilla::PodCopy;
michael@0 49
michael@0 50 static bool
michael@0 51 SetSrcNoteOffset(ExclusiveContext *cx, BytecodeEmitter *bce, unsigned index, unsigned which, ptrdiff_t offset);
michael@0 52
michael@0 53 struct frontend::StmtInfoBCE : public StmtInfoBase
michael@0 54 {
michael@0 55 StmtInfoBCE *down; /* info for enclosing statement */
michael@0 56 StmtInfoBCE *downScope; /* next enclosing lexical scope */
michael@0 57
michael@0 58 ptrdiff_t update; /* loop update offset (top if none) */
michael@0 59 ptrdiff_t breaks; /* offset of last break in loop */
michael@0 60 ptrdiff_t continues; /* offset of last continue in loop */
michael@0 61 uint32_t blockScopeIndex; /* index of scope in BlockScopeArray */
michael@0 62
michael@0 63 StmtInfoBCE(ExclusiveContext *cx) : StmtInfoBase(cx) {}
michael@0 64
michael@0 65 /*
michael@0 66 * To reuse space, alias two of the ptrdiff_t fields for use during
michael@0 67 * try/catch/finally code generation and backpatching.
michael@0 68 *
michael@0 69 * Only a loop, switch, or label statement info record can have breaks and
michael@0 70 * continues, and only a for loop has an update backpatch chain, so it's
michael@0 71 * safe to overlay these for the "trying" StmtTypes.
michael@0 72 */
michael@0 73
michael@0 74 ptrdiff_t &gosubs() {
michael@0 75 JS_ASSERT(type == STMT_FINALLY);
michael@0 76 return breaks;
michael@0 77 }
michael@0 78
michael@0 79 ptrdiff_t &guardJump() {
michael@0 80 JS_ASSERT(type == STMT_TRY || type == STMT_FINALLY);
michael@0 81 return continues;
michael@0 82 }
michael@0 83 };
michael@0 84
michael@0 85
michael@0 86 namespace {
michael@0 87
michael@0 88 struct LoopStmtInfo : public StmtInfoBCE
michael@0 89 {
michael@0 90 int32_t stackDepth; // Stack depth when this loop was pushed.
michael@0 91 uint32_t loopDepth; // Loop depth.
michael@0 92
michael@0 93 // Can we OSR into Ion from here? True unless there is non-loop state on the stack.
michael@0 94 bool canIonOsr;
michael@0 95
michael@0 96 LoopStmtInfo(ExclusiveContext *cx) : StmtInfoBCE(cx) {}
michael@0 97
michael@0 98 static LoopStmtInfo* fromStmtInfo(StmtInfoBCE *stmt) {
michael@0 99 JS_ASSERT(stmt->isLoop());
michael@0 100 return static_cast<LoopStmtInfo*>(stmt);
michael@0 101 }
michael@0 102 };
michael@0 103
michael@0 104 } // anonymous namespace
michael@0 105
michael@0 106 BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent,
michael@0 107 Parser<FullParseHandler> *parser, SharedContext *sc,
michael@0 108 HandleScript script, bool insideEval, HandleScript evalCaller,
michael@0 109 bool hasGlobalScope, uint32_t lineNum, EmitterMode emitterMode)
michael@0 110 : sc(sc),
michael@0 111 parent(parent),
michael@0 112 script(sc->context, script),
michael@0 113 prolog(sc->context, lineNum),
michael@0 114 main(sc->context, lineNum),
michael@0 115 current(&main),
michael@0 116 parser(parser),
michael@0 117 evalCaller(evalCaller),
michael@0 118 topStmt(nullptr),
michael@0 119 topScopeStmt(nullptr),
michael@0 120 staticScope(sc->context),
michael@0 121 atomIndices(sc->context),
michael@0 122 firstLine(lineNum),
michael@0 123 stackDepth(0), maxStackDepth(0),
michael@0 124 arrayCompDepth(0),
michael@0 125 emitLevel(0),
michael@0 126 constList(sc->context),
michael@0 127 tryNoteList(sc->context),
michael@0 128 blockScopeList(sc->context),
michael@0 129 typesetCount(0),
michael@0 130 hasSingletons(false),
michael@0 131 emittingForInit(false),
michael@0 132 emittingRunOnceLambda(false),
michael@0 133 lazyRunOnceLambda(false),
michael@0 134 insideEval(insideEval),
michael@0 135 hasGlobalScope(hasGlobalScope),
michael@0 136 emitterMode(emitterMode)
michael@0 137 {
michael@0 138 JS_ASSERT_IF(evalCaller, insideEval);
michael@0 139 }
michael@0 140
michael@0 141 bool
michael@0 142 BytecodeEmitter::init()
michael@0 143 {
michael@0 144 return atomIndices.ensureMap(sc->context);
michael@0 145 }
michael@0 146
michael@0 147 static ptrdiff_t
michael@0 148 EmitCheck(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t delta)
michael@0 149 {
michael@0 150 ptrdiff_t offset = bce->code().length();
michael@0 151
michael@0 152 // Start it off moderately large to avoid repeated resizings early on.
michael@0 153 if (bce->code().capacity() == 0 && !bce->code().reserve(1024))
michael@0 154 return -1;
michael@0 155
michael@0 156 jsbytecode dummy = 0;
michael@0 157 if (!bce->code().appendN(dummy, delta)) {
michael@0 158 js_ReportOutOfMemory(cx);
michael@0 159 return -1;
michael@0 160 }
michael@0 161 return offset;
michael@0 162 }
michael@0 163
michael@0 164 static void
michael@0 165 UpdateDepth(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t target)
michael@0 166 {
michael@0 167 jsbytecode *pc = bce->code(target);
michael@0 168 JSOp op = (JSOp) *pc;
michael@0 169 const JSCodeSpec *cs = &js_CodeSpec[op];
michael@0 170
michael@0 171 if (cs->format & JOF_TMPSLOT_MASK) {
michael@0 172 /*
michael@0 173 * An opcode may temporarily consume stack space during execution.
michael@0 174 * Account for this in maxStackDepth separately from uses/defs here.
michael@0 175 */
michael@0 176 uint32_t depth = (uint32_t) bce->stackDepth +
michael@0 177 ((cs->format & JOF_TMPSLOT_MASK) >> JOF_TMPSLOT_SHIFT);
michael@0 178 if (depth > bce->maxStackDepth)
michael@0 179 bce->maxStackDepth = depth;
michael@0 180 }
michael@0 181
michael@0 182 int nuses = StackUses(nullptr, pc);
michael@0 183 int ndefs = StackDefs(nullptr, pc);
michael@0 184
michael@0 185 bce->stackDepth -= nuses;
michael@0 186 JS_ASSERT(bce->stackDepth >= 0);
michael@0 187 bce->stackDepth += ndefs;
michael@0 188 if ((uint32_t)bce->stackDepth > bce->maxStackDepth)
michael@0 189 bce->maxStackDepth = bce->stackDepth;
michael@0 190 }
michael@0 191
michael@0 192 ptrdiff_t
michael@0 193 frontend::Emit1(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op)
michael@0 194 {
michael@0 195 ptrdiff_t offset = EmitCheck(cx, bce, 1);
michael@0 196 if (offset < 0)
michael@0 197 return -1;
michael@0 198
michael@0 199 jsbytecode *code = bce->code(offset);
michael@0 200 code[0] = jsbytecode(op);
michael@0 201 UpdateDepth(cx, bce, offset);
michael@0 202 return offset;
michael@0 203 }
michael@0 204
michael@0 205 ptrdiff_t
michael@0 206 frontend::Emit2(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1)
michael@0 207 {
michael@0 208 ptrdiff_t offset = EmitCheck(cx, bce, 2);
michael@0 209 if (offset < 0)
michael@0 210 return -1;
michael@0 211
michael@0 212 jsbytecode *code = bce->code(offset);
michael@0 213 code[0] = jsbytecode(op);
michael@0 214 code[1] = op1;
michael@0 215 UpdateDepth(cx, bce, offset);
michael@0 216 return offset;
michael@0 217 }
michael@0 218
michael@0 219 ptrdiff_t
michael@0 220 frontend::Emit3(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1,
michael@0 221 jsbytecode op2)
michael@0 222 {
michael@0 223 /* These should filter through EmitVarOp. */
michael@0 224 JS_ASSERT(!IsArgOp(op));
michael@0 225 JS_ASSERT(!IsLocalOp(op));
michael@0 226
michael@0 227 ptrdiff_t offset = EmitCheck(cx, bce, 3);
michael@0 228 if (offset < 0)
michael@0 229 return -1;
michael@0 230
michael@0 231 jsbytecode *code = bce->code(offset);
michael@0 232 code[0] = jsbytecode(op);
michael@0 233 code[1] = op1;
michael@0 234 code[2] = op2;
michael@0 235 UpdateDepth(cx, bce, offset);
michael@0 236 return offset;
michael@0 237 }
michael@0 238
michael@0 239 ptrdiff_t
michael@0 240 frontend::EmitN(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op, size_t extra)
michael@0 241 {
michael@0 242 ptrdiff_t length = 1 + (ptrdiff_t)extra;
michael@0 243 ptrdiff_t offset = EmitCheck(cx, bce, length);
michael@0 244 if (offset < 0)
michael@0 245 return -1;
michael@0 246
michael@0 247 jsbytecode *code = bce->code(offset);
michael@0 248 code[0] = jsbytecode(op);
michael@0 249 /* The remaining |extra| bytes are set by the caller */
michael@0 250
michael@0 251 /*
michael@0 252 * Don't UpdateDepth if op's use-count comes from the immediate
michael@0 253 * operand yet to be stored in the extra bytes after op.
michael@0 254 */
michael@0 255 if (js_CodeSpec[op].nuses >= 0)
michael@0 256 UpdateDepth(cx, bce, offset);
michael@0 257
michael@0 258 return offset;
michael@0 259 }
michael@0 260
michael@0 261 static ptrdiff_t
michael@0 262 EmitJump(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op, ptrdiff_t off)
michael@0 263 {
michael@0 264 ptrdiff_t offset = EmitCheck(cx, bce, 5);
michael@0 265 if (offset < 0)
michael@0 266 return -1;
michael@0 267
michael@0 268 jsbytecode *code = bce->code(offset);
michael@0 269 code[0] = jsbytecode(op);
michael@0 270 SET_JUMP_OFFSET(code, off);
michael@0 271 UpdateDepth(cx, bce, offset);
michael@0 272 return offset;
michael@0 273 }
michael@0 274
michael@0 275 static ptrdiff_t
michael@0 276 EmitCall(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op, uint16_t argc)
michael@0 277 {
michael@0 278 return Emit3(cx, bce, op, ARGC_HI(argc), ARGC_LO(argc));
michael@0 279 }
michael@0 280
michael@0 281 // Dup the var in operand stack slot "slot". The first item on the operand
michael@0 282 // stack is one slot past the last fixed slot. The last (most recent) item is
michael@0 283 // slot bce->stackDepth - 1.
michael@0 284 //
michael@0 285 // The instruction that is written (JSOP_DUPAT) switches the depth around so
michael@0 286 // that it is addressed from the sp instead of from the fp. This is useful when
michael@0 287 // you don't know the size of the fixed stack segment (nfixed), as is the case
michael@0 288 // when compiling scripts (because each statement is parsed and compiled
michael@0 289 // separately, but they all together form one script with one fixed stack
michael@0 290 // frame).
michael@0 291 static bool
michael@0 292 EmitDupAt(ExclusiveContext *cx, BytecodeEmitter *bce, unsigned slot)
michael@0 293 {
michael@0 294 JS_ASSERT(slot < unsigned(bce->stackDepth));
michael@0 295 // The slot's position on the operand stack, measured from the top.
michael@0 296 unsigned slotFromTop = bce->stackDepth - 1 - slot;
michael@0 297 if (slotFromTop >= JS_BIT(24)) {
michael@0 298 bce->reportError(nullptr, JSMSG_TOO_MANY_LOCALS);
michael@0 299 return false;
michael@0 300 }
michael@0 301 ptrdiff_t off = EmitN(cx, bce, JSOP_DUPAT, 3);
michael@0 302 if (off < 0)
michael@0 303 return false;
michael@0 304 jsbytecode *pc = bce->code(off);
michael@0 305 SET_UINT24(pc, slotFromTop);
michael@0 306 return true;
michael@0 307 }
michael@0 308
michael@0 309 /* XXX too many "... statement" L10N gaffes below -- fix via js.msg! */
michael@0 310 const char js_with_statement_str[] = "with statement";
michael@0 311 const char js_finally_block_str[] = "finally block";
michael@0 312 const char js_script_str[] = "script";
michael@0 313
michael@0 314 static const char * const statementName[] = {
michael@0 315 "label statement", /* LABEL */
michael@0 316 "if statement", /* IF */
michael@0 317 "else statement", /* ELSE */
michael@0 318 "destructuring body", /* BODY */
michael@0 319 "switch statement", /* SWITCH */
michael@0 320 "block", /* BLOCK */
michael@0 321 js_with_statement_str, /* WITH */
michael@0 322 "catch block", /* CATCH */
michael@0 323 "try block", /* TRY */
michael@0 324 js_finally_block_str, /* FINALLY */
michael@0 325 js_finally_block_str, /* SUBROUTINE */
michael@0 326 "do loop", /* DO_LOOP */
michael@0 327 "for loop", /* FOR_LOOP */
michael@0 328 "for/in loop", /* FOR_IN_LOOP */
michael@0 329 "for/of loop", /* FOR_OF_LOOP */
michael@0 330 "while loop", /* WHILE_LOOP */
michael@0 331 };
michael@0 332
michael@0 333 JS_STATIC_ASSERT(JS_ARRAY_LENGTH(statementName) == STMT_LIMIT);
michael@0 334
michael@0 335 static const char *
michael@0 336 StatementName(StmtInfoBCE *topStmt)
michael@0 337 {
michael@0 338 if (!topStmt)
michael@0 339 return js_script_str;
michael@0 340 return statementName[topStmt->type];
michael@0 341 }
michael@0 342
michael@0 343 static void
michael@0 344 ReportStatementTooLarge(TokenStream &ts, StmtInfoBCE *topStmt)
michael@0 345 {
michael@0 346 ts.reportError(JSMSG_NEED_DIET, StatementName(topStmt));
michael@0 347 }
michael@0 348
michael@0 349 /*
michael@0 350 * Emit a backpatch op with offset pointing to the previous jump of this type,
michael@0 351 * so that we can walk back up the chain fixing up the op and jump offset.
michael@0 352 */
michael@0 353 static ptrdiff_t
michael@0 354 EmitBackPatchOp(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t *lastp)
michael@0 355 {
michael@0 356 ptrdiff_t offset, delta;
michael@0 357
michael@0 358 offset = bce->offset();
michael@0 359 delta = offset - *lastp;
michael@0 360 *lastp = offset;
michael@0 361 JS_ASSERT(delta > 0);
michael@0 362 return EmitJump(cx, bce, JSOP_BACKPATCH, delta);
michael@0 363 }
michael@0 364
michael@0 365 static inline unsigned
michael@0 366 LengthOfSetLine(unsigned line)
michael@0 367 {
michael@0 368 return 1 /* SN_SETLINE */ + (line > SN_4BYTE_OFFSET_MASK ? 4 : 1);
michael@0 369 }
michael@0 370
michael@0 371 /* Updates line number notes, not column notes. */
michael@0 372 static inline bool
michael@0 373 UpdateLineNumberNotes(ExclusiveContext *cx, BytecodeEmitter *bce, uint32_t offset)
michael@0 374 {
michael@0 375 TokenStream *ts = &bce->parser->tokenStream;
michael@0 376 if (!ts->srcCoords.isOnThisLine(offset, bce->currentLine())) {
michael@0 377 unsigned line = ts->srcCoords.lineNum(offset);
michael@0 378 unsigned delta = line - bce->currentLine();
michael@0 379
michael@0 380 /*
michael@0 381 * Encode any change in the current source line number by using
michael@0 382 * either several SRC_NEWLINE notes or just one SRC_SETLINE note,
michael@0 383 * whichever consumes less space.
michael@0 384 *
michael@0 385 * NB: We handle backward line number deltas (possible with for
michael@0 386 * loops where the update part is emitted after the body, but its
michael@0 387 * line number is <= any line number in the body) here by letting
michael@0 388 * unsigned delta_ wrap to a very large number, which triggers a
michael@0 389 * SRC_SETLINE.
michael@0 390 */
michael@0 391 bce->current->currentLine = line;
michael@0 392 bce->current->lastColumn = 0;
michael@0 393 if (delta >= LengthOfSetLine(line)) {
michael@0 394 if (NewSrcNote2(cx, bce, SRC_SETLINE, (ptrdiff_t)line) < 0)
michael@0 395 return false;
michael@0 396 } else {
michael@0 397 do {
michael@0 398 if (NewSrcNote(cx, bce, SRC_NEWLINE) < 0)
michael@0 399 return false;
michael@0 400 } while (--delta != 0);
michael@0 401 }
michael@0 402 }
michael@0 403 return true;
michael@0 404 }
michael@0 405
michael@0 406 /* A function, so that we avoid macro-bloating all the other callsites. */
michael@0 407 static bool
michael@0 408 UpdateSourceCoordNotes(ExclusiveContext *cx, BytecodeEmitter *bce, uint32_t offset)
michael@0 409 {
michael@0 410 if (!UpdateLineNumberNotes(cx, bce, offset))
michael@0 411 return false;
michael@0 412
michael@0 413 uint32_t columnIndex = bce->parser->tokenStream.srcCoords.columnIndex(offset);
michael@0 414 ptrdiff_t colspan = ptrdiff_t(columnIndex) - ptrdiff_t(bce->current->lastColumn);
michael@0 415 if (colspan != 0) {
michael@0 416 if (colspan < 0) {
michael@0 417 colspan += SN_COLSPAN_DOMAIN;
michael@0 418 } else if (colspan >= SN_COLSPAN_DOMAIN / 2) {
michael@0 419 // If the column span is so large that we can't store it, then just
michael@0 420 // discard this information because column information would most
michael@0 421 // likely be useless anyway once the column numbers are ~4000000.
michael@0 422 // This has been known to happen with scripts that have been
michael@0 423 // minimized and put into all one line.
michael@0 424 return true;
michael@0 425 }
michael@0 426 if (NewSrcNote2(cx, bce, SRC_COLSPAN, colspan) < 0)
michael@0 427 return false;
michael@0 428 bce->current->lastColumn = columnIndex;
michael@0 429 }
michael@0 430 return true;
michael@0 431 }
michael@0 432
michael@0 433 static ptrdiff_t
michael@0 434 EmitLoopHead(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *nextpn)
michael@0 435 {
michael@0 436 if (nextpn) {
michael@0 437 /*
michael@0 438 * Try to give the JSOP_LOOPHEAD the same line number as the next
michael@0 439 * instruction. nextpn is often a block, in which case the next
michael@0 440 * instruction typically comes from the first statement inside.
michael@0 441 */
michael@0 442 JS_ASSERT_IF(nextpn->isKind(PNK_STATEMENTLIST), nextpn->isArity(PN_LIST));
michael@0 443 if (nextpn->isKind(PNK_STATEMENTLIST) && nextpn->pn_head)
michael@0 444 nextpn = nextpn->pn_head;
michael@0 445 if (!UpdateSourceCoordNotes(cx, bce, nextpn->pn_pos.begin))
michael@0 446 return -1;
michael@0 447 }
michael@0 448
michael@0 449 return Emit1(cx, bce, JSOP_LOOPHEAD);
michael@0 450 }
michael@0 451
michael@0 452 static bool
michael@0 453 EmitLoopEntry(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *nextpn)
michael@0 454 {
michael@0 455 if (nextpn) {
michael@0 456 /* Update the line number, as for LOOPHEAD. */
michael@0 457 JS_ASSERT_IF(nextpn->isKind(PNK_STATEMENTLIST), nextpn->isArity(PN_LIST));
michael@0 458 if (nextpn->isKind(PNK_STATEMENTLIST) && nextpn->pn_head)
michael@0 459 nextpn = nextpn->pn_head;
michael@0 460 if (!UpdateSourceCoordNotes(cx, bce, nextpn->pn_pos.begin))
michael@0 461 return false;
michael@0 462 }
michael@0 463
michael@0 464 LoopStmtInfo *loop = LoopStmtInfo::fromStmtInfo(bce->topStmt);
michael@0 465 JS_ASSERT(loop->loopDepth > 0);
michael@0 466
michael@0 467 uint8_t loopDepthAndFlags = PackLoopEntryDepthHintAndFlags(loop->loopDepth, loop->canIonOsr);
michael@0 468 return Emit2(cx, bce, JSOP_LOOPENTRY, loopDepthAndFlags) >= 0;
michael@0 469 }
michael@0 470
michael@0 471 /*
michael@0 472 * If op is JOF_TYPESET (see the type barriers comment in jsinfer.h), reserve
michael@0 473 * a type set to store its result.
michael@0 474 */
michael@0 475 static inline void
michael@0 476 CheckTypeSet(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op)
michael@0 477 {
michael@0 478 if (js_CodeSpec[op].format & JOF_TYPESET) {
michael@0 479 if (bce->typesetCount < UINT16_MAX)
michael@0 480 bce->typesetCount++;
michael@0 481 }
michael@0 482 }
michael@0 483
michael@0 484 /*
michael@0 485 * Macro to emit a bytecode followed by a uint16_t immediate operand stored in
michael@0 486 * big-endian order.
michael@0 487 *
michael@0 488 * NB: We use cx and bce from our caller's lexical environment, and return
michael@0 489 * false on error.
michael@0 490 */
michael@0 491 #define EMIT_UINT16_IMM_OP(op, i) \
michael@0 492 JS_BEGIN_MACRO \
michael@0 493 if (Emit3(cx, bce, op, UINT16_HI(i), UINT16_LO(i)) < 0) \
michael@0 494 return false; \
michael@0 495 CheckTypeSet(cx, bce, op); \
michael@0 496 JS_END_MACRO
michael@0 497
michael@0 498 static bool
michael@0 499 FlushPops(ExclusiveContext *cx, BytecodeEmitter *bce, int *npops)
michael@0 500 {
michael@0 501 JS_ASSERT(*npops != 0);
michael@0 502 EMIT_UINT16_IMM_OP(JSOP_POPN, *npops);
michael@0 503 *npops = 0;
michael@0 504 return true;
michael@0 505 }
michael@0 506
michael@0 507 static bool
michael@0 508 PopIterator(ExclusiveContext *cx, BytecodeEmitter *bce)
michael@0 509 {
michael@0 510 if (Emit1(cx, bce, JSOP_ENDITER) < 0)
michael@0 511 return false;
michael@0 512 return true;
michael@0 513 }
michael@0 514
michael@0 515 namespace {
michael@0 516
michael@0 517 class NonLocalExitScope {
michael@0 518 ExclusiveContext *cx;
michael@0 519 BytecodeEmitter *bce;
michael@0 520 const uint32_t savedScopeIndex;
michael@0 521 const int savedDepth;
michael@0 522 uint32_t openScopeIndex;
michael@0 523
michael@0 524 NonLocalExitScope(const NonLocalExitScope &) MOZ_DELETE;
michael@0 525
michael@0 526 public:
michael@0 527 explicit NonLocalExitScope(ExclusiveContext *cx_, BytecodeEmitter *bce_)
michael@0 528 : cx(cx_),
michael@0 529 bce(bce_),
michael@0 530 savedScopeIndex(bce->blockScopeList.length()),
michael@0 531 savedDepth(bce->stackDepth),
michael@0 532 openScopeIndex(UINT32_MAX) {
michael@0 533 if (bce->staticScope) {
michael@0 534 StmtInfoBCE *stmt = bce->topStmt;
michael@0 535 while (1) {
michael@0 536 JS_ASSERT(stmt);
michael@0 537 if (stmt->isNestedScope) {
michael@0 538 openScopeIndex = stmt->blockScopeIndex;
michael@0 539 break;
michael@0 540 }
michael@0 541 stmt = stmt->down;
michael@0 542 }
michael@0 543 }
michael@0 544 }
michael@0 545
michael@0 546 ~NonLocalExitScope() {
michael@0 547 for (uint32_t n = savedScopeIndex; n < bce->blockScopeList.length(); n++)
michael@0 548 bce->blockScopeList.recordEnd(n, bce->offset());
michael@0 549 bce->stackDepth = savedDepth;
michael@0 550 }
michael@0 551
michael@0 552 bool popScopeForNonLocalExit(uint32_t blockScopeIndex) {
michael@0 553 uint32_t scopeObjectIndex = bce->blockScopeList.findEnclosingScope(blockScopeIndex);
michael@0 554 uint32_t parent = openScopeIndex;
michael@0 555
michael@0 556 if (!bce->blockScopeList.append(scopeObjectIndex, bce->offset(), parent))
michael@0 557 return false;
michael@0 558 openScopeIndex = bce->blockScopeList.length() - 1;
michael@0 559 return true;
michael@0 560 }
michael@0 561
michael@0 562 bool prepareForNonLocalJump(StmtInfoBCE *toStmt);
michael@0 563 };
michael@0 564
michael@0 565 /*
michael@0 566 * Emit additional bytecode(s) for non-local jumps.
michael@0 567 */
michael@0 568 bool
michael@0 569 NonLocalExitScope::prepareForNonLocalJump(StmtInfoBCE *toStmt)
michael@0 570 {
michael@0 571 int npops = 0;
michael@0 572
michael@0 573 #define FLUSH_POPS() if (npops && !FlushPops(cx, bce, &npops)) return false
michael@0 574
michael@0 575 for (StmtInfoBCE *stmt = bce->topStmt; stmt != toStmt; stmt = stmt->down) {
michael@0 576 switch (stmt->type) {
michael@0 577 case STMT_FINALLY:
michael@0 578 FLUSH_POPS();
michael@0 579 if (EmitBackPatchOp(cx, bce, &stmt->gosubs()) < 0)
michael@0 580 return false;
michael@0 581 break;
michael@0 582
michael@0 583 case STMT_WITH:
michael@0 584 if (Emit1(cx, bce, JSOP_LEAVEWITH) < 0)
michael@0 585 return false;
michael@0 586 JS_ASSERT(stmt->isNestedScope);
michael@0 587 if (!popScopeForNonLocalExit(stmt->blockScopeIndex))
michael@0 588 return false;
michael@0 589 break;
michael@0 590
michael@0 591 case STMT_FOR_OF_LOOP:
michael@0 592 npops += 2;
michael@0 593 break;
michael@0 594
michael@0 595 case STMT_FOR_IN_LOOP:
michael@0 596 FLUSH_POPS();
michael@0 597 if (!PopIterator(cx, bce))
michael@0 598 return false;
michael@0 599 break;
michael@0 600
michael@0 601 case STMT_SUBROUTINE:
michael@0 602 /*
michael@0 603 * There's a [exception or hole, retsub pc-index] pair on the
michael@0 604 * stack that we need to pop.
michael@0 605 */
michael@0 606 npops += 2;
michael@0 607 break;
michael@0 608
michael@0 609 default:;
michael@0 610 }
michael@0 611
michael@0 612 if (stmt->isBlockScope) {
michael@0 613 JS_ASSERT(stmt->isNestedScope);
michael@0 614 StaticBlockObject &blockObj = stmt->staticBlock();
michael@0 615 if (Emit1(cx, bce, JSOP_DEBUGLEAVEBLOCK) < 0)
michael@0 616 return false;
michael@0 617 if (!popScopeForNonLocalExit(stmt->blockScopeIndex))
michael@0 618 return false;
michael@0 619 if (blockObj.needsClone()) {
michael@0 620 if (Emit1(cx, bce, JSOP_POPBLOCKSCOPE) < 0)
michael@0 621 return false;
michael@0 622 }
michael@0 623 }
michael@0 624 }
michael@0 625
michael@0 626 FLUSH_POPS();
michael@0 627 return true;
michael@0 628
michael@0 629 #undef FLUSH_POPS
michael@0 630 }
michael@0 631
michael@0 632 } // anonymous namespace
michael@0 633
michael@0 634 static ptrdiff_t
michael@0 635 EmitGoto(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *toStmt, ptrdiff_t *lastp,
michael@0 636 SrcNoteType noteType = SRC_NULL)
michael@0 637 {
michael@0 638 NonLocalExitScope nle(cx, bce);
michael@0 639
michael@0 640 if (!nle.prepareForNonLocalJump(toStmt))
michael@0 641 return -1;
michael@0 642
michael@0 643 if (noteType != SRC_NULL) {
michael@0 644 if (NewSrcNote(cx, bce, noteType) < 0)
michael@0 645 return -1;
michael@0 646 }
michael@0 647
michael@0 648 return EmitBackPatchOp(cx, bce, lastp);
michael@0 649 }
michael@0 650
michael@0 651 static bool
michael@0 652 BackPatch(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t last, jsbytecode *target, jsbytecode op)
michael@0 653 {
michael@0 654 jsbytecode *pc, *stop;
michael@0 655 ptrdiff_t delta, span;
michael@0 656
michael@0 657 pc = bce->code(last);
michael@0 658 stop = bce->code(-1);
michael@0 659 while (pc != stop) {
michael@0 660 delta = GET_JUMP_OFFSET(pc);
michael@0 661 span = target - pc;
michael@0 662 SET_JUMP_OFFSET(pc, span);
michael@0 663 *pc = op;
michael@0 664 pc -= delta;
michael@0 665 }
michael@0 666 return true;
michael@0 667 }
michael@0 668
michael@0 669 #define SET_STATEMENT_TOP(stmt, top) \
michael@0 670 ((stmt)->update = (top), (stmt)->breaks = (stmt)->continues = (-1))
michael@0 671
michael@0 672 static void
michael@0 673 PushStatementInner(BytecodeEmitter *bce, StmtInfoBCE *stmt, StmtType type, ptrdiff_t top)
michael@0 674 {
michael@0 675 SET_STATEMENT_TOP(stmt, top);
michael@0 676 PushStatement(bce, stmt, type);
michael@0 677 }
michael@0 678
michael@0 679 static void
michael@0 680 PushStatementBCE(BytecodeEmitter *bce, StmtInfoBCE *stmt, StmtType type, ptrdiff_t top)
michael@0 681 {
michael@0 682 PushStatementInner(bce, stmt, type, top);
michael@0 683 JS_ASSERT(!stmt->isLoop());
michael@0 684 }
michael@0 685
michael@0 686 static void
michael@0 687 PushLoopStatement(BytecodeEmitter *bce, LoopStmtInfo *stmt, StmtType type, ptrdiff_t top)
michael@0 688 {
michael@0 689 PushStatementInner(bce, stmt, type, top);
michael@0 690 JS_ASSERT(stmt->isLoop());
michael@0 691
michael@0 692 LoopStmtInfo *downLoop = nullptr;
michael@0 693 for (StmtInfoBCE *outer = stmt->down; outer; outer = outer->down) {
michael@0 694 if (outer->isLoop()) {
michael@0 695 downLoop = LoopStmtInfo::fromStmtInfo(outer);
michael@0 696 break;
michael@0 697 }
michael@0 698 }
michael@0 699
michael@0 700 stmt->stackDepth = bce->stackDepth;
michael@0 701 stmt->loopDepth = downLoop ? downLoop->loopDepth + 1 : 1;
michael@0 702
michael@0 703 int loopSlots;
michael@0 704 if (type == STMT_FOR_OF_LOOP)
michael@0 705 loopSlots = 2;
michael@0 706 else if (type == STMT_FOR_IN_LOOP)
michael@0 707 loopSlots = 1;
michael@0 708 else
michael@0 709 loopSlots = 0;
michael@0 710
michael@0 711 if (downLoop)
michael@0 712 stmt->canIonOsr = (downLoop->canIonOsr &&
michael@0 713 stmt->stackDepth == downLoop->stackDepth + loopSlots);
michael@0 714 else
michael@0 715 stmt->canIonOsr = stmt->stackDepth == loopSlots;
michael@0 716 }
michael@0 717
michael@0 718 /*
michael@0 719 * Return the enclosing lexical scope, which is the innermost enclosing static
michael@0 720 * block object or compiler created function.
michael@0 721 */
michael@0 722 static JSObject *
michael@0 723 EnclosingStaticScope(BytecodeEmitter *bce)
michael@0 724 {
michael@0 725 if (bce->staticScope)
michael@0 726 return bce->staticScope;
michael@0 727
michael@0 728 if (!bce->sc->isFunctionBox()) {
michael@0 729 JS_ASSERT(!bce->parent);
michael@0 730 return nullptr;
michael@0 731 }
michael@0 732
michael@0 733 return bce->sc->asFunctionBox()->function();
michael@0 734 }
michael@0 735
michael@0 736 #ifdef DEBUG
michael@0 737 static bool
michael@0 738 AllLocalsAliased(StaticBlockObject &obj)
michael@0 739 {
michael@0 740 for (unsigned i = 0; i < obj.numVariables(); i++)
michael@0 741 if (!obj.isAliased(i))
michael@0 742 return false;
michael@0 743 return true;
michael@0 744 }
michael@0 745 #endif
michael@0 746
michael@0 747 static bool
michael@0 748 ComputeAliasedSlots(ExclusiveContext *cx, BytecodeEmitter *bce, Handle<StaticBlockObject *> blockObj)
michael@0 749 {
michael@0 750 for (unsigned i = 0; i < blockObj->numVariables(); i++) {
michael@0 751 Definition *dn = blockObj->definitionParseNode(i);
michael@0 752
michael@0 753 JS_ASSERT(dn->isDefn());
michael@0 754 if (!dn->pn_cookie.set(bce->parser->tokenStream, dn->pn_cookie.level(),
michael@0 755 blockObj->blockIndexToLocalIndex(dn->frameSlot())))
michael@0 756 {
michael@0 757 return false;
michael@0 758 }
michael@0 759
michael@0 760 #ifdef DEBUG
michael@0 761 for (ParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
michael@0 762 JS_ASSERT(pnu->pn_lexdef == dn);
michael@0 763 JS_ASSERT(!(pnu->pn_dflags & PND_BOUND));
michael@0 764 JS_ASSERT(pnu->pn_cookie.isFree());
michael@0 765 }
michael@0 766 #endif
michael@0 767
michael@0 768 blockObj->setAliased(i, bce->isAliasedName(dn));
michael@0 769 }
michael@0 770
michael@0 771 JS_ASSERT_IF(bce->sc->allLocalsAliased(), AllLocalsAliased(*blockObj));
michael@0 772
michael@0 773 return true;
michael@0 774 }
michael@0 775
michael@0 776 static bool
michael@0 777 EmitInternedObjectOp(ExclusiveContext *cx, uint32_t index, JSOp op, BytecodeEmitter *bce);
michael@0 778
michael@0 779 // In a function, block-scoped locals go after the vars, and form part of the
michael@0 780 // fixed part of a stack frame. Outside a function, there are no fixed vars,
michael@0 781 // but block-scoped locals still form part of the fixed part of a stack frame
michael@0 782 // and are thus addressable via GETLOCAL and friends.
michael@0 783 static void
michael@0 784 ComputeLocalOffset(ExclusiveContext *cx, BytecodeEmitter *bce, Handle<StaticBlockObject *> blockObj)
michael@0 785 {
michael@0 786 unsigned nfixedvars = bce->sc->isFunctionBox() ? bce->script->bindings.numVars() : 0;
michael@0 787 unsigned localOffset = nfixedvars;
michael@0 788
michael@0 789 if (bce->staticScope) {
michael@0 790 Rooted<NestedScopeObject *> outer(cx, bce->staticScope);
michael@0 791 for (; outer; outer = outer->enclosingNestedScope()) {
michael@0 792 if (outer->is<StaticBlockObject>()) {
michael@0 793 StaticBlockObject &outerBlock = outer->as<StaticBlockObject>();
michael@0 794 localOffset = outerBlock.localOffset() + outerBlock.numVariables();
michael@0 795 break;
michael@0 796 }
michael@0 797 }
michael@0 798 }
michael@0 799
michael@0 800 JS_ASSERT(localOffset + blockObj->numVariables()
michael@0 801 <= nfixedvars + bce->script->bindings.numBlockScoped());
michael@0 802
michael@0 803 blockObj->setLocalOffset(localOffset);
michael@0 804 }
michael@0 805
michael@0 806 // ~ Nested Scopes ~
michael@0 807 //
michael@0 808 // A nested scope is a region of a compilation unit (function, script, or eval
michael@0 809 // code) with an additional node on the scope chain. This node may either be a
michael@0 810 // "with" object or a "block" object. "With" objects represent "with" scopes.
michael@0 811 // Block objects represent lexical scopes, and contain named block-scoped
michael@0 812 // bindings, for example "let" bindings or the exception in a catch block.
michael@0 813 // Those variables may be local and thus accessible directly from the stack, or
michael@0 814 // "aliased" (accessed by name from nested functions, or dynamically via nested
michael@0 815 // "eval" or "with") and only accessible through the scope chain.
michael@0 816 //
michael@0 817 // All nested scopes are present on the "static scope chain". A nested scope
michael@0 818 // that is a "with" scope will be present on the scope chain at run-time as
michael@0 819 // well. A block scope may or may not have a corresponding link on the run-time
michael@0 820 // scope chain; if no variable declared in the block scope is "aliased", then no
michael@0 821 // scope chain node is allocated.
michael@0 822 //
michael@0 823 // To help debuggers, the bytecode emitter arranges to record the PC ranges
michael@0 824 // comprehended by a nested scope, and ultimately attach them to the JSScript.
michael@0 825 // An element in the "block scope array" specifies the PC range, and links to a
michael@0 826 // NestedScopeObject in the object list of the script. That scope object is
michael@0 827 // linked to the previous link in the static scope chain, if any. The static
michael@0 828 // scope chain at any pre-retire PC can be retrieved using
michael@0 829 // JSScript::getStaticScope(jsbytecode *pc).
michael@0 830 //
michael@0 831 // Block scopes store their locals in the fixed part of a stack frame, after the
michael@0 832 // "fixed var" bindings. A fixed var binding is a "var" or legacy "const"
michael@0 833 // binding that occurs in a function (as opposed to a script or in eval code).
michael@0 834 // Only functions have fixed var bindings.
michael@0 835 //
michael@0 836 // To assist the debugger, we emit a DEBUGLEAVEBLOCK opcode before leaving a
michael@0 837 // block scope, even if the block has no aliased locals. This allows
michael@0 838 // DebugScopes to invalidate any association between a debugger scope object,
michael@0 839 // which can proxy access to unaliased stack locals, and the actual live frame.
michael@0 840 // In normal, non-debug mode, this opcode does not cause any baseline code to be
michael@0 841 // emitted.
michael@0 842 //
michael@0 843 // Enter a nested scope with EnterNestedScope. It will emit
michael@0 844 // PUSHBLOCKSCOPE/ENTERWITH if needed, and arrange to record the PC bounds of
michael@0 845 // the scope. Leave a nested scope with LeaveNestedScope, which, for blocks,
michael@0 846 // will emit DEBUGLEAVEBLOCK and may emit POPBLOCKSCOPE. (For "with" scopes it
michael@0 847 // emits LEAVEWITH, of course.) Pass EnterNestedScope a fresh StmtInfoBCE
michael@0 848 // object, and pass that same object to the corresponding LeaveNestedScope. If
michael@0 849 // the statement is a block scope, pass STMT_BLOCK as stmtType; otherwise for
michael@0 850 // with scopes pass STMT_WITH.
michael@0 851 //
michael@0 852 static bool
michael@0 853 EnterNestedScope(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *stmt, ObjectBox *objbox,
michael@0 854 StmtType stmtType)
michael@0 855 {
michael@0 856 Rooted<NestedScopeObject *> scopeObj(cx, &objbox->object->as<NestedScopeObject>());
michael@0 857 uint32_t scopeObjectIndex = bce->objectList.add(objbox);
michael@0 858
michael@0 859 switch (stmtType) {
michael@0 860 case STMT_BLOCK: {
michael@0 861 Rooted<StaticBlockObject *> blockObj(cx, &scopeObj->as<StaticBlockObject>());
michael@0 862
michael@0 863 ComputeLocalOffset(cx, bce, blockObj);
michael@0 864
michael@0 865 if (!ComputeAliasedSlots(cx, bce, blockObj))
michael@0 866 return false;
michael@0 867
michael@0 868 if (blockObj->needsClone()) {
michael@0 869 if (!EmitInternedObjectOp(cx, scopeObjectIndex, JSOP_PUSHBLOCKSCOPE, bce))
michael@0 870 return false;
michael@0 871 }
michael@0 872 break;
michael@0 873 }
michael@0 874 case STMT_WITH:
michael@0 875 JS_ASSERT(scopeObj->is<StaticWithObject>());
michael@0 876 if (!EmitInternedObjectOp(cx, scopeObjectIndex, JSOP_ENTERWITH, bce))
michael@0 877 return false;
michael@0 878 break;
michael@0 879 default:
michael@0 880 MOZ_ASSUME_UNREACHABLE();
michael@0 881 }
michael@0 882
michael@0 883 uint32_t parent = BlockScopeNote::NoBlockScopeIndex;
michael@0 884 if (StmtInfoBCE *stmt = bce->topScopeStmt) {
michael@0 885 for (; stmt->staticScope != bce->staticScope; stmt = stmt->down) {}
michael@0 886 parent = stmt->blockScopeIndex;
michael@0 887 }
michael@0 888
michael@0 889 stmt->blockScopeIndex = bce->blockScopeList.length();
michael@0 890 if (!bce->blockScopeList.append(scopeObjectIndex, bce->offset(), parent))
michael@0 891 return false;
michael@0 892
michael@0 893 PushStatementBCE(bce, stmt, stmtType, bce->offset());
michael@0 894 scopeObj->initEnclosingNestedScope(EnclosingStaticScope(bce));
michael@0 895 FinishPushNestedScope(bce, stmt, *scopeObj);
michael@0 896 JS_ASSERT(stmt->isNestedScope);
michael@0 897 stmt->isBlockScope = (stmtType == STMT_BLOCK);
michael@0 898
michael@0 899 return true;
michael@0 900 }
michael@0 901
michael@0 902 // Patches |breaks| and |continues| unless the top statement info record
michael@0 903 // represents a try-catch-finally suite. May fail if a jump offset overflows.
michael@0 904 static bool
michael@0 905 PopStatementBCE(ExclusiveContext *cx, BytecodeEmitter *bce)
michael@0 906 {
michael@0 907 StmtInfoBCE *stmt = bce->topStmt;
michael@0 908 if (!stmt->isTrying() &&
michael@0 909 (!BackPatch(cx, bce, stmt->breaks, bce->code().end(), JSOP_GOTO) ||
michael@0 910 !BackPatch(cx, bce, stmt->continues, bce->code(stmt->update), JSOP_GOTO)))
michael@0 911 {
michael@0 912 return false;
michael@0 913 }
michael@0 914
michael@0 915 FinishPopStatement(bce);
michael@0 916 return true;
michael@0 917 }
michael@0 918
michael@0 919 static bool
michael@0 920 LeaveNestedScope(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *stmt)
michael@0 921 {
michael@0 922 JS_ASSERT(stmt == bce->topStmt);
michael@0 923 JS_ASSERT(stmt->isNestedScope);
michael@0 924 JS_ASSERT(stmt->isBlockScope == !(stmt->type == STMT_WITH));
michael@0 925 uint32_t blockScopeIndex = stmt->blockScopeIndex;
michael@0 926
michael@0 927 #ifdef DEBUG
michael@0 928 JS_ASSERT(bce->blockScopeList.list[blockScopeIndex].length == 0);
michael@0 929 uint32_t blockObjIndex = bce->blockScopeList.list[blockScopeIndex].index;
michael@0 930 ObjectBox *blockObjBox = bce->objectList.find(blockObjIndex);
michael@0 931 NestedScopeObject *staticScope = &blockObjBox->object->as<NestedScopeObject>();
michael@0 932 JS_ASSERT(stmt->staticScope == staticScope);
michael@0 933 JS_ASSERT(staticScope == bce->staticScope);
michael@0 934 JS_ASSERT_IF(!stmt->isBlockScope, staticScope->is<StaticWithObject>());
michael@0 935 #endif
michael@0 936
michael@0 937 if (!PopStatementBCE(cx, bce))
michael@0 938 return false;
michael@0 939
michael@0 940 if (Emit1(cx, bce, stmt->isBlockScope ? JSOP_DEBUGLEAVEBLOCK : JSOP_LEAVEWITH) < 0)
michael@0 941 return false;
michael@0 942
michael@0 943 bce->blockScopeList.recordEnd(blockScopeIndex, bce->offset());
michael@0 944
michael@0 945 if (stmt->isBlockScope && stmt->staticScope->as<StaticBlockObject>().needsClone()) {
michael@0 946 if (Emit1(cx, bce, JSOP_POPBLOCKSCOPE) < 0)
michael@0 947 return false;
michael@0 948 }
michael@0 949
michael@0 950 return true;
michael@0 951 }
michael@0 952
michael@0 953 static bool
michael@0 954 EmitIndex32(ExclusiveContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce)
michael@0 955 {
michael@0 956 const size_t len = 1 + UINT32_INDEX_LEN;
michael@0 957 JS_ASSERT(len == size_t(js_CodeSpec[op].length));
michael@0 958 ptrdiff_t offset = EmitCheck(cx, bce, len);
michael@0 959 if (offset < 0)
michael@0 960 return false;
michael@0 961
michael@0 962 jsbytecode *code = bce->code(offset);
michael@0 963 code[0] = jsbytecode(op);
michael@0 964 SET_UINT32_INDEX(code, index);
michael@0 965 UpdateDepth(cx, bce, offset);
michael@0 966 CheckTypeSet(cx, bce, op);
michael@0 967 return true;
michael@0 968 }
michael@0 969
michael@0 970 static bool
michael@0 971 EmitIndexOp(ExclusiveContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce)
michael@0 972 {
michael@0 973 const size_t len = js_CodeSpec[op].length;
michael@0 974 JS_ASSERT(len >= 1 + UINT32_INDEX_LEN);
michael@0 975 ptrdiff_t offset = EmitCheck(cx, bce, len);
michael@0 976 if (offset < 0)
michael@0 977 return false;
michael@0 978
michael@0 979 jsbytecode *code = bce->code(offset);
michael@0 980 code[0] = jsbytecode(op);
michael@0 981 SET_UINT32_INDEX(code, index);
michael@0 982 UpdateDepth(cx, bce, offset);
michael@0 983 CheckTypeSet(cx, bce, op);
michael@0 984 return true;
michael@0 985 }
michael@0 986
michael@0 987 static bool
michael@0 988 EmitAtomOp(ExclusiveContext *cx, JSAtom *atom, JSOp op, BytecodeEmitter *bce)
michael@0 989 {
michael@0 990 JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
michael@0 991
michael@0 992 if (op == JSOP_GETPROP && atom == cx->names().length) {
michael@0 993 /* Specialize length accesses for the interpreter. */
michael@0 994 op = JSOP_LENGTH;
michael@0 995 }
michael@0 996
michael@0 997 jsatomid index;
michael@0 998 if (!bce->makeAtomIndex(atom, &index))
michael@0 999 return false;
michael@0 1000
michael@0 1001 return EmitIndexOp(cx, op, index, bce);
michael@0 1002 }
michael@0 1003
michael@0 1004 static bool
michael@0 1005 EmitAtomOp(ExclusiveContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
michael@0 1006 {
michael@0 1007 JS_ASSERT(pn->pn_atom != nullptr);
michael@0 1008 return EmitAtomOp(cx, pn->pn_atom, op, bce);
michael@0 1009 }
michael@0 1010
michael@0 1011 static bool
michael@0 1012 EmitInternedObjectOp(ExclusiveContext *cx, uint32_t index, JSOp op, BytecodeEmitter *bce)
michael@0 1013 {
michael@0 1014 JS_ASSERT(JOF_OPTYPE(op) == JOF_OBJECT);
michael@0 1015 JS_ASSERT(index < bce->objectList.length);
michael@0 1016 return EmitIndex32(cx, op, index, bce);
michael@0 1017 }
michael@0 1018
michael@0 1019 static bool
michael@0 1020 EmitObjectOp(ExclusiveContext *cx, ObjectBox *objbox, JSOp op, BytecodeEmitter *bce)
michael@0 1021 {
michael@0 1022 return EmitInternedObjectOp(cx, bce->objectList.add(objbox), op, bce);
michael@0 1023 }
michael@0 1024
michael@0 1025 static bool
michael@0 1026 EmitRegExp(ExclusiveContext *cx, uint32_t index, BytecodeEmitter *bce)
michael@0 1027 {
michael@0 1028 return EmitIndex32(cx, JSOP_REGEXP, index, bce);
michael@0 1029 }
michael@0 1030
michael@0 1031 /*
michael@0 1032 * To catch accidental misuse, EMIT_UINT16_IMM_OP/Emit3 assert that they are
michael@0 1033 * not used to unconditionally emit JSOP_GETLOCAL. Variable access should
michael@0 1034 * instead be emitted using EmitVarOp. In special cases, when the caller
michael@0 1035 * definitely knows that a given local slot is unaliased, this function may be
michael@0 1036 * used as a non-asserting version of EMIT_UINT16_IMM_OP.
michael@0 1037 */
michael@0 1038 static bool
michael@0 1039 EmitUnaliasedVarOp(ExclusiveContext *cx, JSOp op, uint32_t slot, BytecodeEmitter *bce)
michael@0 1040 {
michael@0 1041 JS_ASSERT(JOF_OPTYPE(op) != JOF_SCOPECOORD);
michael@0 1042
michael@0 1043 if (IsLocalOp(op)) {
michael@0 1044 ptrdiff_t off = EmitN(cx, bce, op, LOCALNO_LEN);
michael@0 1045 if (off < 0)
michael@0 1046 return false;
michael@0 1047
michael@0 1048 SET_LOCALNO(bce->code(off), slot);
michael@0 1049 return true;
michael@0 1050 }
michael@0 1051
michael@0 1052 JS_ASSERT(IsArgOp(op));
michael@0 1053 ptrdiff_t off = EmitN(cx, bce, op, ARGNO_LEN);
michael@0 1054 if (off < 0)
michael@0 1055 return false;
michael@0 1056
michael@0 1057 SET_ARGNO(bce->code(off), slot);
michael@0 1058 return true;
michael@0 1059 }
michael@0 1060
michael@0 1061 static bool
michael@0 1062 EmitAliasedVarOp(ExclusiveContext *cx, JSOp op, ScopeCoordinate sc, BytecodeEmitter *bce)
michael@0 1063 {
michael@0 1064 JS_ASSERT(JOF_OPTYPE(op) == JOF_SCOPECOORD);
michael@0 1065
michael@0 1066 unsigned n = SCOPECOORD_HOPS_LEN + SCOPECOORD_SLOT_LEN;
michael@0 1067 JS_ASSERT(int(n) + 1 /* op */ == js_CodeSpec[op].length);
michael@0 1068
michael@0 1069 ptrdiff_t off = EmitN(cx, bce, op, n);
michael@0 1070 if (off < 0)
michael@0 1071 return false;
michael@0 1072
michael@0 1073 jsbytecode *pc = bce->code(off);
michael@0 1074 SET_SCOPECOORD_HOPS(pc, sc.hops());
michael@0 1075 pc += SCOPECOORD_HOPS_LEN;
michael@0 1076 SET_SCOPECOORD_SLOT(pc, sc.slot());
michael@0 1077 pc += SCOPECOORD_SLOT_LEN;
michael@0 1078 CheckTypeSet(cx, bce, op);
michael@0 1079 return true;
michael@0 1080 }
michael@0 1081
michael@0 1082 // Compute the number of nested scope objects that will actually be on the scope
michael@0 1083 // chain at runtime, given the BCE's current staticScope.
michael@0 1084 static unsigned
michael@0 1085 DynamicNestedScopeDepth(BytecodeEmitter *bce)
michael@0 1086 {
michael@0 1087 unsigned depth = 0;
michael@0 1088 for (NestedScopeObject *b = bce->staticScope; b; b = b->enclosingNestedScope()) {
michael@0 1089 if (!b->is<StaticBlockObject>() || b->as<StaticBlockObject>().needsClone())
michael@0 1090 ++depth;
michael@0 1091 }
michael@0 1092
michael@0 1093 return depth;
michael@0 1094 }
michael@0 1095
michael@0 1096 static bool
michael@0 1097 LookupAliasedName(HandleScript script, PropertyName *name, uint32_t *pslot)
michael@0 1098 {
michael@0 1099 /*
michael@0 1100 * Beware: BindingIter may contain more than one Binding for a given name
michael@0 1101 * (in the case of |function f(x,x) {}|) but only one will be aliased.
michael@0 1102 */
michael@0 1103 uint32_t slot = CallObject::RESERVED_SLOTS;
michael@0 1104 for (BindingIter bi(script); !bi.done(); bi++) {
michael@0 1105 if (bi->aliased()) {
michael@0 1106 if (bi->name() == name) {
michael@0 1107 *pslot = slot;
michael@0 1108 return true;
michael@0 1109 }
michael@0 1110 slot++;
michael@0 1111 }
michael@0 1112 }
michael@0 1113 return false;
michael@0 1114 }
michael@0 1115
michael@0 1116 static bool
michael@0 1117 LookupAliasedNameSlot(HandleScript script, PropertyName *name, ScopeCoordinate *sc)
michael@0 1118 {
michael@0 1119 uint32_t slot;
michael@0 1120 if (!LookupAliasedName(script, name, &slot))
michael@0 1121 return false;
michael@0 1122
michael@0 1123 sc->setSlot(slot);
michael@0 1124 return true;
michael@0 1125 }
michael@0 1126
michael@0 1127 /*
michael@0 1128 * Use this function instead of assigning directly to 'hops' to guard for
michael@0 1129 * uint8_t overflows.
michael@0 1130 */
michael@0 1131 static bool
michael@0 1132 AssignHops(BytecodeEmitter *bce, ParseNode *pn, unsigned src, ScopeCoordinate *dst)
michael@0 1133 {
michael@0 1134 if (src > UINT8_MAX) {
michael@0 1135 bce->reportError(pn, JSMSG_TOO_DEEP, js_function_str);
michael@0 1136 return false;
michael@0 1137 }
michael@0 1138
michael@0 1139 dst->setHops(src);
michael@0 1140 return true;
michael@0 1141 }
michael@0 1142
michael@0 1143 static bool
michael@0 1144 EmitAliasedVarOp(ExclusiveContext *cx, JSOp op, ParseNode *pn, BytecodeEmitter *bce)
michael@0 1145 {
michael@0 1146 /*
michael@0 1147 * While pn->pn_cookie tells us how many function scopes are between the use and the def this
michael@0 1148 * is not the same as how many hops up the dynamic scope chain are needed. In particular:
michael@0 1149 * - a lexical function scope only contributes a hop if it is "heavyweight" (has a dynamic
michael@0 1150 * scope object).
michael@0 1151 * - a heavyweight named function scope contributes an extra scope to the scope chain (a
michael@0 1152 * DeclEnvObject that holds just the name).
michael@0 1153 * - all the intervening let/catch blocks must be counted.
michael@0 1154 */
michael@0 1155 unsigned skippedScopes = 0;
michael@0 1156 BytecodeEmitter *bceOfDef = bce;
michael@0 1157 if (pn->isUsed()) {
michael@0 1158 /*
michael@0 1159 * As explained in BindNameToSlot, the 'level' of a use indicates how
michael@0 1160 * many function scopes (i.e., BytecodeEmitters) to skip to find the
michael@0 1161 * enclosing function scope of the definition being accessed.
michael@0 1162 */
michael@0 1163 for (unsigned i = pn->pn_cookie.level(); i; i--) {
michael@0 1164 skippedScopes += DynamicNestedScopeDepth(bceOfDef);
michael@0 1165 FunctionBox *funbox = bceOfDef->sc->asFunctionBox();
michael@0 1166 if (funbox->isHeavyweight()) {
michael@0 1167 skippedScopes++;
michael@0 1168 if (funbox->function()->isNamedLambda())
michael@0 1169 skippedScopes++;
michael@0 1170 }
michael@0 1171 bceOfDef = bceOfDef->parent;
michael@0 1172 }
michael@0 1173 } else {
michael@0 1174 JS_ASSERT(pn->isDefn());
michael@0 1175 JS_ASSERT(pn->pn_cookie.level() == bce->script->staticLevel());
michael@0 1176 }
michael@0 1177
michael@0 1178 /*
michael@0 1179 * The final part of the skippedScopes computation depends on the type of
michael@0 1180 * variable. An arg or local variable is at the outer scope of a function
michael@0 1181 * and so includes the full DynamicNestedScopeDepth. A let/catch-binding
michael@0 1182 * requires a search of the block chain to see how many (dynamic) block
michael@0 1183 * objects to skip.
michael@0 1184 */
michael@0 1185 ScopeCoordinate sc;
michael@0 1186 if (IsArgOp(pn->getOp())) {
michael@0 1187 if (!AssignHops(bce, pn, skippedScopes + DynamicNestedScopeDepth(bceOfDef), &sc))
michael@0 1188 return false;
michael@0 1189 JS_ALWAYS_TRUE(LookupAliasedNameSlot(bceOfDef->script, pn->name(), &sc));
michael@0 1190 } else {
michael@0 1191 JS_ASSERT(IsLocalOp(pn->getOp()) || pn->isKind(PNK_FUNCTION));
michael@0 1192 uint32_t local = pn->pn_cookie.slot();
michael@0 1193 if (local < bceOfDef->script->bindings.numVars()) {
michael@0 1194 if (!AssignHops(bce, pn, skippedScopes + DynamicNestedScopeDepth(bceOfDef), &sc))
michael@0 1195 return false;
michael@0 1196 JS_ALWAYS_TRUE(LookupAliasedNameSlot(bceOfDef->script, pn->name(), &sc));
michael@0 1197 } else {
michael@0 1198 JS_ASSERT_IF(bce->sc->isFunctionBox(), local <= bceOfDef->script->bindings.numLocals());
michael@0 1199 JS_ASSERT(bceOfDef->staticScope->is<StaticBlockObject>());
michael@0 1200 Rooted<StaticBlockObject*> b(cx, &bceOfDef->staticScope->as<StaticBlockObject>());
michael@0 1201 while (local < b->localOffset()) {
michael@0 1202 if (b->needsClone())
michael@0 1203 skippedScopes++;
michael@0 1204 b = &b->enclosingNestedScope()->as<StaticBlockObject>();
michael@0 1205 }
michael@0 1206 if (!AssignHops(bce, pn, skippedScopes, &sc))
michael@0 1207 return false;
michael@0 1208 sc.setSlot(b->localIndexToSlot(local));
michael@0 1209 }
michael@0 1210 }
michael@0 1211
michael@0 1212 return EmitAliasedVarOp(cx, op, sc, bce);
michael@0 1213 }
michael@0 1214
michael@0 1215 static bool
michael@0 1216 EmitVarOp(ExclusiveContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
michael@0 1217 {
michael@0 1218 JS_ASSERT(pn->isKind(PNK_FUNCTION) || pn->isKind(PNK_NAME));
michael@0 1219 JS_ASSERT(!pn->pn_cookie.isFree());
michael@0 1220
michael@0 1221 if (IsAliasedVarOp(op)) {
michael@0 1222 ScopeCoordinate sc;
michael@0 1223 sc.setHops(pn->pn_cookie.level());
michael@0 1224 sc.setSlot(pn->pn_cookie.slot());
michael@0 1225 return EmitAliasedVarOp(cx, op, sc, bce);
michael@0 1226 }
michael@0 1227
michael@0 1228 JS_ASSERT_IF(pn->isKind(PNK_NAME), IsArgOp(op) || IsLocalOp(op));
michael@0 1229
michael@0 1230 if (!bce->isAliasedName(pn)) {
michael@0 1231 JS_ASSERT(pn->isUsed() || pn->isDefn());
michael@0 1232 JS_ASSERT_IF(pn->isUsed(), pn->pn_cookie.level() == 0);
michael@0 1233 JS_ASSERT_IF(pn->isDefn(), pn->pn_cookie.level() == bce->script->staticLevel());
michael@0 1234 return EmitUnaliasedVarOp(cx, op, pn->pn_cookie.slot(), bce);
michael@0 1235 }
michael@0 1236
michael@0 1237 switch (op) {
michael@0 1238 case JSOP_GETARG: case JSOP_GETLOCAL: op = JSOP_GETALIASEDVAR; break;
michael@0 1239 case JSOP_SETARG: case JSOP_SETLOCAL: op = JSOP_SETALIASEDVAR; break;
michael@0 1240 default: MOZ_ASSUME_UNREACHABLE("unexpected var op");
michael@0 1241 }
michael@0 1242
michael@0 1243 return EmitAliasedVarOp(cx, op, pn, bce);
michael@0 1244 }
michael@0 1245
michael@0 1246 static JSOp
michael@0 1247 GetIncDecInfo(ParseNodeKind kind, bool *post)
michael@0 1248 {
michael@0 1249 JS_ASSERT(kind == PNK_POSTINCREMENT || kind == PNK_PREINCREMENT ||
michael@0 1250 kind == PNK_POSTDECREMENT || kind == PNK_PREDECREMENT);
michael@0 1251 *post = kind == PNK_POSTINCREMENT || kind == PNK_POSTDECREMENT;
michael@0 1252 return (kind == PNK_POSTINCREMENT || kind == PNK_PREINCREMENT) ? JSOP_ADD : JSOP_SUB;
michael@0 1253 }
michael@0 1254
michael@0 1255 static bool
michael@0 1256 EmitVarIncDec(ExclusiveContext *cx, ParseNode *pn, BytecodeEmitter *bce)
michael@0 1257 {
michael@0 1258 JSOp op = pn->pn_kid->getOp();
michael@0 1259 JS_ASSERT(IsArgOp(op) || IsLocalOp(op) || IsAliasedVarOp(op));
michael@0 1260 JS_ASSERT(pn->pn_kid->isKind(PNK_NAME));
michael@0 1261 JS_ASSERT(!pn->pn_kid->pn_cookie.isFree());
michael@0 1262
michael@0 1263 bool post;
michael@0 1264 JSOp binop = GetIncDecInfo(pn->getKind(), &post);
michael@0 1265
michael@0 1266 JSOp getOp, setOp;
michael@0 1267 if (IsLocalOp(op)) {
michael@0 1268 getOp = JSOP_GETLOCAL;
michael@0 1269 setOp = JSOP_SETLOCAL;
michael@0 1270 } else if (IsArgOp(op)) {
michael@0 1271 getOp = JSOP_GETARG;
michael@0 1272 setOp = JSOP_SETARG;
michael@0 1273 } else {
michael@0 1274 getOp = JSOP_GETALIASEDVAR;
michael@0 1275 setOp = JSOP_SETALIASEDVAR;
michael@0 1276 }
michael@0 1277
michael@0 1278 if (!EmitVarOp(cx, pn->pn_kid, getOp, bce)) // V
michael@0 1279 return false;
michael@0 1280 if (Emit1(cx, bce, JSOP_POS) < 0) // N
michael@0 1281 return false;
michael@0 1282 if (post && Emit1(cx, bce, JSOP_DUP) < 0) // N? N
michael@0 1283 return false;
michael@0 1284 if (Emit1(cx, bce, JSOP_ONE) < 0) // N? N 1
michael@0 1285 return false;
michael@0 1286 if (Emit1(cx, bce, binop) < 0) // N? N+1
michael@0 1287 return false;
michael@0 1288 if (!EmitVarOp(cx, pn->pn_kid, setOp, bce)) // N? N+1
michael@0 1289 return false;
michael@0 1290 if (post && Emit1(cx, bce, JSOP_POP) < 0) // RESULT
michael@0 1291 return false;
michael@0 1292
michael@0 1293 return true;
michael@0 1294 }
michael@0 1295
michael@0 1296 bool
michael@0 1297 BytecodeEmitter::isAliasedName(ParseNode *pn)
michael@0 1298 {
michael@0 1299 Definition *dn = pn->resolve();
michael@0 1300 JS_ASSERT(dn->isDefn());
michael@0 1301 JS_ASSERT(!dn->isPlaceholder());
michael@0 1302 JS_ASSERT(dn->isBound());
michael@0 1303
michael@0 1304 /* If dn is in an enclosing function, it is definitely aliased. */
michael@0 1305 if (dn->pn_cookie.level() != script->staticLevel())
michael@0 1306 return true;
michael@0 1307
michael@0 1308 switch (dn->kind()) {
michael@0 1309 case Definition::LET:
michael@0 1310 /*
michael@0 1311 * There are two ways to alias a let variable: nested functions and
michael@0 1312 * dynamic scope operations. (This is overly conservative since the
michael@0 1313 * bindingsAccessedDynamically flag, checked by allLocalsAliased, is
michael@0 1314 * function-wide.)
michael@0 1315 *
michael@0 1316 * In addition all locals in generators are marked as aliased, to ensure
michael@0 1317 * that they are allocated on scope chains instead of on the stack. See
michael@0 1318 * the definition of SharedContext::allLocalsAliased.
michael@0 1319 */
michael@0 1320 return dn->isClosed() || sc->allLocalsAliased();
michael@0 1321 case Definition::ARG:
michael@0 1322 /*
michael@0 1323 * Consult the bindings, since they already record aliasing. We might
michael@0 1324 * be tempted to use the same definition as VAR/CONST/LET, but there is
michael@0 1325 * a problem caused by duplicate arguments: only the last argument with
michael@0 1326 * a given name is aliased. This is necessary to avoid generating a
michael@0 1327 * shape for the call object with with more than one name for a given
michael@0 1328 * slot (which violates internal engine invariants). All this means that
michael@0 1329 * the '|| sc->allLocalsAliased()' disjunct is incorrect since it will
michael@0 1330 * mark both parameters in function(x,x) as aliased.
michael@0 1331 */
michael@0 1332 return script->formalIsAliased(pn->pn_cookie.slot());
michael@0 1333 case Definition::VAR:
michael@0 1334 case Definition::CONST:
michael@0 1335 JS_ASSERT_IF(sc->allLocalsAliased(), script->varIsAliased(pn->pn_cookie.slot()));
michael@0 1336 return script->varIsAliased(pn->pn_cookie.slot());
michael@0 1337 case Definition::PLACEHOLDER:
michael@0 1338 case Definition::NAMED_LAMBDA:
michael@0 1339 case Definition::MISSING:
michael@0 1340 MOZ_ASSUME_UNREACHABLE("unexpected dn->kind");
michael@0 1341 }
michael@0 1342 return false;
michael@0 1343 }
michael@0 1344
michael@0 1345 /*
michael@0 1346 * Try to convert a *NAME op with a free name to a more specialized GNAME,
michael@0 1347 * INTRINSIC or ALIASEDVAR op, which optimize accesses on that name.
michael@0 1348 * Return true if a conversion was made.
michael@0 1349 */
michael@0 1350 static bool
michael@0 1351 TryConvertFreeName(BytecodeEmitter *bce, ParseNode *pn)
michael@0 1352 {
michael@0 1353 /*
michael@0 1354 * In self-hosting mode, JSOP_*NAME is unconditionally converted to
michael@0 1355 * JSOP_*INTRINSIC. This causes lookups to be redirected to the special
michael@0 1356 * intrinsics holder in the global object, into which any missing values are
michael@0 1357 * cloned lazily upon first access.
michael@0 1358 */
michael@0 1359 if (bce->emitterMode == BytecodeEmitter::SelfHosting) {
michael@0 1360 JSOp op;
michael@0 1361 switch (pn->getOp()) {
michael@0 1362 case JSOP_NAME: op = JSOP_GETINTRINSIC; break;
michael@0 1363 case JSOP_SETNAME: op = JSOP_SETINTRINSIC; break;
michael@0 1364 /* Other *NAME ops aren't (yet) supported in self-hosted code. */
michael@0 1365 default: MOZ_ASSUME_UNREACHABLE("intrinsic");
michael@0 1366 }
michael@0 1367 pn->setOp(op);
michael@0 1368 return true;
michael@0 1369 }
michael@0 1370
michael@0 1371 /*
michael@0 1372 * When parsing inner functions lazily, parse nodes for outer functions no
michael@0 1373 * longer exist and only the function's scope chain is available for
michael@0 1374 * resolving upvar accesses within the inner function.
michael@0 1375 */
michael@0 1376 if (bce->emitterMode == BytecodeEmitter::LazyFunction) {
michael@0 1377 // The only statements within a lazy function which can push lexical
michael@0 1378 // scopes are try/catch blocks. Use generic ops in this case.
michael@0 1379 for (StmtInfoBCE *stmt = bce->topStmt; stmt; stmt = stmt->down) {
michael@0 1380 if (stmt->type == STMT_CATCH)
michael@0 1381 return true;
michael@0 1382 }
michael@0 1383
michael@0 1384 size_t hops = 0;
michael@0 1385 FunctionBox *funbox = bce->sc->asFunctionBox();
michael@0 1386 if (funbox->hasExtensibleScope())
michael@0 1387 return false;
michael@0 1388 if (funbox->function()->isNamedLambda() && funbox->function()->atom() == pn->pn_atom)
michael@0 1389 return false;
michael@0 1390 if (funbox->isHeavyweight()) {
michael@0 1391 hops++;
michael@0 1392 if (funbox->function()->isNamedLambda())
michael@0 1393 hops++;
michael@0 1394 }
michael@0 1395 if (bce->script->directlyInsideEval())
michael@0 1396 return false;
michael@0 1397 RootedObject outerScope(bce->sc->context, bce->script->enclosingStaticScope());
michael@0 1398 for (StaticScopeIter<CanGC> ssi(bce->sc->context, outerScope); !ssi.done(); ssi++) {
michael@0 1399 if (ssi.type() != StaticScopeIter<CanGC>::FUNCTION) {
michael@0 1400 if (ssi.type() == StaticScopeIter<CanGC>::BLOCK) {
michael@0 1401 // Use generic ops if a catch block is encountered.
michael@0 1402 return false;
michael@0 1403 }
michael@0 1404 if (ssi.hasDynamicScopeObject())
michael@0 1405 hops++;
michael@0 1406 continue;
michael@0 1407 }
michael@0 1408 RootedScript script(bce->sc->context, ssi.funScript());
michael@0 1409 if (script->functionNonDelazifying()->atom() == pn->pn_atom)
michael@0 1410 return false;
michael@0 1411 if (ssi.hasDynamicScopeObject()) {
michael@0 1412 uint32_t slot;
michael@0 1413 if (LookupAliasedName(script, pn->pn_atom->asPropertyName(), &slot)) {
michael@0 1414 JSOp op;
michael@0 1415 switch (pn->getOp()) {
michael@0 1416 case JSOP_NAME: op = JSOP_GETALIASEDVAR; break;
michael@0 1417 case JSOP_SETNAME: op = JSOP_SETALIASEDVAR; break;
michael@0 1418 default: return false;
michael@0 1419 }
michael@0 1420 pn->setOp(op);
michael@0 1421 JS_ALWAYS_TRUE(pn->pn_cookie.set(bce->parser->tokenStream, hops, slot));
michael@0 1422 return true;
michael@0 1423 }
michael@0 1424 hops++;
michael@0 1425 }
michael@0 1426
michael@0 1427 if (script->funHasExtensibleScope() || script->directlyInsideEval())
michael@0 1428 return false;
michael@0 1429 }
michael@0 1430 }
michael@0 1431
michael@0 1432 // Unbound names aren't recognizable global-property references if the
michael@0 1433 // script isn't running against its global object.
michael@0 1434 if (!bce->script->compileAndGo() || !bce->hasGlobalScope)
michael@0 1435 return false;
michael@0 1436
michael@0 1437 // Deoptimized names also aren't necessarily globals.
michael@0 1438 if (pn->isDeoptimized())
michael@0 1439 return false;
michael@0 1440
michael@0 1441 if (bce->sc->isFunctionBox()) {
michael@0 1442 // Unbound names in function code may not be globals if new locals can
michael@0 1443 // be added to this function (or an enclosing one) to alias a global
michael@0 1444 // reference.
michael@0 1445 FunctionBox *funbox = bce->sc->asFunctionBox();
michael@0 1446 if (funbox->mightAliasLocals())
michael@0 1447 return false;
michael@0 1448 }
michael@0 1449
michael@0 1450 // If this is eval code, being evaluated inside strict mode eval code,
michael@0 1451 // an "unbound" name might be a binding local to that outer eval:
michael@0 1452 //
michael@0 1453 // var x = "GLOBAL";
michael@0 1454 // eval('"use strict"; ' +
michael@0 1455 // 'var x; ' +
michael@0 1456 // 'eval("print(x)");'); // "undefined", not "GLOBAL"
michael@0 1457 //
michael@0 1458 // Given the enclosing eval code's strictness and its bindings (neither is
michael@0 1459 // readily available now), we could exactly check global-ness, but it's not
michael@0 1460 // worth the trouble for doubly-nested eval code. So we conservatively
michael@0 1461 // approximate. If the outer eval code is strict, then this eval code will
michael@0 1462 // be: thus, don't optimize if we're compiling strict code inside an eval.
michael@0 1463 if (bce->insideEval && bce->sc->strict)
michael@0 1464 return false;
michael@0 1465
michael@0 1466 // Beware: if you change anything here, you might also need to change
michael@0 1467 // js::ReportIfUndeclaredVarAssignment.
michael@0 1468 JSOp op;
michael@0 1469 switch (pn->getOp()) {
michael@0 1470 case JSOP_NAME: op = JSOP_GETGNAME; break;
michael@0 1471 case JSOP_SETNAME: op = JSOP_SETGNAME; break;
michael@0 1472 case JSOP_SETCONST:
michael@0 1473 // Not supported.
michael@0 1474 return false;
michael@0 1475 default: MOZ_ASSUME_UNREACHABLE("gname");
michael@0 1476 }
michael@0 1477 pn->setOp(op);
michael@0 1478 return true;
michael@0 1479 }
michael@0 1480
michael@0 1481 /*
michael@0 1482 * BindNameToSlotHelper attempts to optimize name gets and sets to stack slot
michael@0 1483 * loads and stores, given the compile-time information in bce and a PNK_NAME
michael@0 1484 * node pn. It returns false on error, true on success.
michael@0 1485 *
michael@0 1486 * The caller can test pn->pn_cookie.isFree() to tell whether optimization
michael@0 1487 * occurred, in which case BindNameToSlotHelper also updated pn->pn_op. If
michael@0 1488 * pn->pn_cookie.isFree() is still true on return, pn->pn_op still may have
michael@0 1489 * been optimized, e.g., from JSOP_NAME to JSOP_CALLEE. Whether or not
michael@0 1490 * pn->pn_op was modified, if this function finds an argument or local variable
michael@0 1491 * name, PND_CONST will be set in pn_dflags for read-only properties after a
michael@0 1492 * successful return.
michael@0 1493 *
michael@0 1494 * NB: if you add more opcodes specialized from JSOP_NAME, etc., don't forget
michael@0 1495 * to update the special cases in EmitFor (for-in) and EmitAssignment (= and
michael@0 1496 * op=, e.g. +=).
michael@0 1497 */
michael@0 1498 static bool
michael@0 1499 BindNameToSlotHelper(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
michael@0 1500 {
michael@0 1501 JS_ASSERT(pn->isKind(PNK_NAME));
michael@0 1502
michael@0 1503 JS_ASSERT_IF(pn->isKind(PNK_FUNCTION), pn->isBound());
michael@0 1504
michael@0 1505 /* Don't attempt if 'pn' is already bound or deoptimized or a function. */
michael@0 1506 if (pn->isBound() || pn->isDeoptimized())
michael@0 1507 return true;
michael@0 1508
michael@0 1509 /* JSOP_CALLEE is pre-bound by definition. */
michael@0 1510 JSOp op = pn->getOp();
michael@0 1511 JS_ASSERT(op != JSOP_CALLEE);
michael@0 1512 JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
michael@0 1513
michael@0 1514 /*
michael@0 1515 * The parser already linked name uses to definitions when (where not
michael@0 1516 * prevented by non-lexical constructs like 'with' and 'eval').
michael@0 1517 */
michael@0 1518 Definition *dn;
michael@0 1519 if (pn->isUsed()) {
michael@0 1520 JS_ASSERT(pn->pn_cookie.isFree());
michael@0 1521 dn = pn->pn_lexdef;
michael@0 1522 JS_ASSERT(dn->isDefn());
michael@0 1523 pn->pn_dflags |= (dn->pn_dflags & PND_CONST);
michael@0 1524 } else if (pn->isDefn()) {
michael@0 1525 dn = (Definition *) pn;
michael@0 1526 } else {
michael@0 1527 return true;
michael@0 1528 }
michael@0 1529
michael@0 1530 /*
michael@0 1531 * Turn attempts to mutate const-declared bindings into get ops (for
michael@0 1532 * pre-increment and pre-decrement ops, our caller will have to emit
michael@0 1533 * JSOP_POS, JSOP_ONE, and JSOP_ADD as well).
michael@0 1534 *
michael@0 1535 * Turn JSOP_DELNAME into JSOP_FALSE if dn is known, as all declared
michael@0 1536 * bindings visible to the compiler are permanent in JS unless the
michael@0 1537 * declaration originates at top level in eval code.
michael@0 1538 */
michael@0 1539 switch (op) {
michael@0 1540 case JSOP_NAME:
michael@0 1541 case JSOP_SETCONST:
michael@0 1542 break;
michael@0 1543 default:
michael@0 1544 if (pn->isConst()) {
michael@0 1545 if (bce->sc->needStrictChecks()) {
michael@0 1546 JSAutoByteString name;
michael@0 1547 if (!AtomToPrintableString(cx, pn->pn_atom, &name) ||
michael@0 1548 !bce->reportStrictModeError(pn, JSMSG_READ_ONLY, name.ptr()))
michael@0 1549 {
michael@0 1550 return false;
michael@0 1551 }
michael@0 1552 }
michael@0 1553 pn->setOp(op = JSOP_NAME);
michael@0 1554 }
michael@0 1555 }
michael@0 1556
michael@0 1557 if (dn->pn_cookie.isFree()) {
michael@0 1558 if (HandleScript caller = bce->evalCaller) {
michael@0 1559 JS_ASSERT(bce->script->compileAndGo());
michael@0 1560
michael@0 1561 /*
michael@0 1562 * Don't generate upvars on the left side of a for loop. See
michael@0 1563 * bug 470758.
michael@0 1564 */
michael@0 1565 if (bce->emittingForInit)
michael@0 1566 return true;
michael@0 1567
michael@0 1568 /*
michael@0 1569 * If this is an eval in the global scope, then unbound variables
michael@0 1570 * must be globals, so try to use GNAME ops.
michael@0 1571 */
michael@0 1572 if (!caller->functionOrCallerFunction() && TryConvertFreeName(bce, pn)) {
michael@0 1573 pn->pn_dflags |= PND_BOUND;
michael@0 1574 return true;
michael@0 1575 }
michael@0 1576
michael@0 1577 /*
michael@0 1578 * Out of tricks, so we must rely on PICs to optimize named
michael@0 1579 * accesses from direct eval called from function code.
michael@0 1580 */
michael@0 1581 return true;
michael@0 1582 }
michael@0 1583
michael@0 1584 /* Optimize accesses to undeclared globals. */
michael@0 1585 if (!TryConvertFreeName(bce, pn))
michael@0 1586 return true;
michael@0 1587
michael@0 1588 pn->pn_dflags |= PND_BOUND;
michael@0 1589 return true;
michael@0 1590 }
michael@0 1591
michael@0 1592 /*
michael@0 1593 * At this point, we are only dealing with uses that have already been
michael@0 1594 * bound to definitions via pn_lexdef. The rest of this routine converts
michael@0 1595 * the parse node of the use from its initial JSOP_*NAME* op to a LOCAL/ARG
michael@0 1596 * op. This requires setting the node's pn_cookie with a pair (level, slot)
michael@0 1597 * where 'level' is the number of function scopes between the use and the
michael@0 1598 * def and 'slot' is the index to emit as the immediate of the ARG/LOCAL
michael@0 1599 * op. For example, in this code:
michael@0 1600 *
michael@0 1601 * function(a,b,x) { return x }
michael@0 1602 * function(y) { function() { return y } }
michael@0 1603 *
michael@0 1604 * x will get (level = 0, slot = 2) and y will get (level = 1, slot = 0).
michael@0 1605 */
michael@0 1606 JS_ASSERT(!pn->isDefn());
michael@0 1607 JS_ASSERT(pn->isUsed());
michael@0 1608 JS_ASSERT(pn->pn_lexdef);
michael@0 1609 JS_ASSERT(pn->pn_cookie.isFree());
michael@0 1610
michael@0 1611 /*
michael@0 1612 * We are compiling a function body and may be able to optimize name
michael@0 1613 * to stack slot. Look for an argument or variable in the function and
michael@0 1614 * rewrite pn_op and update pn accordingly.
michael@0 1615 */
michael@0 1616 switch (dn->kind()) {
michael@0 1617 case Definition::ARG:
michael@0 1618 switch (op) {
michael@0 1619 case JSOP_NAME: op = JSOP_GETARG; break;
michael@0 1620 case JSOP_SETNAME: op = JSOP_SETARG; break;
michael@0 1621 default: MOZ_ASSUME_UNREACHABLE("arg");
michael@0 1622 }
michael@0 1623 JS_ASSERT(!pn->isConst());
michael@0 1624 break;
michael@0 1625
michael@0 1626 case Definition::VAR:
michael@0 1627 case Definition::CONST:
michael@0 1628 case Definition::LET:
michael@0 1629 switch (op) {
michael@0 1630 case JSOP_NAME: op = JSOP_GETLOCAL; break;
michael@0 1631 case JSOP_SETNAME: op = JSOP_SETLOCAL; break;
michael@0 1632 case JSOP_SETCONST: op = JSOP_SETLOCAL; break;
michael@0 1633 default: MOZ_ASSUME_UNREACHABLE("local");
michael@0 1634 }
michael@0 1635 break;
michael@0 1636
michael@0 1637 case Definition::NAMED_LAMBDA: {
michael@0 1638 JS_ASSERT(dn->isOp(JSOP_CALLEE));
michael@0 1639 JS_ASSERT(op != JSOP_CALLEE);
michael@0 1640
michael@0 1641 /*
michael@0 1642 * Currently, the ALIASEDVAR ops do not support accessing the
michael@0 1643 * callee of a DeclEnvObject, so use NAME.
michael@0 1644 */
michael@0 1645 if (dn->pn_cookie.level() != bce->script->staticLevel())
michael@0 1646 return true;
michael@0 1647
michael@0 1648 DebugOnly<JSFunction *> fun = bce->sc->asFunctionBox()->function();
michael@0 1649 JS_ASSERT(fun->isLambda());
michael@0 1650 JS_ASSERT(pn->pn_atom == fun->atom());
michael@0 1651
michael@0 1652 /*
michael@0 1653 * Leave pn->isOp(JSOP_NAME) if bce->fun is heavyweight to
michael@0 1654 * address two cases: a new binding introduced by eval, and
michael@0 1655 * assignment to the name in strict mode.
michael@0 1656 *
michael@0 1657 * var fun = (function f(s) { eval(s); return f; });
michael@0 1658 * assertEq(fun("var f = 42"), 42);
michael@0 1659 *
michael@0 1660 * ECMAScript specifies that a function expression's name is bound
michael@0 1661 * in a lexical environment distinct from that used to bind its
michael@0 1662 * named parameters, the arguments object, and its variables. The
michael@0 1663 * new binding for "var f = 42" shadows the binding for the
michael@0 1664 * function itself, so the name of the function will not refer to
michael@0 1665 * the function.
michael@0 1666 *
michael@0 1667 * (function f() { "use strict"; f = 12; })();
michael@0 1668 *
michael@0 1669 * Outside strict mode, assignment to a function expression's name
michael@0 1670 * has no effect. But in strict mode, this attempt to mutate an
michael@0 1671 * immutable binding must throw a TypeError. We implement this by
michael@0 1672 * not optimizing such assignments and by marking such functions as
michael@0 1673 * heavyweight, ensuring that the function name is represented in
michael@0 1674 * the scope chain so that assignment will throw a TypeError.
michael@0 1675 */
michael@0 1676 if (!bce->sc->asFunctionBox()->isHeavyweight()) {
michael@0 1677 op = JSOP_CALLEE;
michael@0 1678 pn->pn_dflags |= PND_CONST;
michael@0 1679 }
michael@0 1680
michael@0 1681 pn->setOp(op);
michael@0 1682 pn->pn_dflags |= PND_BOUND;
michael@0 1683 return true;
michael@0 1684 }
michael@0 1685
michael@0 1686 case Definition::PLACEHOLDER:
michael@0 1687 return true;
michael@0 1688
michael@0 1689 case Definition::MISSING:
michael@0 1690 MOZ_ASSUME_UNREACHABLE("missing");
michael@0 1691 }
michael@0 1692
michael@0 1693 /*
michael@0 1694 * The difference between the current static level and the static level of
michael@0 1695 * the definition is the number of function scopes between the current
michael@0 1696 * scope and dn's scope.
michael@0 1697 */
michael@0 1698 unsigned skip = bce->script->staticLevel() - dn->pn_cookie.level();
michael@0 1699 JS_ASSERT_IF(skip, dn->isClosed());
michael@0 1700
michael@0 1701 /*
michael@0 1702 * Explicitly disallow accessing var/let bindings in global scope from
michael@0 1703 * nested functions. The reason for this limitation is that, since the
michael@0 1704 * global script is not included in the static scope chain (1. because it
michael@0 1705 * has no object to stand in the static scope chain, 2. to minimize memory
michael@0 1706 * bloat where a single live function keeps its whole global script
michael@0 1707 * alive.), ScopeCoordinateToTypeSet is not able to find the var/let's
michael@0 1708 * associated types::TypeSet.
michael@0 1709 */
michael@0 1710 if (skip) {
michael@0 1711 BytecodeEmitter *bceSkipped = bce;
michael@0 1712 for (unsigned i = 0; i < skip; i++)
michael@0 1713 bceSkipped = bceSkipped->parent;
michael@0 1714 if (!bceSkipped->sc->isFunctionBox())
michael@0 1715 return true;
michael@0 1716 }
michael@0 1717
michael@0 1718 JS_ASSERT(!pn->isOp(op));
michael@0 1719 pn->setOp(op);
michael@0 1720 if (!pn->pn_cookie.set(bce->parser->tokenStream, skip, dn->pn_cookie.slot()))
michael@0 1721 return false;
michael@0 1722
michael@0 1723 pn->pn_dflags |= PND_BOUND;
michael@0 1724 return true;
michael@0 1725 }
michael@0 1726
michael@0 1727 /*
michael@0 1728 * Attempts to bind the name, then checks that no dynamic scope lookup ops are
michael@0 1729 * emitted in self-hosting mode. NAME ops do lookups off current scope chain,
michael@0 1730 * and we do not want to allow self-hosted code to use the dynamic scope.
michael@0 1731 */
michael@0 1732 static bool
michael@0 1733 BindNameToSlot(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
michael@0 1734 {
michael@0 1735 if (!BindNameToSlotHelper(cx, bce, pn))
michael@0 1736 return false;
michael@0 1737
michael@0 1738 if (bce->emitterMode == BytecodeEmitter::SelfHosting && !pn->isBound()) {
michael@0 1739 bce->reportError(pn, JSMSG_SELFHOSTED_UNBOUND_NAME);
michael@0 1740 return false;
michael@0 1741 }
michael@0 1742
michael@0 1743 return true;
michael@0 1744 }
michael@0 1745
michael@0 1746 /*
michael@0 1747 * If pn contains a useful expression, return true with *answer set to true.
michael@0 1748 * If pn contains a useless expression, return true with *answer set to false.
michael@0 1749 * Return false on error.
michael@0 1750 *
michael@0 1751 * The caller should initialize *answer to false and invoke this function on
michael@0 1752 * an expression statement or similar subtree to decide whether the tree could
michael@0 1753 * produce code that has any side effects. For an expression statement, we
michael@0 1754 * define useless code as code with no side effects, because the main effect,
michael@0 1755 * the value left on the stack after the code executes, will be discarded by a
michael@0 1756 * pop bytecode.
michael@0 1757 */
michael@0 1758 static bool
michael@0 1759 CheckSideEffects(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool *answer)
michael@0 1760 {
michael@0 1761 if (!pn || *answer)
michael@0 1762 return true;
michael@0 1763
michael@0 1764 switch (pn->getArity()) {
michael@0 1765 case PN_CODE:
michael@0 1766 /*
michael@0 1767 * A named function, contrary to ES3, is no longer useful, because we
michael@0 1768 * bind its name lexically (using JSOP_CALLEE) instead of creating an
michael@0 1769 * Object instance and binding a readonly, permanent property in it
michael@0 1770 * (the object and binding can be detected and hijacked or captured).
michael@0 1771 * This is a bug fix to ES3; it is fixed in ES3.1 drafts.
michael@0 1772 */
michael@0 1773 MOZ_ASSERT(*answer == false);
michael@0 1774 return true;
michael@0 1775
michael@0 1776 case PN_LIST:
michael@0 1777 if (pn->isOp(JSOP_NOP) || pn->isOp(JSOP_OR) || pn->isOp(JSOP_AND) ||
michael@0 1778 pn->isOp(JSOP_STRICTEQ) || pn->isOp(JSOP_STRICTNE)) {
michael@0 1779 /*
michael@0 1780 * Non-operators along with ||, &&, ===, and !== never invoke
michael@0 1781 * toString or valueOf.
michael@0 1782 */
michael@0 1783 bool ok = true;
michael@0 1784 for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
michael@0 1785 ok &= CheckSideEffects(cx, bce, pn2, answer);
michael@0 1786 return ok;
michael@0 1787 }
michael@0 1788
michael@0 1789 if (pn->isKind(PNK_GENEXP)) {
michael@0 1790 /* Generator-expressions are harmless if the result is ignored. */
michael@0 1791 MOZ_ASSERT(*answer == false);
michael@0 1792 return true;
michael@0 1793 }
michael@0 1794
michael@0 1795 /*
michael@0 1796 * All invocation operations (construct: PNK_NEW, call: PNK_CALL)
michael@0 1797 * are presumed to be useful, because they may have side effects
michael@0 1798 * even if their main effect (their return value) is discarded.
michael@0 1799 *
michael@0 1800 * PNK_ELEM binary trees of 3+ nodes are flattened into lists to
michael@0 1801 * avoid too much recursion. All such lists must be presumed to be
michael@0 1802 * useful because each index operation could invoke a getter.
michael@0 1803 *
michael@0 1804 * Likewise, array and object initialisers may call prototype
michael@0 1805 * setters (the __defineSetter__ built-in, and writable __proto__
michael@0 1806 * on Array.prototype create this hazard). Initialiser list nodes
michael@0 1807 * have JSOP_NEWINIT in their pn_op.
michael@0 1808 */
michael@0 1809 *answer = true;
michael@0 1810 return true;
michael@0 1811
michael@0 1812 case PN_TERNARY:
michael@0 1813 return CheckSideEffects(cx, bce, pn->pn_kid1, answer) &&
michael@0 1814 CheckSideEffects(cx, bce, pn->pn_kid2, answer) &&
michael@0 1815 CheckSideEffects(cx, bce, pn->pn_kid3, answer);
michael@0 1816
michael@0 1817 case PN_BINARY:
michael@0 1818 case PN_BINARY_OBJ:
michael@0 1819 if (pn->isAssignment()) {
michael@0 1820 /*
michael@0 1821 * Assignment is presumed to be useful, even if the next operation
michael@0 1822 * is another assignment overwriting this one's ostensible effect,
michael@0 1823 * because the left operand may be a property with a setter that
michael@0 1824 * has side effects.
michael@0 1825 *
michael@0 1826 * The only exception is assignment of a useless value to a const
michael@0 1827 * declared in the function currently being compiled.
michael@0 1828 */
michael@0 1829 ParseNode *pn2 = pn->pn_left;
michael@0 1830 if (!pn2->isKind(PNK_NAME)) {
michael@0 1831 *answer = true;
michael@0 1832 } else {
michael@0 1833 if (!BindNameToSlot(cx, bce, pn2))
michael@0 1834 return false;
michael@0 1835 if (!CheckSideEffects(cx, bce, pn->pn_right, answer))
michael@0 1836 return false;
michael@0 1837 if (!*answer && (!pn->isOp(JSOP_NOP) || !pn2->isConst()))
michael@0 1838 *answer = true;
michael@0 1839 }
michael@0 1840 return true;
michael@0 1841 }
michael@0 1842
michael@0 1843 if (pn->isOp(JSOP_OR) || pn->isOp(JSOP_AND) || pn->isOp(JSOP_STRICTEQ) ||
michael@0 1844 pn->isOp(JSOP_STRICTNE)) {
michael@0 1845 /*
michael@0 1846 * ||, &&, ===, and !== do not convert their operands via
michael@0 1847 * toString or valueOf method calls.
michael@0 1848 */
michael@0 1849 return CheckSideEffects(cx, bce, pn->pn_left, answer) &&
michael@0 1850 CheckSideEffects(cx, bce, pn->pn_right, answer);
michael@0 1851 }
michael@0 1852
michael@0 1853 /*
michael@0 1854 * We can't easily prove that neither operand ever denotes an
michael@0 1855 * object with a toString or valueOf method.
michael@0 1856 */
michael@0 1857 *answer = true;
michael@0 1858 return true;
michael@0 1859
michael@0 1860 case PN_UNARY:
michael@0 1861 switch (pn->getKind()) {
michael@0 1862 case PNK_DELETE:
michael@0 1863 {
michael@0 1864 ParseNode *pn2 = pn->pn_kid;
michael@0 1865 switch (pn2->getKind()) {
michael@0 1866 case PNK_NAME:
michael@0 1867 if (!BindNameToSlot(cx, bce, pn2))
michael@0 1868 return false;
michael@0 1869 if (pn2->isConst()) {
michael@0 1870 MOZ_ASSERT(*answer == false);
michael@0 1871 return true;
michael@0 1872 }
michael@0 1873 /* FALL THROUGH */
michael@0 1874 case PNK_DOT:
michael@0 1875 case PNK_CALL:
michael@0 1876 case PNK_ELEM:
michael@0 1877 /* All these delete addressing modes have effects too. */
michael@0 1878 *answer = true;
michael@0 1879 return true;
michael@0 1880 default:
michael@0 1881 return CheckSideEffects(cx, bce, pn2, answer);
michael@0 1882 }
michael@0 1883 MOZ_ASSUME_UNREACHABLE("We have a returning default case");
michael@0 1884 }
michael@0 1885
michael@0 1886 case PNK_TYPEOF:
michael@0 1887 case PNK_VOID:
michael@0 1888 case PNK_NOT:
michael@0 1889 case PNK_BITNOT:
michael@0 1890 if (pn->isOp(JSOP_NOT)) {
michael@0 1891 /* ! does not convert its operand via toString or valueOf. */
michael@0 1892 return CheckSideEffects(cx, bce, pn->pn_kid, answer);
michael@0 1893 }
michael@0 1894 /* FALL THROUGH */
michael@0 1895
michael@0 1896 default:
michael@0 1897 /*
michael@0 1898 * All of PNK_INC, PNK_DEC, PNK_THROW, PNK_YIELD, and PNK_YIELD_STAR
michael@0 1899 * have direct effects. Of the remaining unary-arity node types, we
michael@0 1900 * can't easily prove that the operand never denotes an object with
michael@0 1901 * a toString or valueOf method.
michael@0 1902 */
michael@0 1903 *answer = true;
michael@0 1904 return true;
michael@0 1905 }
michael@0 1906 MOZ_ASSUME_UNREACHABLE("We have a returning default case");
michael@0 1907
michael@0 1908 case PN_NAME:
michael@0 1909 /*
michael@0 1910 * Take care to avoid trying to bind a label name (labels, both for
michael@0 1911 * statements and property values in object initialisers, have pn_op
michael@0 1912 * defaulted to JSOP_NOP).
michael@0 1913 */
michael@0 1914 if (pn->isKind(PNK_NAME) && !pn->isOp(JSOP_NOP)) {
michael@0 1915 if (!BindNameToSlot(cx, bce, pn))
michael@0 1916 return false;
michael@0 1917 if (!pn->isOp(JSOP_CALLEE) && pn->pn_cookie.isFree()) {
michael@0 1918 /*
michael@0 1919 * Not a use of an unshadowed named function expression's given
michael@0 1920 * name, so this expression could invoke a getter that has side
michael@0 1921 * effects.
michael@0 1922 */
michael@0 1923 *answer = true;
michael@0 1924 }
michael@0 1925 }
michael@0 1926 if (pn->isKind(PNK_DOT)) {
michael@0 1927 /* Dotted property references in general can call getters. */
michael@0 1928 *answer = true;
michael@0 1929 }
michael@0 1930 return CheckSideEffects(cx, bce, pn->maybeExpr(), answer);
michael@0 1931
michael@0 1932 case PN_NULLARY:
michael@0 1933 if (pn->isKind(PNK_DEBUGGER))
michael@0 1934 *answer = true;
michael@0 1935 return true;
michael@0 1936 }
michael@0 1937 return true;
michael@0 1938 }
michael@0 1939
michael@0 1940 bool
michael@0 1941 BytecodeEmitter::isInLoop()
michael@0 1942 {
michael@0 1943 for (StmtInfoBCE *stmt = topStmt; stmt; stmt = stmt->down) {
michael@0 1944 if (stmt->isLoop())
michael@0 1945 return true;
michael@0 1946 }
michael@0 1947 return false;
michael@0 1948 }
michael@0 1949
michael@0 1950 bool
michael@0 1951 BytecodeEmitter::checkSingletonContext()
michael@0 1952 {
michael@0 1953 if (!script->compileAndGo() || sc->isFunctionBox() || isInLoop())
michael@0 1954 return false;
michael@0 1955 hasSingletons = true;
michael@0 1956 return true;
michael@0 1957 }
michael@0 1958
michael@0 1959 bool
michael@0 1960 BytecodeEmitter::needsImplicitThis()
michael@0 1961 {
michael@0 1962 if (!script->compileAndGo())
michael@0 1963 return true;
michael@0 1964
michael@0 1965 if (sc->isFunctionBox()) {
michael@0 1966 if (sc->asFunctionBox()->inWith)
michael@0 1967 return true;
michael@0 1968 } else {
michael@0 1969 JSObject *scope = sc->asGlobalSharedContext()->scopeChain();
michael@0 1970 while (scope) {
michael@0 1971 if (scope->is<DynamicWithObject>())
michael@0 1972 return true;
michael@0 1973 scope = scope->enclosingScope();
michael@0 1974 }
michael@0 1975 }
michael@0 1976
michael@0 1977 for (StmtInfoBCE *stmt = topStmt; stmt; stmt = stmt->down) {
michael@0 1978 if (stmt->type == STMT_WITH)
michael@0 1979 return true;
michael@0 1980 }
michael@0 1981 return false;
michael@0 1982 }
michael@0 1983
michael@0 1984 void
michael@0 1985 BytecodeEmitter::tellDebuggerAboutCompiledScript(ExclusiveContext *cx)
michael@0 1986 {
michael@0 1987 // Note: when parsing off thread the resulting scripts need to be handed to
michael@0 1988 // the debugger after rejoining to the main thread.
michael@0 1989 if (!cx->isJSContext())
michael@0 1990 return;
michael@0 1991
michael@0 1992 RootedFunction function(cx, script->functionNonDelazifying());
michael@0 1993 CallNewScriptHook(cx->asJSContext(), script, function);
michael@0 1994 // Lazy scripts are never top level (despite always being invoked with a
michael@0 1995 // nullptr parent), and so the hook should never be fired.
michael@0 1996 if (emitterMode != LazyFunction && !parent) {
michael@0 1997 GlobalObject *compileAndGoGlobal = nullptr;
michael@0 1998 if (script->compileAndGo())
michael@0 1999 compileAndGoGlobal = &script->global();
michael@0 2000 Debugger::onNewScript(cx->asJSContext(), script, compileAndGoGlobal);
michael@0 2001 }
michael@0 2002 }
michael@0 2003
michael@0 2004 inline TokenStream *
michael@0 2005 BytecodeEmitter::tokenStream()
michael@0 2006 {
michael@0 2007 return &parser->tokenStream;
michael@0 2008 }
michael@0 2009
michael@0 2010 bool
michael@0 2011 BytecodeEmitter::reportError(ParseNode *pn, unsigned errorNumber, ...)
michael@0 2012 {
michael@0 2013 TokenPos pos = pn ? pn->pn_pos : tokenStream()->currentToken().pos;
michael@0 2014
michael@0 2015 va_list args;
michael@0 2016 va_start(args, errorNumber);
michael@0 2017 bool result = tokenStream()->reportCompileErrorNumberVA(pos.begin, JSREPORT_ERROR,
michael@0 2018 errorNumber, args);
michael@0 2019 va_end(args);
michael@0 2020 return result;
michael@0 2021 }
michael@0 2022
michael@0 2023 bool
michael@0 2024 BytecodeEmitter::reportStrictWarning(ParseNode *pn, unsigned errorNumber, ...)
michael@0 2025 {
michael@0 2026 TokenPos pos = pn ? pn->pn_pos : tokenStream()->currentToken().pos;
michael@0 2027
michael@0 2028 va_list args;
michael@0 2029 va_start(args, errorNumber);
michael@0 2030 bool result = tokenStream()->reportStrictWarningErrorNumberVA(pos.begin, errorNumber, args);
michael@0 2031 va_end(args);
michael@0 2032 return result;
michael@0 2033 }
michael@0 2034
michael@0 2035 bool
michael@0 2036 BytecodeEmitter::reportStrictModeError(ParseNode *pn, unsigned errorNumber, ...)
michael@0 2037 {
michael@0 2038 TokenPos pos = pn ? pn->pn_pos : tokenStream()->currentToken().pos;
michael@0 2039
michael@0 2040 va_list args;
michael@0 2041 va_start(args, errorNumber);
michael@0 2042 bool result = tokenStream()->reportStrictModeErrorNumberVA(pos.begin, sc->strict,
michael@0 2043 errorNumber, args);
michael@0 2044 va_end(args);
michael@0 2045 return result;
michael@0 2046 }
michael@0 2047
michael@0 2048 static bool
michael@0 2049 EmitNewInit(ExclusiveContext *cx, BytecodeEmitter *bce, JSProtoKey key)
michael@0 2050 {
michael@0 2051 const size_t len = 1 + UINT32_INDEX_LEN;
michael@0 2052 ptrdiff_t offset = EmitCheck(cx, bce, len);
michael@0 2053 if (offset < 0)
michael@0 2054 return false;
michael@0 2055
michael@0 2056 jsbytecode *code = bce->code(offset);
michael@0 2057 code[0] = JSOP_NEWINIT;
michael@0 2058 code[1] = jsbytecode(key);
michael@0 2059 code[2] = 0;
michael@0 2060 code[3] = 0;
michael@0 2061 code[4] = 0;
michael@0 2062 UpdateDepth(cx, bce, offset);
michael@0 2063 CheckTypeSet(cx, bce, JSOP_NEWINIT);
michael@0 2064 return true;
michael@0 2065 }
michael@0 2066
michael@0 2067 static bool
michael@0 2068 IteratorResultShape(ExclusiveContext *cx, BytecodeEmitter *bce, unsigned *shape)
michael@0 2069 {
michael@0 2070 JS_ASSERT(bce->script->compileAndGo());
michael@0 2071
michael@0 2072 RootedObject obj(cx);
michael@0 2073 gc::AllocKind kind = GuessObjectGCKind(2);
michael@0 2074 obj = NewBuiltinClassInstance(cx, &JSObject::class_, kind);
michael@0 2075 if (!obj)
michael@0 2076 return false;
michael@0 2077
michael@0 2078 Rooted<jsid> value_id(cx, AtomToId(cx->names().value));
michael@0 2079 Rooted<jsid> done_id(cx, AtomToId(cx->names().done));
michael@0 2080 if (!DefineNativeProperty(cx, obj, value_id, UndefinedHandleValue, nullptr, nullptr,
michael@0 2081 JSPROP_ENUMERATE))
michael@0 2082 return false;
michael@0 2083 if (!DefineNativeProperty(cx, obj, done_id, UndefinedHandleValue, nullptr, nullptr,
michael@0 2084 JSPROP_ENUMERATE))
michael@0 2085 return false;
michael@0 2086
michael@0 2087 ObjectBox *objbox = bce->parser->newObjectBox(obj);
michael@0 2088 if (!objbox)
michael@0 2089 return false;
michael@0 2090
michael@0 2091 *shape = bce->objectList.add(objbox);
michael@0 2092
michael@0 2093 return true;
michael@0 2094 }
michael@0 2095
michael@0 2096 static bool
michael@0 2097 EmitPrepareIteratorResult(ExclusiveContext *cx, BytecodeEmitter *bce)
michael@0 2098 {
michael@0 2099 if (bce->script->compileAndGo()) {
michael@0 2100 unsigned shape;
michael@0 2101 if (!IteratorResultShape(cx, bce, &shape))
michael@0 2102 return false;
michael@0 2103 return EmitIndex32(cx, JSOP_NEWOBJECT, shape, bce);
michael@0 2104 }
michael@0 2105
michael@0 2106 return EmitNewInit(cx, bce, JSProto_Object);
michael@0 2107 }
michael@0 2108
michael@0 2109 static bool
michael@0 2110 EmitFinishIteratorResult(ExclusiveContext *cx, BytecodeEmitter *bce, bool done)
michael@0 2111 {
michael@0 2112 jsatomid value_id;
michael@0 2113 if (!bce->makeAtomIndex(cx->names().value, &value_id))
michael@0 2114 return UINT_MAX;
michael@0 2115 jsatomid done_id;
michael@0 2116 if (!bce->makeAtomIndex(cx->names().done, &done_id))
michael@0 2117 return UINT_MAX;
michael@0 2118
michael@0 2119 if (!EmitIndex32(cx, JSOP_INITPROP, value_id, bce))
michael@0 2120 return false;
michael@0 2121 if (Emit1(cx, bce, done ? JSOP_TRUE : JSOP_FALSE) < 0)
michael@0 2122 return false;
michael@0 2123 if (!EmitIndex32(cx, JSOP_INITPROP, done_id, bce))
michael@0 2124 return false;
michael@0 2125 if (Emit1(cx, bce, JSOP_ENDINIT) < 0)
michael@0 2126 return false;
michael@0 2127 return true;
michael@0 2128 }
michael@0 2129
michael@0 2130 static bool
michael@0 2131 EmitNameOp(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool callContext)
michael@0 2132 {
michael@0 2133 if (!BindNameToSlot(cx, bce, pn))
michael@0 2134 return false;
michael@0 2135
michael@0 2136 JSOp op = pn->getOp();
michael@0 2137
michael@0 2138 if (op == JSOP_CALLEE) {
michael@0 2139 if (Emit1(cx, bce, op) < 0)
michael@0 2140 return false;
michael@0 2141 } else {
michael@0 2142 if (!pn->pn_cookie.isFree()) {
michael@0 2143 JS_ASSERT(JOF_OPTYPE(op) != JOF_ATOM);
michael@0 2144 if (!EmitVarOp(cx, pn, op, bce))
michael@0 2145 return false;
michael@0 2146 } else {
michael@0 2147 if (!EmitAtomOp(cx, pn, op, bce))
michael@0 2148 return false;
michael@0 2149 }
michael@0 2150 }
michael@0 2151
michael@0 2152 /* Need to provide |this| value for call */
michael@0 2153 if (callContext) {
michael@0 2154 if (op == JSOP_NAME && bce->needsImplicitThis()) {
michael@0 2155 if (!EmitAtomOp(cx, pn, JSOP_IMPLICITTHIS, bce))
michael@0 2156 return false;
michael@0 2157 } else {
michael@0 2158 if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
michael@0 2159 return false;
michael@0 2160 }
michael@0 2161 }
michael@0 2162
michael@0 2163 return true;
michael@0 2164 }
michael@0 2165
michael@0 2166 static bool
michael@0 2167 EmitPropLHS(ExclusiveContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
michael@0 2168 {
michael@0 2169 JS_ASSERT(pn->isKind(PNK_DOT));
michael@0 2170 ParseNode *pn2 = pn->maybeExpr();
michael@0 2171
michael@0 2172 /*
michael@0 2173 * If the object operand is also a dotted property reference, reverse the
michael@0 2174 * list linked via pn_expr temporarily so we can iterate over it from the
michael@0 2175 * bottom up (reversing again as we go), to avoid excessive recursion.
michael@0 2176 */
michael@0 2177 if (pn2->isKind(PNK_DOT)) {
michael@0 2178 ParseNode *pndot = pn2;
michael@0 2179 ParseNode *pnup = nullptr, *pndown;
michael@0 2180 ptrdiff_t top = bce->offset();
michael@0 2181 for (;;) {
michael@0 2182 /* Reverse pndot->pn_expr to point up, not down. */
michael@0 2183 pndot->pn_offset = top;
michael@0 2184 JS_ASSERT(!pndot->isUsed());
michael@0 2185 pndown = pndot->pn_expr;
michael@0 2186 pndot->pn_expr = pnup;
michael@0 2187 if (!pndown->isKind(PNK_DOT))
michael@0 2188 break;
michael@0 2189 pnup = pndot;
michael@0 2190 pndot = pndown;
michael@0 2191 }
michael@0 2192
michael@0 2193 /* pndown is a primary expression, not a dotted property reference. */
michael@0 2194 if (!EmitTree(cx, bce, pndown))
michael@0 2195 return false;
michael@0 2196
michael@0 2197 do {
michael@0 2198 /* Walk back up the list, emitting annotated name ops. */
michael@0 2199 if (!EmitAtomOp(cx, pndot, JSOP_GETPROP, bce))
michael@0 2200 return false;
michael@0 2201
michael@0 2202 /* Reverse the pn_expr link again. */
michael@0 2203 pnup = pndot->pn_expr;
michael@0 2204 pndot->pn_expr = pndown;
michael@0 2205 pndown = pndot;
michael@0 2206 } while ((pndot = pnup) != nullptr);
michael@0 2207 return true;
michael@0 2208 }
michael@0 2209
michael@0 2210 // The non-optimized case.
michael@0 2211 return EmitTree(cx, bce, pn2);
michael@0 2212 }
michael@0 2213
michael@0 2214 static bool
michael@0 2215 EmitPropOp(ExclusiveContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
michael@0 2216 {
michael@0 2217 JS_ASSERT(pn->isArity(PN_NAME));
michael@0 2218
michael@0 2219 if (!EmitPropLHS(cx, pn, op, bce))
michael@0 2220 return false;
michael@0 2221
michael@0 2222 if (op == JSOP_CALLPROP && Emit1(cx, bce, JSOP_DUP) < 0)
michael@0 2223 return false;
michael@0 2224
michael@0 2225 if (!EmitAtomOp(cx, pn, op, bce))
michael@0 2226 return false;
michael@0 2227
michael@0 2228 if (op == JSOP_CALLPROP && Emit1(cx, bce, JSOP_SWAP) < 0)
michael@0 2229 return false;
michael@0 2230
michael@0 2231 return true;
michael@0 2232 }
michael@0 2233
michael@0 2234 static bool
michael@0 2235 EmitPropIncDec(ExclusiveContext *cx, ParseNode *pn, BytecodeEmitter *bce)
michael@0 2236 {
michael@0 2237 JS_ASSERT(pn->pn_kid->getKind() == PNK_DOT);
michael@0 2238
michael@0 2239 bool post;
michael@0 2240 JSOp binop = GetIncDecInfo(pn->getKind(), &post);
michael@0 2241
michael@0 2242 JSOp get = JSOP_GETPROP;
michael@0 2243 if (!EmitPropLHS(cx, pn->pn_kid, get, bce)) // OBJ
michael@0 2244 return false;
michael@0 2245 if (Emit1(cx, bce, JSOP_DUP) < 0) // OBJ OBJ
michael@0 2246 return false;
michael@0 2247 if (!EmitAtomOp(cx, pn->pn_kid, JSOP_GETPROP, bce)) // OBJ V
michael@0 2248 return false;
michael@0 2249 if (Emit1(cx, bce, JSOP_POS) < 0) // OBJ N
michael@0 2250 return false;
michael@0 2251 if (post && Emit1(cx, bce, JSOP_DUP) < 0) // OBJ N? N
michael@0 2252 return false;
michael@0 2253 if (Emit1(cx, bce, JSOP_ONE) < 0) // OBJ N? N 1
michael@0 2254 return false;
michael@0 2255 if (Emit1(cx, bce, binop) < 0) // OBJ N? N+1
michael@0 2256 return false;
michael@0 2257
michael@0 2258 if (post) {
michael@0 2259 if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)2) < 0) // N? N+1 OBJ
michael@0 2260 return false;
michael@0 2261 if (Emit1(cx, bce, JSOP_SWAP) < 0) // N? OBJ N+1
michael@0 2262 return false;
michael@0 2263 }
michael@0 2264
michael@0 2265 if (!EmitAtomOp(cx, pn->pn_kid, JSOP_SETPROP, bce)) // N? N+1
michael@0 2266 return false;
michael@0 2267 if (post && Emit1(cx, bce, JSOP_POP) < 0) // RESULT
michael@0 2268 return false;
michael@0 2269
michael@0 2270 return true;
michael@0 2271 }
michael@0 2272
michael@0 2273 static bool
michael@0 2274 EmitNameIncDec(ExclusiveContext *cx, ParseNode *pn, BytecodeEmitter *bce)
michael@0 2275 {
michael@0 2276 const JSCodeSpec *cs = &js_CodeSpec[pn->pn_kid->getOp()];
michael@0 2277
michael@0 2278 bool global = (cs->format & JOF_GNAME);
michael@0 2279 bool post;
michael@0 2280 JSOp binop = GetIncDecInfo(pn->getKind(), &post);
michael@0 2281
michael@0 2282 if (!EmitAtomOp(cx, pn->pn_kid, global ? JSOP_BINDGNAME : JSOP_BINDNAME, bce)) // OBJ
michael@0 2283 return false;
michael@0 2284 if (!EmitAtomOp(cx, pn->pn_kid, global ? JSOP_GETGNAME : JSOP_NAME, bce)) // OBJ V
michael@0 2285 return false;
michael@0 2286 if (Emit1(cx, bce, JSOP_POS) < 0) // OBJ N
michael@0 2287 return false;
michael@0 2288 if (post && Emit1(cx, bce, JSOP_DUP) < 0) // OBJ N? N
michael@0 2289 return false;
michael@0 2290 if (Emit1(cx, bce, JSOP_ONE) < 0) // OBJ N? N 1
michael@0 2291 return false;
michael@0 2292 if (Emit1(cx, bce, binop) < 0) // OBJ N? N+1
michael@0 2293 return false;
michael@0 2294
michael@0 2295 if (post) {
michael@0 2296 if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)2) < 0) // N? N+1 OBJ
michael@0 2297 return false;
michael@0 2298 if (Emit1(cx, bce, JSOP_SWAP) < 0) // N? OBJ N+1
michael@0 2299 return false;
michael@0 2300 }
michael@0 2301
michael@0 2302 if (!EmitAtomOp(cx, pn->pn_kid, global ? JSOP_SETGNAME : JSOP_SETNAME, bce)) // N? N+1
michael@0 2303 return false;
michael@0 2304 if (post && Emit1(cx, bce, JSOP_POP) < 0) // RESULT
michael@0 2305 return false;
michael@0 2306
michael@0 2307 return true;
michael@0 2308 }
michael@0 2309
michael@0 2310 /*
michael@0 2311 * Emit bytecode to put operands for a JSOP_GETELEM/CALLELEM/SETELEM/DELELEM
michael@0 2312 * opcode onto the stack in the right order. In the case of SETELEM, the
michael@0 2313 * value to be assigned must already be pushed.
michael@0 2314 */
michael@0 2315 static bool
michael@0 2316 EmitElemOperands(ExclusiveContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
michael@0 2317 {
michael@0 2318 JS_ASSERT(pn->isArity(PN_BINARY));
michael@0 2319 if (!EmitTree(cx, bce, pn->pn_left))
michael@0 2320 return false;
michael@0 2321 if (op == JSOP_CALLELEM && Emit1(cx, bce, JSOP_DUP) < 0)
michael@0 2322 return false;
michael@0 2323 if (!EmitTree(cx, bce, pn->pn_right))
michael@0 2324 return false;
michael@0 2325 if (op == JSOP_SETELEM && Emit2(cx, bce, JSOP_PICK, (jsbytecode)2) < 0)
michael@0 2326 return false;
michael@0 2327 return true;
michael@0 2328 }
michael@0 2329
michael@0 2330 static inline bool
michael@0 2331 EmitElemOpBase(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op)
michael@0 2332 {
michael@0 2333 if (Emit1(cx, bce, op) < 0)
michael@0 2334 return false;
michael@0 2335 CheckTypeSet(cx, bce, op);
michael@0 2336
michael@0 2337 if (op == JSOP_CALLELEM) {
michael@0 2338 if (Emit1(cx, bce, JSOP_SWAP) < 0)
michael@0 2339 return false;
michael@0 2340 }
michael@0 2341 return true;
michael@0 2342 }
michael@0 2343
michael@0 2344 static bool
michael@0 2345 EmitElemOp(ExclusiveContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
michael@0 2346 {
michael@0 2347 return EmitElemOperands(cx, pn, op, bce) && EmitElemOpBase(cx, bce, op);
michael@0 2348 }
michael@0 2349
michael@0 2350 static bool
michael@0 2351 EmitElemIncDec(ExclusiveContext *cx, ParseNode *pn, BytecodeEmitter *bce)
michael@0 2352 {
michael@0 2353 JS_ASSERT(pn->pn_kid->getKind() == PNK_ELEM);
michael@0 2354
michael@0 2355 if (!EmitElemOperands(cx, pn->pn_kid, JSOP_GETELEM, bce))
michael@0 2356 return false;
michael@0 2357
michael@0 2358 bool post;
michael@0 2359 JSOp binop = GetIncDecInfo(pn->getKind(), &post);
michael@0 2360
michael@0 2361 /*
michael@0 2362 * We need to convert the key to an object id first, so that we do not do
michael@0 2363 * it inside both the GETELEM and the SETELEM.
michael@0 2364 */
michael@0 2365 // OBJ KEY*
michael@0 2366 if (Emit1(cx, bce, JSOP_TOID) < 0) // OBJ KEY
michael@0 2367 return false;
michael@0 2368 if (Emit1(cx, bce, JSOP_DUP2) < 0) // OBJ KEY OBJ KEY
michael@0 2369 return false;
michael@0 2370 if (!EmitElemOpBase(cx, bce, JSOP_GETELEM)) // OBJ KEY V
michael@0 2371 return false;
michael@0 2372 if (Emit1(cx, bce, JSOP_POS) < 0) // OBJ KEY N
michael@0 2373 return false;
michael@0 2374 if (post && Emit1(cx, bce, JSOP_DUP) < 0) // OBJ KEY N? N
michael@0 2375 return false;
michael@0 2376 if (Emit1(cx, bce, JSOP_ONE) < 0) // OBJ KEY N? N 1
michael@0 2377 return false;
michael@0 2378 if (Emit1(cx, bce, binop) < 0) // OBJ KEY N? N+1
michael@0 2379 return false;
michael@0 2380
michael@0 2381 if (post) {
michael@0 2382 if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)3) < 0) // KEY N N+1 OBJ
michael@0 2383 return false;
michael@0 2384 if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)3) < 0) // N N+1 OBJ KEY
michael@0 2385 return false;
michael@0 2386 if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)2) < 0) // N OBJ KEY N+1
michael@0 2387 return false;
michael@0 2388 }
michael@0 2389
michael@0 2390 if (!EmitElemOpBase(cx, bce, JSOP_SETELEM)) // N? N+1
michael@0 2391 return false;
michael@0 2392 if (post && Emit1(cx, bce, JSOP_POP) < 0) // RESULT
michael@0 2393 return false;
michael@0 2394
michael@0 2395 return true;
michael@0 2396 }
michael@0 2397
michael@0 2398 static bool
michael@0 2399 EmitNumberOp(ExclusiveContext *cx, double dval, BytecodeEmitter *bce)
michael@0 2400 {
michael@0 2401 int32_t ival;
michael@0 2402 uint32_t u;
michael@0 2403 ptrdiff_t off;
michael@0 2404 jsbytecode *pc;
michael@0 2405
michael@0 2406 if (NumberIsInt32(dval, &ival)) {
michael@0 2407 if (ival == 0)
michael@0 2408 return Emit1(cx, bce, JSOP_ZERO) >= 0;
michael@0 2409 if (ival == 1)
michael@0 2410 return Emit1(cx, bce, JSOP_ONE) >= 0;
michael@0 2411 if ((int)(int8_t)ival == ival)
michael@0 2412 return Emit2(cx, bce, JSOP_INT8, (jsbytecode)(int8_t)ival) >= 0;
michael@0 2413
michael@0 2414 u = (uint32_t)ival;
michael@0 2415 if (u < JS_BIT(16)) {
michael@0 2416 EMIT_UINT16_IMM_OP(JSOP_UINT16, u);
michael@0 2417 } else if (u < JS_BIT(24)) {
michael@0 2418 off = EmitN(cx, bce, JSOP_UINT24, 3);
michael@0 2419 if (off < 0)
michael@0 2420 return false;
michael@0 2421 pc = bce->code(off);
michael@0 2422 SET_UINT24(pc, u);
michael@0 2423 } else {
michael@0 2424 off = EmitN(cx, bce, JSOP_INT32, 4);
michael@0 2425 if (off < 0)
michael@0 2426 return false;
michael@0 2427 pc = bce->code(off);
michael@0 2428 SET_INT32(pc, ival);
michael@0 2429 }
michael@0 2430 return true;
michael@0 2431 }
michael@0 2432
michael@0 2433 if (!bce->constList.append(DoubleValue(dval)))
michael@0 2434 return false;
michael@0 2435
michael@0 2436 return EmitIndex32(cx, JSOP_DOUBLE, bce->constList.length() - 1, bce);
michael@0 2437 }
michael@0 2438
michael@0 2439 static inline void
michael@0 2440 SetJumpOffsetAt(BytecodeEmitter *bce, ptrdiff_t off)
michael@0 2441 {
michael@0 2442 SET_JUMP_OFFSET(bce->code(off), bce->offset() - off);
michael@0 2443 }
michael@0 2444
michael@0 2445 static bool
michael@0 2446 PushUndefinedValues(ExclusiveContext *cx, BytecodeEmitter *bce, unsigned n)
michael@0 2447 {
michael@0 2448 for (unsigned i = 0; i < n; ++i) {
michael@0 2449 if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
michael@0 2450 return false;
michael@0 2451 }
michael@0 2452 return true;
michael@0 2453 }
michael@0 2454
michael@0 2455 static bool
michael@0 2456 InitializeBlockScopedLocalsFromStack(ExclusiveContext *cx, BytecodeEmitter *bce,
michael@0 2457 Handle<StaticBlockObject *> blockObj)
michael@0 2458 {
michael@0 2459 for (unsigned i = blockObj->numVariables(); i > 0; --i) {
michael@0 2460 if (blockObj->isAliased(i - 1)) {
michael@0 2461 ScopeCoordinate sc;
michael@0 2462 sc.setHops(0);
michael@0 2463 sc.setSlot(BlockObject::RESERVED_SLOTS + i - 1);
michael@0 2464 if (!EmitAliasedVarOp(cx, JSOP_SETALIASEDVAR, sc, bce))
michael@0 2465 return false;
michael@0 2466 } else {
michael@0 2467 unsigned local = blockObj->blockIndexToLocalIndex(i - 1);
michael@0 2468 if (!EmitUnaliasedVarOp(cx, JSOP_SETLOCAL, local, bce))
michael@0 2469 return false;
michael@0 2470 }
michael@0 2471 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 2472 return false;
michael@0 2473 }
michael@0 2474 return true;
michael@0 2475 }
michael@0 2476
michael@0 2477 static bool
michael@0 2478 EnterBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *stmtInfo,
michael@0 2479 ObjectBox *objbox, unsigned alreadyPushed = 0)
michael@0 2480 {
michael@0 2481 // Initial values for block-scoped locals.
michael@0 2482 Rooted<StaticBlockObject *> blockObj(cx, &objbox->object->as<StaticBlockObject>());
michael@0 2483 if (!PushUndefinedValues(cx, bce, blockObj->numVariables() - alreadyPushed))
michael@0 2484 return false;
michael@0 2485
michael@0 2486 if (!EnterNestedScope(cx, bce, stmtInfo, objbox, STMT_BLOCK))
michael@0 2487 return false;
michael@0 2488
michael@0 2489 if (!InitializeBlockScopedLocalsFromStack(cx, bce, blockObj))
michael@0 2490 return false;
michael@0 2491
michael@0 2492 return true;
michael@0 2493 }
michael@0 2494
michael@0 2495 /*
michael@0 2496 * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047.
michael@0 2497 * LLVM is deciding to inline this function which uses a lot of stack space
michael@0 2498 * into EmitTree which is recursive and uses relatively little stack space.
michael@0 2499 */
michael@0 2500 MOZ_NEVER_INLINE static bool
michael@0 2501 EmitSwitch(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
michael@0 2502 {
michael@0 2503 JSOp switchOp;
michael@0 2504 bool hasDefault;
michael@0 2505 ptrdiff_t top, off, defaultOffset;
michael@0 2506 ParseNode *pn2, *pn3, *pn4;
michael@0 2507 int32_t low, high;
michael@0 2508 int noteIndex;
michael@0 2509 size_t switchSize;
michael@0 2510 jsbytecode *pc;
michael@0 2511
michael@0 2512 /* Try for most optimal, fall back if not dense ints. */
michael@0 2513 switchOp = JSOP_TABLESWITCH;
michael@0 2514 hasDefault = false;
michael@0 2515 defaultOffset = -1;
michael@0 2516
michael@0 2517 pn2 = pn->pn_right;
michael@0 2518 JS_ASSERT(pn2->isKind(PNK_LEXICALSCOPE) || pn2->isKind(PNK_STATEMENTLIST));
michael@0 2519
michael@0 2520 /* Push the discriminant. */
michael@0 2521 if (!EmitTree(cx, bce, pn->pn_left))
michael@0 2522 return false;
michael@0 2523
michael@0 2524 StmtInfoBCE stmtInfo(cx);
michael@0 2525 if (pn2->isKind(PNK_LEXICALSCOPE)) {
michael@0 2526 if (!EnterBlockScope(cx, bce, &stmtInfo, pn2->pn_objbox, 0))
michael@0 2527 return false;
michael@0 2528
michael@0 2529 stmtInfo.type = STMT_SWITCH;
michael@0 2530 stmtInfo.update = top = bce->offset();
michael@0 2531 /* Advance pn2 to refer to the switch case list. */
michael@0 2532 pn2 = pn2->expr();
michael@0 2533 } else {
michael@0 2534 JS_ASSERT(pn2->isKind(PNK_STATEMENTLIST));
michael@0 2535 top = bce->offset();
michael@0 2536 PushStatementBCE(bce, &stmtInfo, STMT_SWITCH, top);
michael@0 2537 }
michael@0 2538
michael@0 2539 /* Switch bytecodes run from here till end of final case. */
michael@0 2540 uint32_t caseCount = pn2->pn_count;
michael@0 2541 uint32_t tableLength = 0;
michael@0 2542 ScopedJSFreePtr<ParseNode*> table(nullptr);
michael@0 2543
michael@0 2544 if (caseCount > JS_BIT(16)) {
michael@0 2545 bce->parser->tokenStream.reportError(JSMSG_TOO_MANY_CASES);
michael@0 2546 return false;
michael@0 2547 }
michael@0 2548
michael@0 2549 if (caseCount == 0 ||
michael@0 2550 (caseCount == 1 &&
michael@0 2551 (hasDefault = (pn2->pn_head->isKind(PNK_DEFAULT))))) {
michael@0 2552 caseCount = 0;
michael@0 2553 low = 0;
michael@0 2554 high = -1;
michael@0 2555 } else {
michael@0 2556 bool ok = true;
michael@0 2557 #define INTMAP_LENGTH 256
michael@0 2558 jsbitmap intmap_space[INTMAP_LENGTH];
michael@0 2559 jsbitmap *intmap = nullptr;
michael@0 2560 int32_t intmap_bitlen = 0;
michael@0 2561
michael@0 2562 low = JSVAL_INT_MAX;
michael@0 2563 high = JSVAL_INT_MIN;
michael@0 2564
michael@0 2565 for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
michael@0 2566 if (pn3->isKind(PNK_DEFAULT)) {
michael@0 2567 hasDefault = true;
michael@0 2568 caseCount--; /* one of the "cases" was the default */
michael@0 2569 continue;
michael@0 2570 }
michael@0 2571
michael@0 2572 JS_ASSERT(pn3->isKind(PNK_CASE));
michael@0 2573 if (switchOp == JSOP_CONDSWITCH)
michael@0 2574 continue;
michael@0 2575
michael@0 2576 JS_ASSERT(switchOp == JSOP_TABLESWITCH);
michael@0 2577
michael@0 2578 pn4 = pn3->pn_left;
michael@0 2579
michael@0 2580 if (pn4->getKind() != PNK_NUMBER) {
michael@0 2581 switchOp = JSOP_CONDSWITCH;
michael@0 2582 continue;
michael@0 2583 }
michael@0 2584
michael@0 2585 int32_t i;
michael@0 2586 if (!NumberIsInt32(pn4->pn_dval, &i)) {
michael@0 2587 switchOp = JSOP_CONDSWITCH;
michael@0 2588 continue;
michael@0 2589 }
michael@0 2590
michael@0 2591 if ((unsigned)(i + (int)JS_BIT(15)) >= (unsigned)JS_BIT(16)) {
michael@0 2592 switchOp = JSOP_CONDSWITCH;
michael@0 2593 continue;
michael@0 2594 }
michael@0 2595 if (i < low)
michael@0 2596 low = i;
michael@0 2597 if (high < i)
michael@0 2598 high = i;
michael@0 2599
michael@0 2600 /*
michael@0 2601 * Check for duplicates, which require a JSOP_CONDSWITCH.
michael@0 2602 * We bias i by 65536 if it's negative, and hope that's a rare
michael@0 2603 * case (because it requires a malloc'd bitmap).
michael@0 2604 */
michael@0 2605 if (i < 0)
michael@0 2606 i += JS_BIT(16);
michael@0 2607 if (i >= intmap_bitlen) {
michael@0 2608 if (!intmap &&
michael@0 2609 size_t(i) < (INTMAP_LENGTH * JS_BITMAP_NBITS)) {
michael@0 2610 intmap = intmap_space;
michael@0 2611 intmap_bitlen = INTMAP_LENGTH * JS_BITMAP_NBITS;
michael@0 2612 } else {
michael@0 2613 /* Just grab 8K for the worst-case bitmap. */
michael@0 2614 intmap_bitlen = JS_BIT(16);
michael@0 2615 intmap = cx->pod_malloc<jsbitmap>(JS_BIT(16) / JS_BITMAP_NBITS);
michael@0 2616 if (!intmap) {
michael@0 2617 js_ReportOutOfMemory(cx);
michael@0 2618 return false;
michael@0 2619 }
michael@0 2620 }
michael@0 2621 memset(intmap, 0, size_t(intmap_bitlen) / CHAR_BIT);
michael@0 2622 }
michael@0 2623 if (JS_TEST_BIT(intmap, i)) {
michael@0 2624 switchOp = JSOP_CONDSWITCH;
michael@0 2625 continue;
michael@0 2626 }
michael@0 2627 JS_SET_BIT(intmap, i);
michael@0 2628 }
michael@0 2629
michael@0 2630 if (intmap && intmap != intmap_space)
michael@0 2631 js_free(intmap);
michael@0 2632 if (!ok)
michael@0 2633 return false;
michael@0 2634
michael@0 2635 /*
michael@0 2636 * Compute table length and select condswitch instead if overlarge or
michael@0 2637 * more than half-sparse.
michael@0 2638 */
michael@0 2639 if (switchOp == JSOP_TABLESWITCH) {
michael@0 2640 tableLength = (uint32_t)(high - low + 1);
michael@0 2641 if (tableLength >= JS_BIT(16) || tableLength > 2 * caseCount)
michael@0 2642 switchOp = JSOP_CONDSWITCH;
michael@0 2643 }
michael@0 2644 }
michael@0 2645
michael@0 2646 /*
michael@0 2647 * The note has one or two offsets: first tells total switch code length;
michael@0 2648 * second (if condswitch) tells offset to first JSOP_CASE.
michael@0 2649 */
michael@0 2650 if (switchOp == JSOP_CONDSWITCH) {
michael@0 2651 /* 0 bytes of immediate for unoptimized switch. */
michael@0 2652 switchSize = 0;
michael@0 2653 noteIndex = NewSrcNote3(cx, bce, SRC_CONDSWITCH, 0, 0);
michael@0 2654 } else {
michael@0 2655 JS_ASSERT(switchOp == JSOP_TABLESWITCH);
michael@0 2656
michael@0 2657 /* 3 offsets (len, low, high) before the table, 1 per entry. */
michael@0 2658 switchSize = (size_t)(JUMP_OFFSET_LEN * (3 + tableLength));
michael@0 2659 noteIndex = NewSrcNote2(cx, bce, SRC_TABLESWITCH, 0);
michael@0 2660 }
michael@0 2661 if (noteIndex < 0)
michael@0 2662 return false;
michael@0 2663
michael@0 2664 /* Emit switchOp followed by switchSize bytes of jump or lookup table. */
michael@0 2665 if (EmitN(cx, bce, switchOp, switchSize) < 0)
michael@0 2666 return false;
michael@0 2667
michael@0 2668 off = -1;
michael@0 2669 if (switchOp == JSOP_CONDSWITCH) {
michael@0 2670 int caseNoteIndex = -1;
michael@0 2671 bool beforeCases = true;
michael@0 2672
michael@0 2673 /* Emit code for evaluating cases and jumping to case statements. */
michael@0 2674 for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
michael@0 2675 pn4 = pn3->pn_left;
michael@0 2676 if (pn4 && !EmitTree(cx, bce, pn4))
michael@0 2677 return false;
michael@0 2678 if (caseNoteIndex >= 0) {
michael@0 2679 /* off is the previous JSOP_CASE's bytecode offset. */
michael@0 2680 if (!SetSrcNoteOffset(cx, bce, (unsigned)caseNoteIndex, 0, bce->offset() - off))
michael@0 2681 return false;
michael@0 2682 }
michael@0 2683 if (!pn4) {
michael@0 2684 JS_ASSERT(pn3->isKind(PNK_DEFAULT));
michael@0 2685 continue;
michael@0 2686 }
michael@0 2687 caseNoteIndex = NewSrcNote2(cx, bce, SRC_NEXTCASE, 0);
michael@0 2688 if (caseNoteIndex < 0)
michael@0 2689 return false;
michael@0 2690 off = EmitJump(cx, bce, JSOP_CASE, 0);
michael@0 2691 if (off < 0)
michael@0 2692 return false;
michael@0 2693 pn3->pn_offset = off;
michael@0 2694 if (beforeCases) {
michael@0 2695 unsigned noteCount, noteCountDelta;
michael@0 2696
michael@0 2697 /* Switch note's second offset is to first JSOP_CASE. */
michael@0 2698 noteCount = bce->notes().length();
michael@0 2699 if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 1, off - top))
michael@0 2700 return false;
michael@0 2701 noteCountDelta = bce->notes().length() - noteCount;
michael@0 2702 if (noteCountDelta != 0)
michael@0 2703 caseNoteIndex += noteCountDelta;
michael@0 2704 beforeCases = false;
michael@0 2705 }
michael@0 2706 }
michael@0 2707
michael@0 2708 /*
michael@0 2709 * If we didn't have an explicit default (which could fall in between
michael@0 2710 * cases, preventing us from fusing this SetSrcNoteOffset with the call
michael@0 2711 * in the loop above), link the last case to the implicit default for
michael@0 2712 * the benefit of IonBuilder.
michael@0 2713 */
michael@0 2714 if (!hasDefault &&
michael@0 2715 caseNoteIndex >= 0 &&
michael@0 2716 !SetSrcNoteOffset(cx, bce, (unsigned)caseNoteIndex, 0, bce->offset() - off))
michael@0 2717 {
michael@0 2718 return false;
michael@0 2719 }
michael@0 2720
michael@0 2721 /* Emit default even if no explicit default statement. */
michael@0 2722 defaultOffset = EmitJump(cx, bce, JSOP_DEFAULT, 0);
michael@0 2723 if (defaultOffset < 0)
michael@0 2724 return false;
michael@0 2725 } else {
michael@0 2726 JS_ASSERT(switchOp == JSOP_TABLESWITCH);
michael@0 2727 pc = bce->code(top + JUMP_OFFSET_LEN);
michael@0 2728
michael@0 2729 /* Fill in switch bounds, which we know fit in 16-bit offsets. */
michael@0 2730 SET_JUMP_OFFSET(pc, low);
michael@0 2731 pc += JUMP_OFFSET_LEN;
michael@0 2732 SET_JUMP_OFFSET(pc, high);
michael@0 2733 pc += JUMP_OFFSET_LEN;
michael@0 2734
michael@0 2735 /*
michael@0 2736 * Use malloc to avoid arena bloat for programs with many switches.
michael@0 2737 * ScopedJSFreePtr takes care of freeing it on exit.
michael@0 2738 */
michael@0 2739 if (tableLength != 0) {
michael@0 2740 table = cx->pod_calloc<ParseNode*>(tableLength);
michael@0 2741 if (!table)
michael@0 2742 return false;
michael@0 2743 for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
michael@0 2744 if (pn3->isKind(PNK_DEFAULT))
michael@0 2745 continue;
michael@0 2746
michael@0 2747 JS_ASSERT(pn3->isKind(PNK_CASE));
michael@0 2748
michael@0 2749 pn4 = pn3->pn_left;
michael@0 2750 JS_ASSERT(pn4->getKind() == PNK_NUMBER);
michael@0 2751
michael@0 2752 int32_t i = int32_t(pn4->pn_dval);
michael@0 2753 JS_ASSERT(double(i) == pn4->pn_dval);
michael@0 2754
michael@0 2755 i -= low;
michael@0 2756 JS_ASSERT(uint32_t(i) < tableLength);
michael@0 2757 table[i] = pn3;
michael@0 2758 }
michael@0 2759 }
michael@0 2760 }
michael@0 2761
michael@0 2762 /* Emit code for each case's statements, copying pn_offset up to pn3. */
michael@0 2763 for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
michael@0 2764 if (switchOp == JSOP_CONDSWITCH && !pn3->isKind(PNK_DEFAULT))
michael@0 2765 SetJumpOffsetAt(bce, pn3->pn_offset);
michael@0 2766 pn4 = pn3->pn_right;
michael@0 2767 if (!EmitTree(cx, bce, pn4))
michael@0 2768 return false;
michael@0 2769 pn3->pn_offset = pn4->pn_offset;
michael@0 2770 if (pn3->isKind(PNK_DEFAULT))
michael@0 2771 off = pn3->pn_offset - top;
michael@0 2772 }
michael@0 2773
michael@0 2774 if (!hasDefault) {
michael@0 2775 /* If no default case, offset for default is to end of switch. */
michael@0 2776 off = bce->offset() - top;
michael@0 2777 }
michael@0 2778
michael@0 2779 /* We better have set "off" by now. */
michael@0 2780 JS_ASSERT(off != -1);
michael@0 2781
michael@0 2782 /* Set the default offset (to end of switch if no default). */
michael@0 2783 if (switchOp == JSOP_CONDSWITCH) {
michael@0 2784 pc = nullptr;
michael@0 2785 JS_ASSERT(defaultOffset != -1);
michael@0 2786 SET_JUMP_OFFSET(bce->code(defaultOffset), off - (defaultOffset - top));
michael@0 2787 } else {
michael@0 2788 pc = bce->code(top);
michael@0 2789 SET_JUMP_OFFSET(pc, off);
michael@0 2790 pc += JUMP_OFFSET_LEN;
michael@0 2791 }
michael@0 2792
michael@0 2793 /* Set the SRC_SWITCH note's offset operand to tell end of switch. */
michael@0 2794 off = bce->offset() - top;
michael@0 2795 if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, off))
michael@0 2796 return false;
michael@0 2797
michael@0 2798 if (switchOp == JSOP_TABLESWITCH) {
michael@0 2799 /* Skip over the already-initialized switch bounds. */
michael@0 2800 pc += 2 * JUMP_OFFSET_LEN;
michael@0 2801
michael@0 2802 /* Fill in the jump table, if there is one. */
michael@0 2803 for (uint32_t i = 0; i < tableLength; i++) {
michael@0 2804 pn3 = table[i];
michael@0 2805 off = pn3 ? pn3->pn_offset - top : 0;
michael@0 2806 SET_JUMP_OFFSET(pc, off);
michael@0 2807 pc += JUMP_OFFSET_LEN;
michael@0 2808 }
michael@0 2809 }
michael@0 2810
michael@0 2811 if (pn->pn_right->isKind(PNK_LEXICALSCOPE)) {
michael@0 2812 if (!LeaveNestedScope(cx, bce, &stmtInfo))
michael@0 2813 return false;
michael@0 2814 } else {
michael@0 2815 if (!PopStatementBCE(cx, bce))
michael@0 2816 return false;
michael@0 2817 }
michael@0 2818
michael@0 2819 return true;
michael@0 2820 }
michael@0 2821
michael@0 2822 bool
michael@0 2823 BytecodeEmitter::isRunOnceLambda()
michael@0 2824 {
michael@0 2825 // The run once lambda flags set by the parser are approximate, and we look
michael@0 2826 // at properties of the function itself before deciding to emit a function
michael@0 2827 // as a run once lambda.
michael@0 2828
michael@0 2829 if (!(parent && parent->emittingRunOnceLambda) && !lazyRunOnceLambda)
michael@0 2830 return false;
michael@0 2831
michael@0 2832 FunctionBox *funbox = sc->asFunctionBox();
michael@0 2833 return !funbox->argumentsHasLocalBinding() &&
michael@0 2834 !funbox->isGenerator() &&
michael@0 2835 !funbox->function()->name();
michael@0 2836 }
michael@0 2837
michael@0 2838 bool
michael@0 2839 frontend::EmitFunctionScript(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *body)
michael@0 2840 {
michael@0 2841 /*
michael@0 2842 * IonBuilder has assumptions about what may occur immediately after
michael@0 2843 * script->main (e.g., in the case of destructuring params). Thus, put the
michael@0 2844 * following ops into the range [script->code, script->main). Note:
michael@0 2845 * execution starts from script->code, so this has no semantic effect.
michael@0 2846 */
michael@0 2847
michael@0 2848 FunctionBox *funbox = bce->sc->asFunctionBox();
michael@0 2849 if (funbox->argumentsHasLocalBinding()) {
michael@0 2850 JS_ASSERT(bce->offset() == 0); /* See JSScript::argumentsBytecode. */
michael@0 2851 bce->switchToProlog();
michael@0 2852 if (Emit1(cx, bce, JSOP_ARGUMENTS) < 0)
michael@0 2853 return false;
michael@0 2854 InternalBindingsHandle bindings(bce->script, &bce->script->bindings);
michael@0 2855 uint32_t varIndex = Bindings::argumentsVarIndex(cx, bindings);
michael@0 2856 if (bce->script->varIsAliased(varIndex)) {
michael@0 2857 ScopeCoordinate sc;
michael@0 2858 sc.setHops(0);
michael@0 2859 JS_ALWAYS_TRUE(LookupAliasedNameSlot(bce->script, cx->names().arguments, &sc));
michael@0 2860 if (!EmitAliasedVarOp(cx, JSOP_SETALIASEDVAR, sc, bce))
michael@0 2861 return false;
michael@0 2862 } else {
michael@0 2863 if (!EmitUnaliasedVarOp(cx, JSOP_SETLOCAL, varIndex, bce))
michael@0 2864 return false;
michael@0 2865 }
michael@0 2866 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 2867 return false;
michael@0 2868 bce->switchToMain();
michael@0 2869 }
michael@0 2870
michael@0 2871 if (funbox->isGenerator()) {
michael@0 2872 bce->switchToProlog();
michael@0 2873 if (Emit1(cx, bce, JSOP_GENERATOR) < 0)
michael@0 2874 return false;
michael@0 2875 bce->switchToMain();
michael@0 2876 }
michael@0 2877
michael@0 2878 /*
michael@0 2879 * Emit a prologue for run-once scripts which will deoptimize JIT code if
michael@0 2880 * the script ends up running multiple times via foo.caller related
michael@0 2881 * shenanigans.
michael@0 2882 */
michael@0 2883 bool runOnce = bce->isRunOnceLambda();
michael@0 2884 if (runOnce) {
michael@0 2885 bce->switchToProlog();
michael@0 2886 if (Emit1(cx, bce, JSOP_RUNONCE) < 0)
michael@0 2887 return false;
michael@0 2888 bce->switchToMain();
michael@0 2889 }
michael@0 2890
michael@0 2891 if (!EmitTree(cx, bce, body))
michael@0 2892 return false;
michael@0 2893
michael@0 2894 // If we fall off the end of an ES6 generator, return a boxed iterator
michael@0 2895 // result object of the form { value: undefined, done: true }.
michael@0 2896 if (bce->sc->isFunctionBox() && bce->sc->asFunctionBox()->isStarGenerator()) {
michael@0 2897 if (!EmitPrepareIteratorResult(cx, bce))
michael@0 2898 return false;
michael@0 2899 if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
michael@0 2900 return false;
michael@0 2901 if (!EmitFinishIteratorResult(cx, bce, true))
michael@0 2902 return false;
michael@0 2903
michael@0 2904 // No need to check for finally blocks, etc as in EmitReturn.
michael@0 2905 if (Emit1(cx, bce, JSOP_RETURN) < 0)
michael@0 2906 return false;
michael@0 2907 }
michael@0 2908
michael@0 2909 /*
michael@0 2910 * Always end the script with a JSOP_RETRVAL. Some other parts of the codebase
michael@0 2911 * depend on this opcode, e.g. js_InternalInterpret.
michael@0 2912 */
michael@0 2913 if (Emit1(cx, bce, JSOP_RETRVAL) < 0)
michael@0 2914 return false;
michael@0 2915
michael@0 2916 if (!JSScript::fullyInitFromEmitter(cx, bce->script, bce))
michael@0 2917 return false;
michael@0 2918
michael@0 2919 /*
michael@0 2920 * If this function is only expected to run once, mark the script so that
michael@0 2921 * initializers created within it may be given more precise types.
michael@0 2922 */
michael@0 2923 if (runOnce) {
michael@0 2924 bce->script->setTreatAsRunOnce();
michael@0 2925 JS_ASSERT(!bce->script->hasRunOnce());
michael@0 2926 }
michael@0 2927
michael@0 2928 /* Initialize fun->script() so that the debugger has a valid fun->script(). */
michael@0 2929 RootedFunction fun(cx, bce->script->functionNonDelazifying());
michael@0 2930 JS_ASSERT(fun->isInterpreted());
michael@0 2931
michael@0 2932 if (fun->isInterpretedLazy())
michael@0 2933 fun->setUnlazifiedScript(bce->script);
michael@0 2934 else
michael@0 2935 fun->setScript(bce->script);
michael@0 2936
michael@0 2937 bce->tellDebuggerAboutCompiledScript(cx);
michael@0 2938
michael@0 2939 return true;
michael@0 2940 }
michael@0 2941
michael@0 2942 static bool
michael@0 2943 MaybeEmitVarDecl(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn,
michael@0 2944 jsatomid *result)
michael@0 2945 {
michael@0 2946 jsatomid atomIndex;
michael@0 2947
michael@0 2948 if (!pn->pn_cookie.isFree()) {
michael@0 2949 atomIndex = pn->pn_cookie.slot();
michael@0 2950 } else {
michael@0 2951 if (!bce->makeAtomIndex(pn->pn_atom, &atomIndex))
michael@0 2952 return false;
michael@0 2953 }
michael@0 2954
michael@0 2955 if (JOF_OPTYPE(pn->getOp()) == JOF_ATOM &&
michael@0 2956 (!bce->sc->isFunctionBox() || bce->sc->asFunctionBox()->isHeavyweight()))
michael@0 2957 {
michael@0 2958 bce->switchToProlog();
michael@0 2959 if (!UpdateSourceCoordNotes(cx, bce, pn->pn_pos.begin))
michael@0 2960 return false;
michael@0 2961 if (!EmitIndexOp(cx, prologOp, atomIndex, bce))
michael@0 2962 return false;
michael@0 2963 bce->switchToMain();
michael@0 2964 }
michael@0 2965
michael@0 2966 if (result)
michael@0 2967 *result = atomIndex;
michael@0 2968 return true;
michael@0 2969 }
michael@0 2970
michael@0 2971 /*
michael@0 2972 * This enum tells EmitVariables and the destructuring functions how emit the
michael@0 2973 * given Parser::variables parse tree. In the base case, DefineVars, the caller
michael@0 2974 * only wants variables to be defined in the prologue (if necessary). For
michael@0 2975 * PushInitialValues, variable initializer expressions are evaluated and left
michael@0 2976 * on the stack. For InitializeVars, the initializer expressions values are
michael@0 2977 * assigned (to local variables) and popped.
michael@0 2978 */
michael@0 2979 enum VarEmitOption
michael@0 2980 {
michael@0 2981 DefineVars = 0,
michael@0 2982 PushInitialValues = 1,
michael@0 2983 InitializeVars = 2
michael@0 2984 };
michael@0 2985
michael@0 2986 typedef bool
michael@0 2987 (*DestructuringDeclEmitter)(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn);
michael@0 2988
michael@0 2989 static bool
michael@0 2990 EmitDestructuringDecl(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn)
michael@0 2991 {
michael@0 2992 JS_ASSERT(pn->isKind(PNK_NAME));
michael@0 2993 if (!BindNameToSlot(cx, bce, pn))
michael@0 2994 return false;
michael@0 2995
michael@0 2996 JS_ASSERT(!pn->isOp(JSOP_CALLEE));
michael@0 2997 return MaybeEmitVarDecl(cx, bce, prologOp, pn, nullptr);
michael@0 2998 }
michael@0 2999
michael@0 3000 static bool
michael@0 3001 EmitDestructuringDecls(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp prologOp,
michael@0 3002 ParseNode *pattern)
michael@0 3003 {
michael@0 3004 if (pattern->isKind(PNK_ARRAY)) {
michael@0 3005 for (ParseNode *element = pattern->pn_head; element; element = element->pn_next) {
michael@0 3006 if (element->isKind(PNK_ELISION))
michael@0 3007 continue;
michael@0 3008 DestructuringDeclEmitter emitter =
michael@0 3009 element->isKind(PNK_NAME) ? EmitDestructuringDecl : EmitDestructuringDecls;
michael@0 3010 if (!emitter(cx, bce, prologOp, element))
michael@0 3011 return false;
michael@0 3012 }
michael@0 3013 return true;
michael@0 3014 }
michael@0 3015
michael@0 3016 MOZ_ASSERT(pattern->isKind(PNK_OBJECT));
michael@0 3017 for (ParseNode *member = pattern->pn_head; member; member = member->pn_next) {
michael@0 3018 ParseNode *target = member->pn_right;
michael@0 3019 DestructuringDeclEmitter emitter =
michael@0 3020 target->isKind(PNK_NAME) ? EmitDestructuringDecl : EmitDestructuringDecls;
michael@0 3021 if (!emitter(cx, bce, prologOp, target))
michael@0 3022 return false;
michael@0 3023 }
michael@0 3024 return true;
michael@0 3025 }
michael@0 3026
michael@0 3027 static bool
michael@0 3028 EmitDestructuringOpsHelper(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn,
michael@0 3029 VarEmitOption emitOption);
michael@0 3030
michael@0 3031 /*
michael@0 3032 * EmitDestructuringLHS assumes the to-be-destructured value has been pushed on
michael@0 3033 * the stack and emits code to destructure a single lhs expression (either a
michael@0 3034 * name or a compound []/{} expression).
michael@0 3035 *
michael@0 3036 * If emitOption is InitializeVars, the to-be-destructured value is assigned to
michael@0 3037 * locals and ultimately the initial slot is popped (-1 total depth change).
michael@0 3038 *
michael@0 3039 * If emitOption is PushInitialValues, the to-be-destructured value is replaced
michael@0 3040 * with the initial values of the N (where 0 <= N) variables assigned in the
michael@0 3041 * lhs expression. (Same post-condition as EmitDestructuringOpsHelper)
michael@0 3042 */
michael@0 3043 static bool
michael@0 3044 EmitDestructuringLHS(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, VarEmitOption emitOption)
michael@0 3045 {
michael@0 3046 JS_ASSERT(emitOption != DefineVars);
michael@0 3047
michael@0 3048 // Now emit the lvalue opcode sequence. If the lvalue is a nested
michael@0 3049 // destructuring initialiser-form, call ourselves to handle it, then pop
michael@0 3050 // the matched value. Otherwise emit an lvalue bytecode sequence followed
michael@0 3051 // by an assignment op.
michael@0 3052 if (pn->isKind(PNK_ARRAY) || pn->isKind(PNK_OBJECT)) {
michael@0 3053 if (!EmitDestructuringOpsHelper(cx, bce, pn, emitOption))
michael@0 3054 return false;
michael@0 3055 if (emitOption == InitializeVars) {
michael@0 3056 // Per its post-condition, EmitDestructuringOpsHelper has left the
michael@0 3057 // to-be-destructured value on top of the stack.
michael@0 3058 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 3059 return false;
michael@0 3060 }
michael@0 3061 } else if (emitOption == PushInitialValues) {
michael@0 3062 // The lhs is a simple name so the to-be-destructured value is
michael@0 3063 // its initial value and there is nothing to do.
michael@0 3064 JS_ASSERT(pn->getOp() == JSOP_GETLOCAL);
michael@0 3065 JS_ASSERT(pn->pn_dflags & PND_BOUND);
michael@0 3066 } else {
michael@0 3067 switch (pn->getKind()) {
michael@0 3068 case PNK_NAME:
michael@0 3069 if (!BindNameToSlot(cx, bce, pn))
michael@0 3070 return false;
michael@0 3071
michael@0 3072 // Allow 'const [x,y] = o', make 'const x,y; [x,y] = o' a nop.
michael@0 3073 if (pn->isConst() && !pn->isDefn())
michael@0 3074 return Emit1(cx, bce, JSOP_POP) >= 0;
michael@0 3075
michael@0 3076 switch (pn->getOp()) {
michael@0 3077 case JSOP_SETNAME:
michael@0 3078 case JSOP_SETGNAME:
michael@0 3079 case JSOP_SETCONST: {
michael@0 3080 // This is like ordinary assignment, but with one difference.
michael@0 3081 //
michael@0 3082 // In `a = b`, we first determine a binding for `a` (using
michael@0 3083 // JSOP_BINDNAME or JSOP_BINDGNAME), then we evaluate `b`, then
michael@0 3084 // a JSOP_SETNAME instruction.
michael@0 3085 //
michael@0 3086 // In `[a] = [b]`, per spec, `b` is evaluated first, then we
michael@0 3087 // determine a binding for `a`. Then we need to do assignment--
michael@0 3088 // but the operands are on the stack in the wrong order for
michael@0 3089 // JSOP_SETPROP, so we have to add a JSOP_SWAP.
michael@0 3090 jsatomid atomIndex;
michael@0 3091 if (!bce->makeAtomIndex(pn->pn_atom, &atomIndex))
michael@0 3092 return false;
michael@0 3093
michael@0 3094 if (!pn->isOp(JSOP_SETCONST)) {
michael@0 3095 JSOp bindOp = pn->isOp(JSOP_SETNAME) ? JSOP_BINDNAME : JSOP_BINDGNAME;
michael@0 3096 if (!EmitIndex32(cx, bindOp, atomIndex, bce))
michael@0 3097 return false;
michael@0 3098 if (Emit1(cx, bce, JSOP_SWAP) < 0)
michael@0 3099 return false;
michael@0 3100 }
michael@0 3101
michael@0 3102 if (!EmitIndexOp(cx, pn->getOp(), atomIndex, bce))
michael@0 3103 return false;
michael@0 3104 break;
michael@0 3105 }
michael@0 3106
michael@0 3107 case JSOP_SETLOCAL:
michael@0 3108 case JSOP_SETARG:
michael@0 3109 if (!EmitVarOp(cx, pn, pn->getOp(), bce))
michael@0 3110 return false;
michael@0 3111 break;
michael@0 3112
michael@0 3113 default:
michael@0 3114 MOZ_ASSUME_UNREACHABLE("EmitDestructuringLHS: bad name op");
michael@0 3115 }
michael@0 3116 break;
michael@0 3117
michael@0 3118 case PNK_DOT:
michael@0 3119 // See the (PNK_NAME, JSOP_SETNAME) case above.
michael@0 3120 //
michael@0 3121 // In `a.x = b`, `a` is evaluated first, then `b`, then a
michael@0 3122 // JSOP_SETPROP instruction.
michael@0 3123 //
michael@0 3124 // In `[a.x] = [b]`, per spec, `b` is evaluated before `a`. Then we
michael@0 3125 // need a property set -- but the operands are on the stack in the
michael@0 3126 // wrong order for JSOP_SETPROP, so we have to add a JSOP_SWAP.
michael@0 3127 if (!EmitTree(cx, bce, pn->pn_expr))
michael@0 3128 return false;
michael@0 3129 if (Emit1(cx, bce, JSOP_SWAP) < 0)
michael@0 3130 return false;
michael@0 3131 if (!EmitAtomOp(cx, pn, JSOP_SETPROP, bce))
michael@0 3132 return false;
michael@0 3133 break;
michael@0 3134
michael@0 3135 case PNK_ELEM:
michael@0 3136 // See the comment at `case PNK_DOT:` above. This case,
michael@0 3137 // `[a[x]] = [b]`, is handled much the same way. The JSOP_SWAP
michael@0 3138 // is emitted by EmitElemOperands.
michael@0 3139 if (!EmitElemOp(cx, pn, JSOP_SETELEM, bce))
michael@0 3140 return false;
michael@0 3141 break;
michael@0 3142
michael@0 3143 case PNK_CALL:
michael@0 3144 JS_ASSERT(pn->pn_xflags & PNX_SETCALL);
michael@0 3145 if (!EmitTree(cx, bce, pn))
michael@0 3146 return false;
michael@0 3147
michael@0 3148 // Pop the call return value. Below, we pop the RHS too, balancing
michael@0 3149 // the stack --- presumably for the benefit of bytecode
michael@0 3150 // analysis. (The interpreter will never reach these instructions
michael@0 3151 // since we just emitted JSOP_SETCALL, which always throws. It's
michael@0 3152 // possible no analyses actually depend on this either.)
michael@0 3153 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 3154 return false;
michael@0 3155 break;
michael@0 3156
michael@0 3157 default:
michael@0 3158 MOZ_ASSUME_UNREACHABLE("EmitDestructuringLHS: bad lhs kind");
michael@0 3159 }
michael@0 3160
michael@0 3161 // Pop the assigned value.
michael@0 3162 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 3163 return false;
michael@0 3164 }
michael@0 3165
michael@0 3166 return true;
michael@0 3167 }
michael@0 3168
michael@0 3169 /*
michael@0 3170 * Recursive helper for EmitDestructuringOps.
michael@0 3171 * EmitDestructuringOpsHelper assumes the to-be-destructured value has been
michael@0 3172 * pushed on the stack and emits code to destructure each part of a [] or {}
michael@0 3173 * lhs expression.
michael@0 3174 *
michael@0 3175 * If emitOption is InitializeVars, the initial to-be-destructured value is
michael@0 3176 * left untouched on the stack and the overall depth is not changed.
michael@0 3177 *
michael@0 3178 * If emitOption is PushInitialValues, the to-be-destructured value is replaced
michael@0 3179 * with the initial values of the N (where 0 <= N) variables assigned in the
michael@0 3180 * lhs expression. (Same post-condition as EmitDestructuringLHS)
michael@0 3181 */
michael@0 3182 static bool
michael@0 3183 EmitDestructuringOpsHelper(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn,
michael@0 3184 VarEmitOption emitOption)
michael@0 3185 {
michael@0 3186 JS_ASSERT(emitOption != DefineVars);
michael@0 3187
michael@0 3188 unsigned index;
michael@0 3189 ParseNode *pn2, *pn3;
michael@0 3190 bool doElemOp;
michael@0 3191
michael@0 3192 #ifdef DEBUG
michael@0 3193 int stackDepth = bce->stackDepth;
michael@0 3194 JS_ASSERT(stackDepth != 0);
michael@0 3195 JS_ASSERT(pn->isArity(PN_LIST));
michael@0 3196 JS_ASSERT(pn->isKind(PNK_ARRAY) || pn->isKind(PNK_OBJECT));
michael@0 3197 #endif
michael@0 3198
michael@0 3199 index = 0;
michael@0 3200 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
michael@0 3201 /* Duplicate the value being destructured to use as a reference base. */
michael@0 3202 if (Emit1(cx, bce, JSOP_DUP) < 0)
michael@0 3203 return false;
michael@0 3204
michael@0 3205 /*
michael@0 3206 * Now push the property name currently being matched, which is either
michael@0 3207 * the array initialiser's current index, or the current property name
michael@0 3208 * "label" on the left of a colon in the object initialiser. Set pn3
michael@0 3209 * to the lvalue node, which is in the value-initializing position.
michael@0 3210 */
michael@0 3211 doElemOp = true;
michael@0 3212 if (pn->isKind(PNK_ARRAY)) {
michael@0 3213 if (!EmitNumberOp(cx, index, bce))
michael@0 3214 return false;
michael@0 3215 pn3 = pn2;
michael@0 3216 } else {
michael@0 3217 JS_ASSERT(pn->isKind(PNK_OBJECT));
michael@0 3218 JS_ASSERT(pn2->isKind(PNK_COLON));
michael@0 3219
michael@0 3220 ParseNode *key = pn2->pn_left;
michael@0 3221 if (key->isKind(PNK_NUMBER)) {
michael@0 3222 if (!EmitNumberOp(cx, key->pn_dval, bce))
michael@0 3223 return false;
michael@0 3224 } else {
michael@0 3225 MOZ_ASSERT(key->isKind(PNK_STRING) || key->isKind(PNK_NAME));
michael@0 3226 PropertyName *name = key->pn_atom->asPropertyName();
michael@0 3227
michael@0 3228 // The parser already checked for atoms representing indexes and
michael@0 3229 // used PNK_NUMBER instead, but also watch for ids which TI treats
michael@0 3230 // as indexes for simplification of downstream analysis.
michael@0 3231 jsid id = NameToId(name);
michael@0 3232 if (id != types::IdToTypeId(id)) {
michael@0 3233 if (!EmitTree(cx, bce, key))
michael@0 3234 return false;
michael@0 3235 } else {
michael@0 3236 if (!EmitAtomOp(cx, name, JSOP_GETPROP, bce))
michael@0 3237 return false;
michael@0 3238 doElemOp = false;
michael@0 3239 }
michael@0 3240 }
michael@0 3241
michael@0 3242 pn3 = pn2->pn_right;
michael@0 3243 }
michael@0 3244
michael@0 3245 if (doElemOp) {
michael@0 3246 /*
michael@0 3247 * Ok, get the value of the matching property name. This leaves
michael@0 3248 * that value on top of the value being destructured, so the stack
michael@0 3249 * is one deeper than when we started.
michael@0 3250 */
michael@0 3251 if (!EmitElemOpBase(cx, bce, JSOP_GETELEM))
michael@0 3252 return false;
michael@0 3253 JS_ASSERT(bce->stackDepth >= stackDepth + 1);
michael@0 3254 }
michael@0 3255
michael@0 3256 /* Elision node makes a hole in the array destructurer. */
michael@0 3257 if (pn3->isKind(PNK_ELISION)) {
michael@0 3258 JS_ASSERT(pn->isKind(PNK_ARRAY));
michael@0 3259 JS_ASSERT(pn2 == pn3);
michael@0 3260 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 3261 return false;
michael@0 3262 } else {
michael@0 3263 int32_t depthBefore = bce->stackDepth;
michael@0 3264 if (!EmitDestructuringLHS(cx, bce, pn3, emitOption))
michael@0 3265 return false;
michael@0 3266
michael@0 3267 if (emitOption == PushInitialValues) {
michael@0 3268 /*
michael@0 3269 * After '[x,y]' in 'let ([[x,y], z] = o)', the stack is
michael@0 3270 * | to-be-destructured-value | x | y |
michael@0 3271 * The goal is:
michael@0 3272 * | x | y | z |
michael@0 3273 * so emit a pick to produce the intermediate state
michael@0 3274 * | x | y | to-be-destructured-value |
michael@0 3275 * before destructuring z. This gives the loop invariant that
michael@0 3276 * the to-be-destructured-value is always on top of the stack.
michael@0 3277 */
michael@0 3278 JS_ASSERT((bce->stackDepth - bce->stackDepth) >= -1);
michael@0 3279 uint32_t pickDistance = (uint32_t)((bce->stackDepth + 1) - depthBefore);
michael@0 3280 if (pickDistance > 0) {
michael@0 3281 if (pickDistance > UINT8_MAX) {
michael@0 3282 bce->reportError(pn3, JSMSG_TOO_MANY_LOCALS);
michael@0 3283 return false;
michael@0 3284 }
michael@0 3285 if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)pickDistance) < 0)
michael@0 3286 return false;
michael@0 3287 }
michael@0 3288 }
michael@0 3289 }
michael@0 3290
michael@0 3291 ++index;
michael@0 3292 }
michael@0 3293
michael@0 3294 if (emitOption == PushInitialValues) {
michael@0 3295 /*
michael@0 3296 * Per the above loop invariant, to-be-destructured-value is at the top
michael@0 3297 * of the stack. To achieve the post-condition, pop it.
michael@0 3298 */
michael@0 3299 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 3300 return false;
michael@0 3301 }
michael@0 3302
michael@0 3303 return true;
michael@0 3304 }
michael@0 3305
michael@0 3306 static bool
michael@0 3307 EmitDestructuringOps(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool isLet = false)
michael@0 3308 {
michael@0 3309 /*
michael@0 3310 * Call our recursive helper to emit the destructuring assignments and
michael@0 3311 * related stack manipulations.
michael@0 3312 */
michael@0 3313 VarEmitOption emitOption = isLet ? PushInitialValues : InitializeVars;
michael@0 3314 return EmitDestructuringOpsHelper(cx, bce, pn, emitOption);
michael@0 3315 }
michael@0 3316
michael@0 3317 static bool
michael@0 3318 EmitGroupAssignment(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp prologOp,
michael@0 3319 ParseNode *lhs, ParseNode *rhs)
michael@0 3320 {
michael@0 3321 uint32_t depth, limit, i, nslots;
michael@0 3322 ParseNode *pn;
michael@0 3323
michael@0 3324 depth = limit = (uint32_t) bce->stackDepth;
michael@0 3325 for (pn = rhs->pn_head; pn; pn = pn->pn_next) {
michael@0 3326 if (limit == JS_BIT(16)) {
michael@0 3327 bce->reportError(rhs, JSMSG_ARRAY_INIT_TOO_BIG);
michael@0 3328 return false;
michael@0 3329 }
michael@0 3330
michael@0 3331 /* MaybeEmitGroupAssignment won't call us if rhs is holey. */
michael@0 3332 JS_ASSERT(!pn->isKind(PNK_ELISION));
michael@0 3333 if (!EmitTree(cx, bce, pn))
michael@0 3334 return false;
michael@0 3335 ++limit;
michael@0 3336 }
michael@0 3337
michael@0 3338 i = depth;
michael@0 3339 for (pn = lhs->pn_head; pn; pn = pn->pn_next, ++i) {
michael@0 3340 /* MaybeEmitGroupAssignment requires lhs->pn_count <= rhs->pn_count. */
michael@0 3341 JS_ASSERT(i < limit);
michael@0 3342
michael@0 3343 if (!EmitDupAt(cx, bce, i))
michael@0 3344 return false;
michael@0 3345
michael@0 3346 if (pn->isKind(PNK_ELISION)) {
michael@0 3347 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 3348 return false;
michael@0 3349 } else {
michael@0 3350 if (!EmitDestructuringLHS(cx, bce, pn, InitializeVars))
michael@0 3351 return false;
michael@0 3352 }
michael@0 3353 }
michael@0 3354
michael@0 3355 nslots = limit - depth;
michael@0 3356 EMIT_UINT16_IMM_OP(JSOP_POPN, nslots);
michael@0 3357 bce->stackDepth = (uint32_t) depth;
michael@0 3358 return true;
michael@0 3359 }
michael@0 3360
michael@0 3361 enum GroupOption { GroupIsDecl, GroupIsNotDecl };
michael@0 3362
michael@0 3363 /*
michael@0 3364 * Helper called with pop out param initialized to a JSOP_POP* opcode. If we
michael@0 3365 * can emit a group assignment sequence, which results in 0 stack depth delta,
michael@0 3366 * we set *pop to JSOP_NOP so callers can veto emitting pn followed by a pop.
michael@0 3367 */
michael@0 3368 static bool
michael@0 3369 MaybeEmitGroupAssignment(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn,
michael@0 3370 GroupOption groupOption, JSOp *pop)
michael@0 3371 {
michael@0 3372 JS_ASSERT(pn->isKind(PNK_ASSIGN));
michael@0 3373 JS_ASSERT(pn->isOp(JSOP_NOP));
michael@0 3374 JS_ASSERT(*pop == JSOP_POP || *pop == JSOP_SETRVAL);
michael@0 3375
michael@0 3376 ParseNode *lhs = pn->pn_left;
michael@0 3377 ParseNode *rhs = pn->pn_right;
michael@0 3378 if (lhs->isKind(PNK_ARRAY) && rhs->isKind(PNK_ARRAY) &&
michael@0 3379 !(rhs->pn_xflags & PNX_SPECIALARRAYINIT) &&
michael@0 3380 lhs->pn_count <= rhs->pn_count)
michael@0 3381 {
michael@0 3382 if (groupOption == GroupIsDecl && !EmitDestructuringDecls(cx, bce, prologOp, lhs))
michael@0 3383 return false;
michael@0 3384 if (!EmitGroupAssignment(cx, bce, prologOp, lhs, rhs))
michael@0 3385 return false;
michael@0 3386 *pop = JSOP_NOP;
michael@0 3387 }
michael@0 3388 return true;
michael@0 3389 }
michael@0 3390
michael@0 3391 /*
michael@0 3392 * Like MaybeEmitGroupAssignment, but for 'let ([x,y] = [a,b]) ...'.
michael@0 3393 *
michael@0 3394 * Instead of issuing a sequence |dup|eval-rhs|set-lhs|pop| (which doesn't work
michael@0 3395 * since the bound vars don't yet have slots), just eval/push each rhs element
michael@0 3396 * just like what EmitLet would do for 'let (x = a, y = b) ...'. While shorter,
michael@0 3397 * simpler and more efficient than MaybeEmitGroupAssignment, it is harder to
michael@0 3398 * decompile so we restrict the ourselves to cases where the lhs and rhs are in
michael@0 3399 * 1:1 correspondence and lhs elements are simple names.
michael@0 3400 */
michael@0 3401 static bool
michael@0 3402 MaybeEmitLetGroupDecl(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSOp *pop)
michael@0 3403 {
michael@0 3404 JS_ASSERT(pn->isKind(PNK_ASSIGN));
michael@0 3405 JS_ASSERT(pn->isOp(JSOP_NOP));
michael@0 3406 JS_ASSERT(*pop == JSOP_POP || *pop == JSOP_SETRVAL);
michael@0 3407
michael@0 3408 ParseNode *lhs = pn->pn_left;
michael@0 3409 ParseNode *rhs = pn->pn_right;
michael@0 3410 if (lhs->isKind(PNK_ARRAY) && rhs->isKind(PNK_ARRAY) &&
michael@0 3411 !(rhs->pn_xflags & PNX_SPECIALARRAYINIT) &&
michael@0 3412 !(lhs->pn_xflags & PNX_SPECIALARRAYINIT) &&
michael@0 3413 lhs->pn_count == rhs->pn_count)
michael@0 3414 {
michael@0 3415 for (ParseNode *l = lhs->pn_head; l; l = l->pn_next) {
michael@0 3416 if (l->getOp() != JSOP_SETLOCAL)
michael@0 3417 return true;
michael@0 3418 }
michael@0 3419
michael@0 3420 for (ParseNode *r = rhs->pn_head; r; r = r->pn_next) {
michael@0 3421 if (!EmitTree(cx, bce, r))
michael@0 3422 return false;
michael@0 3423 }
michael@0 3424
michael@0 3425 *pop = JSOP_NOP;
michael@0 3426 }
michael@0 3427 return true;
michael@0 3428 }
michael@0 3429
michael@0 3430 static bool
michael@0 3431 EmitVariables(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, VarEmitOption emitOption,
michael@0 3432 bool isLet = false)
michael@0 3433 {
michael@0 3434 JS_ASSERT(pn->isArity(PN_LIST));
michael@0 3435 JS_ASSERT(isLet == (emitOption == PushInitialValues));
michael@0 3436
michael@0 3437 ParseNode *next;
michael@0 3438 for (ParseNode *pn2 = pn->pn_head; ; pn2 = next) {
michael@0 3439 if (!UpdateSourceCoordNotes(cx, bce, pn2->pn_pos.begin))
michael@0 3440 return false;
michael@0 3441 next = pn2->pn_next;
michael@0 3442
michael@0 3443 ParseNode *pn3;
michael@0 3444 if (!pn2->isKind(PNK_NAME)) {
michael@0 3445 if (pn2->isKind(PNK_ARRAY) || pn2->isKind(PNK_OBJECT)) {
michael@0 3446 /*
michael@0 3447 * Emit variable binding ops, but not destructuring ops. The
michael@0 3448 * parser (see Parser::variables) has ensured that our caller
michael@0 3449 * will be the PNK_FOR/PNK_FORIN/PNK_FOROF case in EmitTree, and
michael@0 3450 * that case will emit the destructuring code only after
michael@0 3451 * emitting an enumerating opcode and a branch that tests
michael@0 3452 * whether the enumeration ended.
michael@0 3453 */
michael@0 3454 JS_ASSERT(emitOption == DefineVars);
michael@0 3455 JS_ASSERT(pn->pn_count == 1);
michael@0 3456 if (!EmitDestructuringDecls(cx, bce, pn->getOp(), pn2))
michael@0 3457 return false;
michael@0 3458 break;
michael@0 3459 }
michael@0 3460
michael@0 3461 /*
michael@0 3462 * A destructuring initialiser assignment preceded by var will
michael@0 3463 * never occur to the left of 'in' in a for-in loop. As with 'for
michael@0 3464 * (var x = i in o)...', this will cause the entire 'var [a, b] =
michael@0 3465 * i' to be hoisted out of the loop.
michael@0 3466 */
michael@0 3467 JS_ASSERT(pn2->isKind(PNK_ASSIGN));
michael@0 3468 JS_ASSERT(pn2->isOp(JSOP_NOP));
michael@0 3469 JS_ASSERT(emitOption != DefineVars);
michael@0 3470
michael@0 3471 /*
michael@0 3472 * To allow the front end to rewrite var f = x; as f = x; when a
michael@0 3473 * function f(){} precedes the var, detect simple name assignment
michael@0 3474 * here and initialize the name.
michael@0 3475 */
michael@0 3476 if (pn2->pn_left->isKind(PNK_NAME)) {
michael@0 3477 pn3 = pn2->pn_right;
michael@0 3478 pn2 = pn2->pn_left;
michael@0 3479 goto do_name;
michael@0 3480 }
michael@0 3481
michael@0 3482 JSOp op = JSOP_POP;
michael@0 3483 if (pn->pn_count == 1) {
michael@0 3484 /*
michael@0 3485 * If this is the only destructuring assignment in the list,
michael@0 3486 * try to optimize to a group assignment. If we're in a let
michael@0 3487 * head, pass JSOP_POP rather than the pseudo-prolog JSOP_NOP
michael@0 3488 * in pn->pn_op, to suppress a second (and misplaced) 'let'.
michael@0 3489 */
michael@0 3490 JS_ASSERT(!pn2->pn_next);
michael@0 3491 if (isLet) {
michael@0 3492 if (!MaybeEmitLetGroupDecl(cx, bce, pn2, &op))
michael@0 3493 return false;
michael@0 3494 } else {
michael@0 3495 if (!MaybeEmitGroupAssignment(cx, bce, pn->getOp(), pn2, GroupIsDecl, &op))
michael@0 3496 return false;
michael@0 3497 }
michael@0 3498 }
michael@0 3499 if (op == JSOP_NOP) {
michael@0 3500 pn->pn_xflags = (pn->pn_xflags & ~PNX_POPVAR) | PNX_GROUPINIT;
michael@0 3501 } else {
michael@0 3502 pn3 = pn2->pn_left;
michael@0 3503 if (!EmitDestructuringDecls(cx, bce, pn->getOp(), pn3))
michael@0 3504 return false;
michael@0 3505
michael@0 3506 if (!EmitTree(cx, bce, pn2->pn_right))
michael@0 3507 return false;
michael@0 3508
michael@0 3509 if (!EmitDestructuringOps(cx, bce, pn3, isLet))
michael@0 3510 return false;
michael@0 3511 }
michael@0 3512
michael@0 3513 /* If we are not initializing, nothing to pop. */
michael@0 3514 if (emitOption != InitializeVars) {
michael@0 3515 if (next)
michael@0 3516 continue;
michael@0 3517 break;
michael@0 3518 }
michael@0 3519 goto emit_note_pop;
michael@0 3520 }
michael@0 3521
michael@0 3522 /*
michael@0 3523 * Load initializer early to share code above that jumps to do_name.
michael@0 3524 * NB: if this var redeclares an existing binding, then pn2 is linked
michael@0 3525 * on its definition's use-chain and pn_expr has been overlayed with
michael@0 3526 * pn_lexdef.
michael@0 3527 */
michael@0 3528 pn3 = pn2->maybeExpr();
michael@0 3529
michael@0 3530 do_name:
michael@0 3531 if (!BindNameToSlot(cx, bce, pn2))
michael@0 3532 return false;
michael@0 3533
michael@0 3534
michael@0 3535 JSOp op;
michael@0 3536 op = pn2->getOp();
michael@0 3537 JS_ASSERT(op != JSOP_CALLEE);
michael@0 3538 JS_ASSERT(!pn2->pn_cookie.isFree() || !pn->isOp(JSOP_NOP));
michael@0 3539
michael@0 3540 jsatomid atomIndex;
michael@0 3541 if (!MaybeEmitVarDecl(cx, bce, pn->getOp(), pn2, &atomIndex))
michael@0 3542 return false;
michael@0 3543
michael@0 3544 if (pn3) {
michael@0 3545 JS_ASSERT(emitOption != DefineVars);
michael@0 3546 if (op == JSOP_SETNAME || op == JSOP_SETGNAME || op == JSOP_SETINTRINSIC) {
michael@0 3547 JS_ASSERT(emitOption != PushInitialValues);
michael@0 3548 JSOp bindOp;
michael@0 3549 if (op == JSOP_SETNAME)
michael@0 3550 bindOp = JSOP_BINDNAME;
michael@0 3551 else if (op == JSOP_SETGNAME)
michael@0 3552 bindOp = JSOP_BINDGNAME;
michael@0 3553 else
michael@0 3554 bindOp = JSOP_BINDINTRINSIC;
michael@0 3555 if (!EmitIndex32(cx, bindOp, atomIndex, bce))
michael@0 3556 return false;
michael@0 3557 }
michael@0 3558
michael@0 3559 bool oldEmittingForInit = bce->emittingForInit;
michael@0 3560 bce->emittingForInit = false;
michael@0 3561 if (!EmitTree(cx, bce, pn3))
michael@0 3562 return false;
michael@0 3563 bce->emittingForInit = oldEmittingForInit;
michael@0 3564 } else if (isLet) {
michael@0 3565 /* JSOP_ENTERLETx expects at least 1 slot to have been pushed. */
michael@0 3566 if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
michael@0 3567 return false;
michael@0 3568 }
michael@0 3569
michael@0 3570 /* If we are not initializing, nothing to pop. */
michael@0 3571 if (emitOption != InitializeVars) {
michael@0 3572 if (next)
michael@0 3573 continue;
michael@0 3574 break;
michael@0 3575 }
michael@0 3576
michael@0 3577 JS_ASSERT_IF(pn2->isDefn(), pn3 == pn2->pn_expr);
michael@0 3578 if (!pn2->pn_cookie.isFree()) {
michael@0 3579 if (!EmitVarOp(cx, pn2, op, bce))
michael@0 3580 return false;
michael@0 3581 } else {
michael@0 3582 if (!EmitIndexOp(cx, op, atomIndex, bce))
michael@0 3583 return false;
michael@0 3584 }
michael@0 3585
michael@0 3586 emit_note_pop:
michael@0 3587 if (!next)
michael@0 3588 break;
michael@0 3589 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 3590 return false;
michael@0 3591 }
michael@0 3592
michael@0 3593 if (pn->pn_xflags & PNX_POPVAR) {
michael@0 3594 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 3595 return false;
michael@0 3596 }
michael@0 3597
michael@0 3598 return true;
michael@0 3599 }
michael@0 3600
michael@0 3601 static bool
michael@0 3602 EmitAssignment(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *lhs, JSOp op, ParseNode *rhs)
michael@0 3603 {
michael@0 3604 /*
michael@0 3605 * Check left operand type and generate specialized code for it.
michael@0 3606 * Specialize to avoid ECMA "reference type" values on the operand
michael@0 3607 * stack, which impose pervasive runtime "GetValue" costs.
michael@0 3608 */
michael@0 3609 jsatomid atomIndex = (jsatomid) -1;
michael@0 3610 jsbytecode offset = 1;
michael@0 3611
michael@0 3612 switch (lhs->getKind()) {
michael@0 3613 case PNK_NAME:
michael@0 3614 if (!BindNameToSlot(cx, bce, lhs))
michael@0 3615 return false;
michael@0 3616 if (lhs->pn_cookie.isFree()) {
michael@0 3617 if (!bce->makeAtomIndex(lhs->pn_atom, &atomIndex))
michael@0 3618 return false;
michael@0 3619 if (!lhs->isConst()) {
michael@0 3620 JSOp bindOp;
michael@0 3621 if (lhs->isOp(JSOP_SETNAME))
michael@0 3622 bindOp = JSOP_BINDNAME;
michael@0 3623 else if (lhs->isOp(JSOP_SETGNAME))
michael@0 3624 bindOp = JSOP_BINDGNAME;
michael@0 3625 else
michael@0 3626 bindOp = JSOP_BINDINTRINSIC;
michael@0 3627 if (!EmitIndex32(cx, bindOp, atomIndex, bce))
michael@0 3628 return false;
michael@0 3629 offset++;
michael@0 3630 }
michael@0 3631 }
michael@0 3632 break;
michael@0 3633 case PNK_DOT:
michael@0 3634 if (!EmitTree(cx, bce, lhs->expr()))
michael@0 3635 return false;
michael@0 3636 offset++;
michael@0 3637 if (!bce->makeAtomIndex(lhs->pn_atom, &atomIndex))
michael@0 3638 return false;
michael@0 3639 break;
michael@0 3640 case PNK_ELEM:
michael@0 3641 JS_ASSERT(lhs->isArity(PN_BINARY));
michael@0 3642 if (!EmitTree(cx, bce, lhs->pn_left))
michael@0 3643 return false;
michael@0 3644 if (!EmitTree(cx, bce, lhs->pn_right))
michael@0 3645 return false;
michael@0 3646 offset += 2;
michael@0 3647 break;
michael@0 3648 case PNK_ARRAY:
michael@0 3649 case PNK_OBJECT:
michael@0 3650 break;
michael@0 3651 case PNK_CALL:
michael@0 3652 JS_ASSERT(lhs->pn_xflags & PNX_SETCALL);
michael@0 3653 if (!EmitTree(cx, bce, lhs))
michael@0 3654 return false;
michael@0 3655 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 3656 return false;
michael@0 3657 break;
michael@0 3658 default:
michael@0 3659 JS_ASSERT(0);
michael@0 3660 }
michael@0 3661
michael@0 3662 if (op != JSOP_NOP) {
michael@0 3663 JS_ASSERT(rhs);
michael@0 3664 switch (lhs->getKind()) {
michael@0 3665 case PNK_NAME:
michael@0 3666 if (lhs->isConst()) {
michael@0 3667 if (lhs->isOp(JSOP_CALLEE)) {
michael@0 3668 if (Emit1(cx, bce, JSOP_CALLEE) < 0)
michael@0 3669 return false;
michael@0 3670 } else if (lhs->isOp(JSOP_NAME) || lhs->isOp(JSOP_GETGNAME)) {
michael@0 3671 if (!EmitIndex32(cx, lhs->getOp(), atomIndex, bce))
michael@0 3672 return false;
michael@0 3673 } else {
michael@0 3674 JS_ASSERT(JOF_OPTYPE(lhs->getOp()) != JOF_ATOM);
michael@0 3675 if (!EmitVarOp(cx, lhs, lhs->getOp(), bce))
michael@0 3676 return false;
michael@0 3677 }
michael@0 3678 } else if (lhs->isOp(JSOP_SETNAME)) {
michael@0 3679 if (Emit1(cx, bce, JSOP_DUP) < 0)
michael@0 3680 return false;
michael@0 3681 if (!EmitIndex32(cx, JSOP_GETXPROP, atomIndex, bce))
michael@0 3682 return false;
michael@0 3683 } else if (lhs->isOp(JSOP_SETGNAME)) {
michael@0 3684 JS_ASSERT(lhs->pn_cookie.isFree());
michael@0 3685 if (!EmitAtomOp(cx, lhs, JSOP_GETGNAME, bce))
michael@0 3686 return false;
michael@0 3687 } else if (lhs->isOp(JSOP_SETINTRINSIC)) {
michael@0 3688 JS_ASSERT(lhs->pn_cookie.isFree());
michael@0 3689 if (!EmitAtomOp(cx, lhs, JSOP_GETINTRINSIC, bce))
michael@0 3690 return false;
michael@0 3691 } else {
michael@0 3692 JSOp op;
michael@0 3693 switch (lhs->getOp()) {
michael@0 3694 case JSOP_SETARG: op = JSOP_GETARG; break;
michael@0 3695 case JSOP_SETLOCAL: op = JSOP_GETLOCAL; break;
michael@0 3696 case JSOP_SETALIASEDVAR: op = JSOP_GETALIASEDVAR; break;
michael@0 3697 default: MOZ_ASSUME_UNREACHABLE("Bad op");
michael@0 3698 }
michael@0 3699 if (!EmitVarOp(cx, lhs, op, bce))
michael@0 3700 return false;
michael@0 3701 }
michael@0 3702 break;
michael@0 3703 case PNK_DOT: {
michael@0 3704 if (Emit1(cx, bce, JSOP_DUP) < 0)
michael@0 3705 return false;
michael@0 3706 bool isLength = (lhs->pn_atom == cx->names().length);
michael@0 3707 if (!EmitIndex32(cx, isLength ? JSOP_LENGTH : JSOP_GETPROP, atomIndex, bce))
michael@0 3708 return false;
michael@0 3709 break;
michael@0 3710 }
michael@0 3711 case PNK_ELEM:
michael@0 3712 if (Emit1(cx, bce, JSOP_DUP2) < 0)
michael@0 3713 return false;
michael@0 3714 if (!EmitElemOpBase(cx, bce, JSOP_GETELEM))
michael@0 3715 return false;
michael@0 3716 break;
michael@0 3717 case PNK_CALL:
michael@0 3718 /*
michael@0 3719 * We just emitted a JSOP_SETCALL (which will always throw) and
michael@0 3720 * popped the call's return value. Push a random value to make sure
michael@0 3721 * the stack depth is correct.
michael@0 3722 */
michael@0 3723 JS_ASSERT(lhs->pn_xflags & PNX_SETCALL);
michael@0 3724 if (Emit1(cx, bce, JSOP_NULL) < 0)
michael@0 3725 return false;
michael@0 3726 break;
michael@0 3727 default:;
michael@0 3728 }
michael@0 3729 }
michael@0 3730
michael@0 3731 /* Now emit the right operand (it may affect the namespace). */
michael@0 3732 if (rhs) {
michael@0 3733 if (!EmitTree(cx, bce, rhs))
michael@0 3734 return false;
michael@0 3735 } else {
michael@0 3736 /*
michael@0 3737 * The value to assign is the next enumeration value in a for-in or
michael@0 3738 * for-of loop. That value has already been emitted: by JSOP_ITERNEXT
michael@0 3739 * in the for-in case, or via a GETPROP "value" on the result object in
michael@0 3740 * the for-of case. If offset == 1, that slot is already at the top of
michael@0 3741 * the stack. Otherwise, rearrange the stack to put that value on top.
michael@0 3742 */
michael@0 3743 if (offset != 1 && Emit2(cx, bce, JSOP_PICK, offset - 1) < 0)
michael@0 3744 return false;
michael@0 3745 }
michael@0 3746
michael@0 3747 /* If += etc., emit the binary operator with a source note. */
michael@0 3748 if (op != JSOP_NOP) {
michael@0 3749 /*
michael@0 3750 * Take care to avoid SRC_ASSIGNOP if the left-hand side is a const
michael@0 3751 * declared in the current compilation unit, as in this case (just
michael@0 3752 * a bit further below) we will avoid emitting the assignment op.
michael@0 3753 */
michael@0 3754 if (!lhs->isKind(PNK_NAME) || !lhs->isConst()) {
michael@0 3755 if (NewSrcNote(cx, bce, SRC_ASSIGNOP) < 0)
michael@0 3756 return false;
michael@0 3757 }
michael@0 3758 if (Emit1(cx, bce, op) < 0)
michael@0 3759 return false;
michael@0 3760 }
michael@0 3761
michael@0 3762 /* Finally, emit the specialized assignment bytecode. */
michael@0 3763 switch (lhs->getKind()) {
michael@0 3764 case PNK_NAME:
michael@0 3765 if (lhs->isConst()) {
michael@0 3766 if (!rhs) {
michael@0 3767 bce->reportError(lhs, JSMSG_BAD_FOR_LEFTSIDE);
michael@0 3768 return false;
michael@0 3769 }
michael@0 3770 break;
michael@0 3771 }
michael@0 3772 if (lhs->isOp(JSOP_SETARG) || lhs->isOp(JSOP_SETLOCAL) || lhs->isOp(JSOP_SETALIASEDVAR)) {
michael@0 3773 if (!EmitVarOp(cx, lhs, lhs->getOp(), bce))
michael@0 3774 return false;
michael@0 3775 } else {
michael@0 3776 if (!EmitIndexOp(cx, lhs->getOp(), atomIndex, bce))
michael@0 3777 return false;
michael@0 3778 }
michael@0 3779 break;
michael@0 3780 case PNK_DOT:
michael@0 3781 if (!EmitIndexOp(cx, JSOP_SETPROP, atomIndex, bce))
michael@0 3782 return false;
michael@0 3783 break;
michael@0 3784 case PNK_CALL:
michael@0 3785 /* Do nothing. The JSOP_SETCALL we emitted will always throw. */
michael@0 3786 JS_ASSERT(lhs->pn_xflags & PNX_SETCALL);
michael@0 3787 break;
michael@0 3788 case PNK_ELEM:
michael@0 3789 if (Emit1(cx, bce, JSOP_SETELEM) < 0)
michael@0 3790 return false;
michael@0 3791 break;
michael@0 3792 case PNK_ARRAY:
michael@0 3793 case PNK_OBJECT:
michael@0 3794 if (!EmitDestructuringOps(cx, bce, lhs))
michael@0 3795 return false;
michael@0 3796 break;
michael@0 3797 default:
michael@0 3798 JS_ASSERT(0);
michael@0 3799 }
michael@0 3800 return true;
michael@0 3801 }
michael@0 3802
michael@0 3803 bool
michael@0 3804 ParseNode::getConstantValue(ExclusiveContext *cx, bool strictChecks, MutableHandleValue vp)
michael@0 3805 {
michael@0 3806 switch (getKind()) {
michael@0 3807 case PNK_NUMBER:
michael@0 3808 vp.setNumber(pn_dval);
michael@0 3809 return true;
michael@0 3810 case PNK_STRING:
michael@0 3811 vp.setString(pn_atom);
michael@0 3812 return true;
michael@0 3813 case PNK_TRUE:
michael@0 3814 vp.setBoolean(true);
michael@0 3815 return true;
michael@0 3816 case PNK_FALSE:
michael@0 3817 vp.setBoolean(false);
michael@0 3818 return true;
michael@0 3819 case PNK_NULL:
michael@0 3820 vp.setNull();
michael@0 3821 return true;
michael@0 3822 case PNK_SPREAD:
michael@0 3823 return false;
michael@0 3824 case PNK_ARRAY: {
michael@0 3825 JS_ASSERT(isOp(JSOP_NEWINIT) && !(pn_xflags & PNX_NONCONST));
michael@0 3826
michael@0 3827 RootedObject obj(cx,
michael@0 3828 NewDenseAllocatedArray(cx, pn_count, nullptr, MaybeSingletonObject));
michael@0 3829 if (!obj)
michael@0 3830 return false;
michael@0 3831
michael@0 3832 unsigned idx = 0;
michael@0 3833 RootedId id(cx);
michael@0 3834 RootedValue value(cx);
michael@0 3835 for (ParseNode *pn = pn_head; pn; idx++, pn = pn->pn_next) {
michael@0 3836 if (!pn->getConstantValue(cx, strictChecks, &value))
michael@0 3837 return false;
michael@0 3838 id = INT_TO_JSID(idx);
michael@0 3839 if (!JSObject::defineGeneric(cx, obj, id, value, nullptr, nullptr, JSPROP_ENUMERATE))
michael@0 3840 return false;
michael@0 3841 }
michael@0 3842 JS_ASSERT(idx == pn_count);
michael@0 3843
michael@0 3844 types::FixArrayType(cx, obj);
michael@0 3845 vp.setObject(*obj);
michael@0 3846 return true;
michael@0 3847 }
michael@0 3848 case PNK_OBJECT: {
michael@0 3849 JS_ASSERT(isOp(JSOP_NEWINIT));
michael@0 3850 JS_ASSERT(!(pn_xflags & PNX_NONCONST));
michael@0 3851
michael@0 3852 gc::AllocKind kind = GuessObjectGCKind(pn_count);
michael@0 3853 RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_, kind, MaybeSingletonObject));
michael@0 3854 if (!obj)
michael@0 3855 return false;
michael@0 3856
michael@0 3857 RootedValue value(cx), idvalue(cx);
michael@0 3858 for (ParseNode *pn = pn_head; pn; pn = pn->pn_next) {
michael@0 3859 if (!pn->pn_right->getConstantValue(cx, strictChecks, &value))
michael@0 3860 return false;
michael@0 3861
michael@0 3862 ParseNode *pnid = pn->pn_left;
michael@0 3863 if (pnid->isKind(PNK_NUMBER)) {
michael@0 3864 idvalue = NumberValue(pnid->pn_dval);
michael@0 3865 } else {
michael@0 3866 JS_ASSERT(pnid->isKind(PNK_NAME) || pnid->isKind(PNK_STRING));
michael@0 3867 JS_ASSERT(pnid->pn_atom != cx->names().proto);
michael@0 3868 idvalue = StringValue(pnid->pn_atom);
michael@0 3869 }
michael@0 3870
michael@0 3871 uint32_t index;
michael@0 3872 if (IsDefinitelyIndex(idvalue, &index)) {
michael@0 3873 if (!JSObject::defineElement(cx, obj, index, value, nullptr, nullptr,
michael@0 3874 JSPROP_ENUMERATE))
michael@0 3875 {
michael@0 3876 return false;
michael@0 3877 }
michael@0 3878
michael@0 3879 continue;
michael@0 3880 }
michael@0 3881
michael@0 3882 JSAtom *name = ToAtom<CanGC>(cx, idvalue);
michael@0 3883 if (!name)
michael@0 3884 return false;
michael@0 3885
michael@0 3886 if (name->isIndex(&index)) {
michael@0 3887 if (!JSObject::defineElement(cx, obj, index, value,
michael@0 3888 nullptr, nullptr, JSPROP_ENUMERATE))
michael@0 3889 return false;
michael@0 3890 } else {
michael@0 3891 if (!JSObject::defineProperty(cx, obj, name->asPropertyName(), value,
michael@0 3892 nullptr, nullptr, JSPROP_ENUMERATE))
michael@0 3893 {
michael@0 3894 return false;
michael@0 3895 }
michael@0 3896 }
michael@0 3897 }
michael@0 3898
michael@0 3899 types::FixObjectType(cx, obj);
michael@0 3900 vp.setObject(*obj);
michael@0 3901 return true;
michael@0 3902 }
michael@0 3903 default:
michael@0 3904 MOZ_ASSUME_UNREACHABLE("Unexpected node");
michael@0 3905 }
michael@0 3906 return false;
michael@0 3907 }
michael@0 3908
michael@0 3909 static bool
michael@0 3910 EmitSingletonInitialiser(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
michael@0 3911 {
michael@0 3912 RootedValue value(cx);
michael@0 3913 if (!pn->getConstantValue(cx, bce->sc->needStrictChecks(), &value))
michael@0 3914 return false;
michael@0 3915
michael@0 3916 JS_ASSERT(value.isObject());
michael@0 3917 ObjectBox *objbox = bce->parser->newObjectBox(&value.toObject());
michael@0 3918 if (!objbox)
michael@0 3919 return false;
michael@0 3920
michael@0 3921 return EmitObjectOp(cx, objbox, JSOP_OBJECT, bce);
michael@0 3922 }
michael@0 3923
michael@0 3924 /* See the SRC_FOR source note offsetBias comments later in this file. */
michael@0 3925 JS_STATIC_ASSERT(JSOP_NOP_LENGTH == 1);
michael@0 3926 JS_STATIC_ASSERT(JSOP_POP_LENGTH == 1);
michael@0 3927
michael@0 3928 namespace {
michael@0 3929
michael@0 3930 class EmitLevelManager
michael@0 3931 {
michael@0 3932 BytecodeEmitter *bce;
michael@0 3933 public:
michael@0 3934 EmitLevelManager(BytecodeEmitter *bce) : bce(bce) { bce->emitLevel++; }
michael@0 3935 ~EmitLevelManager() { bce->emitLevel--; }
michael@0 3936 };
michael@0 3937
michael@0 3938 } /* anonymous namespace */
michael@0 3939
michael@0 3940 static bool
michael@0 3941 EmitCatch(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
michael@0 3942 {
michael@0 3943 /*
michael@0 3944 * Morph STMT_BLOCK to STMT_CATCH, note the block entry code offset,
michael@0 3945 * and save the block object atom.
michael@0 3946 */
michael@0 3947 StmtInfoBCE *stmt = bce->topStmt;
michael@0 3948 JS_ASSERT(stmt->type == STMT_BLOCK && stmt->isBlockScope);
michael@0 3949 stmt->type = STMT_CATCH;
michael@0 3950
michael@0 3951 /* Go up one statement info record to the TRY or FINALLY record. */
michael@0 3952 stmt = stmt->down;
michael@0 3953 JS_ASSERT(stmt->type == STMT_TRY || stmt->type == STMT_FINALLY);
michael@0 3954
michael@0 3955 /* Pick up the pending exception and bind it to the catch variable. */
michael@0 3956 if (Emit1(cx, bce, JSOP_EXCEPTION) < 0)
michael@0 3957 return false;
michael@0 3958
michael@0 3959 /*
michael@0 3960 * Dup the exception object if there is a guard for rethrowing to use
michael@0 3961 * it later when rethrowing or in other catches.
michael@0 3962 */
michael@0 3963 if (pn->pn_kid2 && Emit1(cx, bce, JSOP_DUP) < 0)
michael@0 3964 return false;
michael@0 3965
michael@0 3966 ParseNode *pn2 = pn->pn_kid1;
michael@0 3967 switch (pn2->getKind()) {
michael@0 3968 case PNK_ARRAY:
michael@0 3969 case PNK_OBJECT:
michael@0 3970 if (!EmitDestructuringOps(cx, bce, pn2))
michael@0 3971 return false;
michael@0 3972 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 3973 return false;
michael@0 3974 break;
michael@0 3975
michael@0 3976 case PNK_NAME:
michael@0 3977 /* Inline and specialize BindNameToSlot for pn2. */
michael@0 3978 JS_ASSERT(!pn2->pn_cookie.isFree());
michael@0 3979 if (!EmitVarOp(cx, pn2, JSOP_SETLOCAL, bce))
michael@0 3980 return false;
michael@0 3981 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 3982 return false;
michael@0 3983 break;
michael@0 3984
michael@0 3985 default:
michael@0 3986 JS_ASSERT(0);
michael@0 3987 }
michael@0 3988
michael@0 3989 // If there is a guard expression, emit it and arrange to jump to the next
michael@0 3990 // catch block if the guard expression is false.
michael@0 3991 if (pn->pn_kid2) {
michael@0 3992 if (!EmitTree(cx, bce, pn->pn_kid2))
michael@0 3993 return false;
michael@0 3994
michael@0 3995 // If the guard expression is false, fall through, pop the block scope,
michael@0 3996 // and jump to the next catch block. Otherwise jump over that code and
michael@0 3997 // pop the dupped exception.
michael@0 3998 ptrdiff_t guardCheck = EmitJump(cx, bce, JSOP_IFNE, 0);
michael@0 3999 if (guardCheck < 0)
michael@0 4000 return false;
michael@0 4001
michael@0 4002 {
michael@0 4003 NonLocalExitScope nle(cx, bce);
michael@0 4004
michael@0 4005 // Move exception back to cx->exception to prepare for
michael@0 4006 // the next catch.
michael@0 4007 if (Emit1(cx, bce, JSOP_THROWING) < 0)
michael@0 4008 return false;
michael@0 4009
michael@0 4010 // Leave the scope for this catch block.
michael@0 4011 if (!nle.prepareForNonLocalJump(stmt))
michael@0 4012 return false;
michael@0 4013
michael@0 4014 // Jump to the next handler. The jump target is backpatched by EmitTry.
michael@0 4015 ptrdiff_t guardJump = EmitJump(cx, bce, JSOP_GOTO, 0);
michael@0 4016 if (guardJump < 0)
michael@0 4017 return false;
michael@0 4018 stmt->guardJump() = guardJump;
michael@0 4019 }
michael@0 4020
michael@0 4021 // Back to normal control flow.
michael@0 4022 SetJumpOffsetAt(bce, guardCheck);
michael@0 4023
michael@0 4024 // Pop duplicated exception object as we no longer need it.
michael@0 4025 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 4026 return false;
michael@0 4027 }
michael@0 4028
michael@0 4029 /* Emit the catch body. */
michael@0 4030 return EmitTree(cx, bce, pn->pn_kid3);
michael@0 4031 }
michael@0 4032
michael@0 4033 // Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See the
michael@0 4034 // comment on EmitSwitch.
michael@0 4035 //
michael@0 4036 MOZ_NEVER_INLINE static bool
michael@0 4037 EmitTry(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
michael@0 4038 {
michael@0 4039 StmtInfoBCE stmtInfo(cx);
michael@0 4040
michael@0 4041 // Push stmtInfo to track jumps-over-catches and gosubs-to-finally
michael@0 4042 // for later fixup.
michael@0 4043 //
michael@0 4044 // When a finally block is active (STMT_FINALLY in our parse context),
michael@0 4045 // non-local jumps (including jumps-over-catches) result in a GOSUB
michael@0 4046 // being written into the bytecode stream and fixed-up later (c.f.
michael@0 4047 // EmitBackPatchOp and BackPatch).
michael@0 4048 //
michael@0 4049 PushStatementBCE(bce, &stmtInfo, pn->pn_kid3 ? STMT_FINALLY : STMT_TRY, bce->offset());
michael@0 4050
michael@0 4051 // Since an exception can be thrown at any place inside the try block,
michael@0 4052 // we need to restore the stack and the scope chain before we transfer
michael@0 4053 // the control to the exception handler.
michael@0 4054 //
michael@0 4055 // For that we store in a try note associated with the catch or
michael@0 4056 // finally block the stack depth upon the try entry. The interpreter
michael@0 4057 // uses this depth to properly unwind the stack and the scope chain.
michael@0 4058 //
michael@0 4059 int depth = bce->stackDepth;
michael@0 4060
michael@0 4061 // Record the try location, then emit the try block.
michael@0 4062 ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_TRY);
michael@0 4063 if (noteIndex < 0 || Emit1(cx, bce, JSOP_TRY) < 0)
michael@0 4064 return false;
michael@0 4065 ptrdiff_t tryStart = bce->offset();
michael@0 4066 if (!EmitTree(cx, bce, pn->pn_kid1))
michael@0 4067 return false;
michael@0 4068 JS_ASSERT(depth == bce->stackDepth);
michael@0 4069
michael@0 4070 // GOSUB to finally, if present.
michael@0 4071 if (pn->pn_kid3) {
michael@0 4072 if (EmitBackPatchOp(cx, bce, &stmtInfo.gosubs()) < 0)
michael@0 4073 return false;
michael@0 4074 }
michael@0 4075
michael@0 4076 // Source note points to the jump at the end of the try block.
michael@0 4077 if (!SetSrcNoteOffset(cx, bce, noteIndex, 0, bce->offset() - tryStart + JSOP_TRY_LENGTH))
michael@0 4078 return false;
michael@0 4079
michael@0 4080 // Emit jump over catch and/or finally.
michael@0 4081 ptrdiff_t catchJump = -1;
michael@0 4082 if (EmitBackPatchOp(cx, bce, &catchJump) < 0)
michael@0 4083 return false;
michael@0 4084
michael@0 4085 ptrdiff_t tryEnd = bce->offset();
michael@0 4086
michael@0 4087 // If this try has a catch block, emit it.
michael@0 4088 if (ParseNode *pn2 = pn->pn_kid2) {
michael@0 4089 // The emitted code for a catch block looks like:
michael@0 4090 //
michael@0 4091 // [pushblockscope] only if any local aliased
michael@0 4092 // exception
michael@0 4093 // if there is a catchguard:
michael@0 4094 // dup
michael@0 4095 // setlocal 0; pop assign or possibly destructure exception
michael@0 4096 // if there is a catchguard:
michael@0 4097 // < catchguard code >
michael@0 4098 // ifne POST
michael@0 4099 // debugleaveblock
michael@0 4100 // [popblockscope] only if any local aliased
michael@0 4101 // throwing pop exception to cx->exception
michael@0 4102 // goto <next catch block>
michael@0 4103 // POST: pop
michael@0 4104 // < catch block contents >
michael@0 4105 // debugleaveblock
michael@0 4106 // [popblockscope] only if any local aliased
michael@0 4107 // goto <end of catch blocks> non-local; finally applies
michael@0 4108 //
michael@0 4109 // If there's no catch block without a catchguard, the last <next catch
michael@0 4110 // block> points to rethrow code. This code will [gosub] to the finally
michael@0 4111 // code if appropriate, and is also used for the catch-all trynote for
michael@0 4112 // capturing exceptions thrown from catch{} blocks.
michael@0 4113 //
michael@0 4114 for (ParseNode *pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
michael@0 4115 JS_ASSERT(bce->stackDepth == depth);
michael@0 4116
michael@0 4117 // Emit the lexical scope and catch body.
michael@0 4118 JS_ASSERT(pn3->isKind(PNK_LEXICALSCOPE));
michael@0 4119 if (!EmitTree(cx, bce, pn3))
michael@0 4120 return false;
michael@0 4121
michael@0 4122 // gosub <finally>, if required.
michael@0 4123 if (pn->pn_kid3) {
michael@0 4124 if (EmitBackPatchOp(cx, bce, &stmtInfo.gosubs()) < 0)
michael@0 4125 return false;
michael@0 4126 JS_ASSERT(bce->stackDepth == depth);
michael@0 4127 }
michael@0 4128
michael@0 4129 // Jump over the remaining catch blocks. This will get fixed
michael@0 4130 // up to jump to after catch/finally.
michael@0 4131 if (EmitBackPatchOp(cx, bce, &catchJump) < 0)
michael@0 4132 return false;
michael@0 4133
michael@0 4134 // If this catch block had a guard clause, patch the guard jump to
michael@0 4135 // come here.
michael@0 4136 if (stmtInfo.guardJump() != -1) {
michael@0 4137 SetJumpOffsetAt(bce, stmtInfo.guardJump());
michael@0 4138 stmtInfo.guardJump() = -1;
michael@0 4139
michael@0 4140 // If this catch block is the last one, rethrow, delegating
michael@0 4141 // execution of any finally block to the exception handler.
michael@0 4142 if (!pn3->pn_next) {
michael@0 4143 if (Emit1(cx, bce, JSOP_EXCEPTION) < 0)
michael@0 4144 return false;
michael@0 4145 if (Emit1(cx, bce, JSOP_THROW) < 0)
michael@0 4146 return false;
michael@0 4147 }
michael@0 4148 }
michael@0 4149 }
michael@0 4150 }
michael@0 4151
michael@0 4152 JS_ASSERT(bce->stackDepth == depth);
michael@0 4153
michael@0 4154 // Emit the finally handler, if there is one.
michael@0 4155 ptrdiff_t finallyStart = 0;
michael@0 4156 if (pn->pn_kid3) {
michael@0 4157 // Fix up the gosubs that might have been emitted before non-local
michael@0 4158 // jumps to the finally code.
michael@0 4159 if (!BackPatch(cx, bce, stmtInfo.gosubs(), bce->code().end(), JSOP_GOSUB))
michael@0 4160 return false;
michael@0 4161
michael@0 4162 finallyStart = bce->offset();
michael@0 4163
michael@0 4164 // Indicate that we're emitting a subroutine body.
michael@0 4165 stmtInfo.type = STMT_SUBROUTINE;
michael@0 4166 if (!UpdateSourceCoordNotes(cx, bce, pn->pn_kid3->pn_pos.begin))
michael@0 4167 return false;
michael@0 4168 if (Emit1(cx, bce, JSOP_FINALLY) < 0 ||
michael@0 4169 !EmitTree(cx, bce, pn->pn_kid3) ||
michael@0 4170 Emit1(cx, bce, JSOP_RETSUB) < 0)
michael@0 4171 {
michael@0 4172 return false;
michael@0 4173 }
michael@0 4174 JS_ASSERT(bce->stackDepth == depth);
michael@0 4175 }
michael@0 4176 if (!PopStatementBCE(cx, bce))
michael@0 4177 return false;
michael@0 4178
michael@0 4179 // ReconstructPCStack needs a NOP here to mark the end of the last catch block.
michael@0 4180 if (Emit1(cx, bce, JSOP_NOP) < 0)
michael@0 4181 return false;
michael@0 4182
michael@0 4183 // Fix up the end-of-try/catch jumps to come here.
michael@0 4184 if (!BackPatch(cx, bce, catchJump, bce->code().end(), JSOP_GOTO))
michael@0 4185 return false;
michael@0 4186
michael@0 4187 // Add the try note last, to let post-order give us the right ordering
michael@0 4188 // (first to last for a given nesting level, inner to outer by level).
michael@0 4189 if (pn->pn_kid2 && !bce->tryNoteList.append(JSTRY_CATCH, depth, tryStart, tryEnd))
michael@0 4190 return false;
michael@0 4191
michael@0 4192 // If we've got a finally, mark try+catch region with additional
michael@0 4193 // trynote to catch exceptions (re)thrown from a catch block or
michael@0 4194 // for the try{}finally{} case.
michael@0 4195 if (pn->pn_kid3 && !bce->tryNoteList.append(JSTRY_FINALLY, depth, tryStart, finallyStart))
michael@0 4196 return false;
michael@0 4197
michael@0 4198 return true;
michael@0 4199 }
michael@0 4200
michael@0 4201 static bool
michael@0 4202 EmitIf(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
michael@0 4203 {
michael@0 4204 StmtInfoBCE stmtInfo(cx);
michael@0 4205
michael@0 4206 /* Initialize so we can detect else-if chains and avoid recursion. */
michael@0 4207 stmtInfo.type = STMT_IF;
michael@0 4208 ptrdiff_t beq = -1;
michael@0 4209 ptrdiff_t jmp = -1;
michael@0 4210 ptrdiff_t noteIndex = -1;
michael@0 4211
michael@0 4212 if_again:
michael@0 4213 /* Emit code for the condition before pushing stmtInfo. */
michael@0 4214 if (!EmitTree(cx, bce, pn->pn_kid1))
michael@0 4215 return false;
michael@0 4216 ptrdiff_t top = bce->offset();
michael@0 4217 if (stmtInfo.type == STMT_IF) {
michael@0 4218 PushStatementBCE(bce, &stmtInfo, STMT_IF, top);
michael@0 4219 } else {
michael@0 4220 /*
michael@0 4221 * We came here from the goto further below that detects else-if
michael@0 4222 * chains, so we must mutate stmtInfo back into a STMT_IF record.
michael@0 4223 * Also we need a note offset for SRC_IF_ELSE to help IonMonkey.
michael@0 4224 */
michael@0 4225 JS_ASSERT(stmtInfo.type == STMT_ELSE);
michael@0 4226 stmtInfo.type = STMT_IF;
michael@0 4227 stmtInfo.update = top;
michael@0 4228 if (!SetSrcNoteOffset(cx, bce, noteIndex, 0, jmp - beq))
michael@0 4229 return false;
michael@0 4230 }
michael@0 4231
michael@0 4232 /* Emit an annotated branch-if-false around the then part. */
michael@0 4233 ParseNode *pn3 = pn->pn_kid3;
michael@0 4234 noteIndex = NewSrcNote(cx, bce, pn3 ? SRC_IF_ELSE : SRC_IF);
michael@0 4235 if (noteIndex < 0)
michael@0 4236 return false;
michael@0 4237 beq = EmitJump(cx, bce, JSOP_IFEQ, 0);
michael@0 4238 if (beq < 0)
michael@0 4239 return false;
michael@0 4240
michael@0 4241 /* Emit code for the then and optional else parts. */
michael@0 4242 if (!EmitTree(cx, bce, pn->pn_kid2))
michael@0 4243 return false;
michael@0 4244 if (pn3) {
michael@0 4245 /* Modify stmtInfo so we know we're in the else part. */
michael@0 4246 stmtInfo.type = STMT_ELSE;
michael@0 4247
michael@0 4248 /*
michael@0 4249 * Emit a JSOP_BACKPATCH op to jump from the end of our then part
michael@0 4250 * around the else part. The PopStatementBCE call at the bottom of
michael@0 4251 * this function will fix up the backpatch chain linked from
michael@0 4252 * stmtInfo.breaks.
michael@0 4253 */
michael@0 4254 jmp = EmitGoto(cx, bce, &stmtInfo, &stmtInfo.breaks);
michael@0 4255 if (jmp < 0)
michael@0 4256 return false;
michael@0 4257
michael@0 4258 /* Ensure the branch-if-false comes here, then emit the else. */
michael@0 4259 SetJumpOffsetAt(bce, beq);
michael@0 4260 if (pn3->isKind(PNK_IF)) {
michael@0 4261 pn = pn3;
michael@0 4262 goto if_again;
michael@0 4263 }
michael@0 4264
michael@0 4265 if (!EmitTree(cx, bce, pn3))
michael@0 4266 return false;
michael@0 4267
michael@0 4268 /*
michael@0 4269 * Annotate SRC_IF_ELSE with the offset from branch to jump, for
michael@0 4270 * IonMonkey's benefit. We can't just "back up" from the pc
michael@0 4271 * of the else clause, because we don't know whether an extended
michael@0 4272 * jump was required to leap from the end of the then clause over
michael@0 4273 * the else clause.
michael@0 4274 */
michael@0 4275 if (!SetSrcNoteOffset(cx, bce, noteIndex, 0, jmp - beq))
michael@0 4276 return false;
michael@0 4277 } else {
michael@0 4278 /* No else part, fixup the branch-if-false to come here. */
michael@0 4279 SetJumpOffsetAt(bce, beq);
michael@0 4280 }
michael@0 4281 return PopStatementBCE(cx, bce);
michael@0 4282 }
michael@0 4283
michael@0 4284 /*
michael@0 4285 * pnLet represents one of:
michael@0 4286 *
michael@0 4287 * let-expression: (let (x = y) EXPR)
michael@0 4288 * let-statement: let (x = y) { ... }
michael@0 4289 *
michael@0 4290 * For a let-expression 'let (x = a, [y,z] = b) e', EmitLet produces:
michael@0 4291 *
michael@0 4292 * bytecode stackDepth srcnotes
michael@0 4293 * evaluate a +1
michael@0 4294 * evaluate b +1
michael@0 4295 * dup +1
michael@0 4296 * destructure y
michael@0 4297 * pick 1
michael@0 4298 * dup +1
michael@0 4299 * destructure z
michael@0 4300 * pick 1
michael@0 4301 * pop -1
michael@0 4302 * setlocal 2 -1
michael@0 4303 * setlocal 1 -1
michael@0 4304 * setlocal 0 -1
michael@0 4305 * pushblockscope (if needed)
michael@0 4306 * evaluate e +1
michael@0 4307 * debugleaveblock
michael@0 4308 * popblockscope (if needed)
michael@0 4309 *
michael@0 4310 * Note that, since pushblockscope simply changes fp->scopeChain and does not
michael@0 4311 * otherwise touch the stack, evaluation of the let-var initializers must leave
michael@0 4312 * the initial value in the let-var's future slot.
michael@0 4313 */
michael@0 4314 /*
michael@0 4315 * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
michael@0 4316 * the comment on EmitSwitch.
michael@0 4317 */
michael@0 4318 MOZ_NEVER_INLINE static bool
michael@0 4319 EmitLet(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pnLet)
michael@0 4320 {
michael@0 4321 JS_ASSERT(pnLet->isArity(PN_BINARY));
michael@0 4322 ParseNode *varList = pnLet->pn_left;
michael@0 4323 JS_ASSERT(varList->isArity(PN_LIST));
michael@0 4324 ParseNode *letBody = pnLet->pn_right;
michael@0 4325 JS_ASSERT(letBody->isLet() && letBody->isKind(PNK_LEXICALSCOPE));
michael@0 4326
michael@0 4327 int letHeadDepth = bce->stackDepth;
michael@0 4328
michael@0 4329 if (!EmitVariables(cx, bce, varList, PushInitialValues, true))
michael@0 4330 return false;
michael@0 4331
michael@0 4332 /* Push storage for hoisted let decls (e.g. 'let (x) { let y }'). */
michael@0 4333 uint32_t alreadyPushed = bce->stackDepth - letHeadDepth;
michael@0 4334 StmtInfoBCE stmtInfo(cx);
michael@0 4335 if (!EnterBlockScope(cx, bce, &stmtInfo, letBody->pn_objbox, alreadyPushed))
michael@0 4336 return false;
michael@0 4337
michael@0 4338 if (!EmitTree(cx, bce, letBody->pn_expr))
michael@0 4339 return false;
michael@0 4340
michael@0 4341 if (!LeaveNestedScope(cx, bce, &stmtInfo))
michael@0 4342 return false;
michael@0 4343
michael@0 4344 return true;
michael@0 4345 }
michael@0 4346
michael@0 4347 /*
michael@0 4348 * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
michael@0 4349 * the comment on EmitSwitch.
michael@0 4350 */
michael@0 4351 MOZ_NEVER_INLINE static bool
michael@0 4352 EmitLexicalScope(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
michael@0 4353 {
michael@0 4354 JS_ASSERT(pn->isKind(PNK_LEXICALSCOPE));
michael@0 4355
michael@0 4356 StmtInfoBCE stmtInfo(cx);
michael@0 4357 if (!EnterBlockScope(cx, bce, &stmtInfo, pn->pn_objbox, 0))
michael@0 4358 return false;
michael@0 4359
michael@0 4360 if (!EmitTree(cx, bce, pn->pn_expr))
michael@0 4361 return false;
michael@0 4362
michael@0 4363 if (!LeaveNestedScope(cx, bce, &stmtInfo))
michael@0 4364 return false;
michael@0 4365
michael@0 4366 return true;
michael@0 4367 }
michael@0 4368
michael@0 4369 static bool
michael@0 4370 EmitWith(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
michael@0 4371 {
michael@0 4372 StmtInfoBCE stmtInfo(cx);
michael@0 4373 if (!EmitTree(cx, bce, pn->pn_left))
michael@0 4374 return false;
michael@0 4375 if (!EnterNestedScope(cx, bce, &stmtInfo, pn->pn_binary_obj, STMT_WITH))
michael@0 4376 return false;
michael@0 4377 if (!EmitTree(cx, bce, pn->pn_right))
michael@0 4378 return false;
michael@0 4379 if (!LeaveNestedScope(cx, bce, &stmtInfo))
michael@0 4380 return false;
michael@0 4381 return true;
michael@0 4382 }
michael@0 4383
michael@0 4384 static bool
michael@0 4385 EmitForOf(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
michael@0 4386 {
michael@0 4387 ParseNode *forHead = pn->pn_left;
michael@0 4388 ParseNode *forBody = pn->pn_right;
michael@0 4389
michael@0 4390 ParseNode *pn1 = forHead->pn_kid1;
michael@0 4391 bool letDecl = pn1 && pn1->isKind(PNK_LEXICALSCOPE);
michael@0 4392 JS_ASSERT_IF(letDecl, pn1->isLet());
michael@0 4393
michael@0 4394 // If the left part is 'var x', emit code to define x if necessary using a
michael@0 4395 // prolog opcode, but do not emit a pop.
michael@0 4396 if (pn1) {
michael@0 4397 ParseNode *decl = letDecl ? pn1->pn_expr : pn1;
michael@0 4398 JS_ASSERT(decl->isKind(PNK_VAR) || decl->isKind(PNK_LET));
michael@0 4399 bce->emittingForInit = true;
michael@0 4400 if (!EmitVariables(cx, bce, decl, DefineVars))
michael@0 4401 return false;
michael@0 4402 bce->emittingForInit = false;
michael@0 4403 }
michael@0 4404
michael@0 4405 // For-of loops run with two values on the stack: the iterator and the
michael@0 4406 // current result object.
michael@0 4407
michael@0 4408 // Compile the object expression to the right of 'of'.
michael@0 4409 if (!EmitTree(cx, bce, forHead->pn_kid3))
michael@0 4410 return false;
michael@0 4411
michael@0 4412 // Convert iterable to iterator.
michael@0 4413 if (Emit1(cx, bce, JSOP_DUP) < 0) // OBJ OBJ
michael@0 4414 return false;
michael@0 4415 if (!EmitAtomOp(cx, cx->names().std_iterator, JSOP_CALLPROP, bce)) // OBJ @@ITERATOR
michael@0 4416 return false;
michael@0 4417 if (Emit1(cx, bce, JSOP_SWAP) < 0) // @@ITERATOR OBJ
michael@0 4418 return false;
michael@0 4419 if (EmitCall(cx, bce, JSOP_CALL, 0) < 0) // ITER
michael@0 4420 return false;
michael@0 4421 CheckTypeSet(cx, bce, JSOP_CALL);
michael@0 4422
michael@0 4423 // Push a dummy result so that we properly enter iteration midstream.
michael@0 4424 if (Emit1(cx, bce, JSOP_UNDEFINED) < 0) // ITER RESULT
michael@0 4425 return false;
michael@0 4426
michael@0 4427 // Enter the block before the loop body, after evaluating the obj.
michael@0 4428 StmtInfoBCE letStmt(cx);
michael@0 4429 if (letDecl) {
michael@0 4430 if (!EnterBlockScope(cx, bce, &letStmt, pn1->pn_objbox, 0))
michael@0 4431 return false;
michael@0 4432 }
michael@0 4433
michael@0 4434 LoopStmtInfo stmtInfo(cx);
michael@0 4435 PushLoopStatement(bce, &stmtInfo, STMT_FOR_OF_LOOP, top);
michael@0 4436
michael@0 4437 // Jump down to the loop condition to minimize overhead assuming at least
michael@0 4438 // one iteration, as the other loop forms do. Annotate so IonMonkey can
michael@0 4439 // find the loop-closing jump.
michael@0 4440 int noteIndex = NewSrcNote(cx, bce, SRC_FOR_OF);
michael@0 4441 if (noteIndex < 0)
michael@0 4442 return false;
michael@0 4443 ptrdiff_t jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
michael@0 4444 if (jmp < 0)
michael@0 4445 return false;
michael@0 4446
michael@0 4447 top = bce->offset();
michael@0 4448 SET_STATEMENT_TOP(&stmtInfo, top);
michael@0 4449 if (EmitLoopHead(cx, bce, nullptr) < 0)
michael@0 4450 return false;
michael@0 4451
michael@0 4452 #ifdef DEBUG
michael@0 4453 int loopDepth = bce->stackDepth;
michael@0 4454 #endif
michael@0 4455
michael@0 4456 // Emit code to assign result.value to the iteration variable.
michael@0 4457 if (Emit1(cx, bce, JSOP_DUP) < 0) // ITER RESULT RESULT
michael@0 4458 return false;
michael@0 4459 if (!EmitAtomOp(cx, cx->names().value, JSOP_GETPROP, bce)) // ITER RESULT VALUE
michael@0 4460 return false;
michael@0 4461 if (!EmitAssignment(cx, bce, forHead->pn_kid2, JSOP_NOP, nullptr)) // ITER RESULT VALUE
michael@0 4462 return false;
michael@0 4463 if (Emit1(cx, bce, JSOP_POP) < 0) // ITER RESULT
michael@0 4464 return false;
michael@0 4465
michael@0 4466 // The stack should be balanced around the assignment opcode sequence.
michael@0 4467 JS_ASSERT(bce->stackDepth == loopDepth);
michael@0 4468
michael@0 4469 // Emit code for the loop body.
michael@0 4470 if (!EmitTree(cx, bce, forBody))
michael@0 4471 return false;
michael@0 4472
michael@0 4473 // Set loop and enclosing "update" offsets, for continue.
michael@0 4474 StmtInfoBCE *stmt = &stmtInfo;
michael@0 4475 do {
michael@0 4476 stmt->update = bce->offset();
michael@0 4477 } while ((stmt = stmt->down) != nullptr && stmt->type == STMT_LABEL);
michael@0 4478
michael@0 4479 // COME FROM the beginning of the loop to here.
michael@0 4480 SetJumpOffsetAt(bce, jmp);
michael@0 4481 if (!EmitLoopEntry(cx, bce, nullptr))
michael@0 4482 return false;
michael@0 4483
michael@0 4484 if (Emit1(cx, bce, JSOP_POP) < 0) // ITER
michael@0 4485 return false;
michael@0 4486 if (Emit1(cx, bce, JSOP_DUP) < 0) // ITER ITER
michael@0 4487 return false;
michael@0 4488 if (Emit1(cx, bce, JSOP_DUP) < 0) // ITER ITER ITER
michael@0 4489 return false;
michael@0 4490 if (!EmitAtomOp(cx, cx->names().next, JSOP_CALLPROP, bce)) // ITER ITER NEXT
michael@0 4491 return false;
michael@0 4492 if (Emit1(cx, bce, JSOP_SWAP) < 0) // ITER NEXT ITER
michael@0 4493 return false;
michael@0 4494 if (Emit1(cx, bce, JSOP_UNDEFINED) < 0) // ITER NEXT ITER UNDEFINED
michael@0 4495 return false;
michael@0 4496 if (EmitCall(cx, bce, JSOP_CALL, 1) < 0) // ITER RESULT
michael@0 4497 return false;
michael@0 4498 CheckTypeSet(cx, bce, JSOP_CALL);
michael@0 4499 if (Emit1(cx, bce, JSOP_DUP) < 0) // ITER RESULT RESULT
michael@0 4500 return false;
michael@0 4501 if (!EmitAtomOp(cx, cx->names().done, JSOP_GETPROP, bce)) // ITER RESULT DONE?
michael@0 4502 return false;
michael@0 4503
michael@0 4504 ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFEQ, top - bce->offset()); // ITER RESULT
michael@0 4505 if (beq < 0)
michael@0 4506 return false;
michael@0 4507
michael@0 4508 JS_ASSERT(bce->stackDepth == loopDepth);
michael@0 4509
michael@0 4510 // Let Ion know where the closing jump of this loop is.
michael@0 4511 if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, beq - jmp))
michael@0 4512 return false;
michael@0 4513
michael@0 4514 // Fixup breaks and continues.
michael@0 4515 if (!PopStatementBCE(cx, bce))
michael@0 4516 return false;
michael@0 4517
michael@0 4518 if (letDecl) {
michael@0 4519 if (!LeaveNestedScope(cx, bce, &letStmt))
michael@0 4520 return false;
michael@0 4521 }
michael@0 4522
michael@0 4523 // Pop the result and the iter.
michael@0 4524 EMIT_UINT16_IMM_OP(JSOP_POPN, 2);
michael@0 4525
michael@0 4526 return true;
michael@0 4527 }
michael@0 4528
michael@0 4529 static bool
michael@0 4530 EmitForIn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
michael@0 4531 {
michael@0 4532 ParseNode *forHead = pn->pn_left;
michael@0 4533 ParseNode *forBody = pn->pn_right;
michael@0 4534
michael@0 4535 ParseNode *pn1 = forHead->pn_kid1;
michael@0 4536 bool letDecl = pn1 && pn1->isKind(PNK_LEXICALSCOPE);
michael@0 4537 JS_ASSERT_IF(letDecl, pn1->isLet());
michael@0 4538
michael@0 4539 /*
michael@0 4540 * If the left part is 'var x', emit code to define x if necessary
michael@0 4541 * using a prolog opcode, but do not emit a pop. If the left part was
michael@0 4542 * originally 'var x = i', the parser will have rewritten it; see
michael@0 4543 * Parser::forStatement. 'for (let x = i in o)' is mercifully banned.
michael@0 4544 */
michael@0 4545 if (pn1) {
michael@0 4546 ParseNode *decl = letDecl ? pn1->pn_expr : pn1;
michael@0 4547 JS_ASSERT(decl->isKind(PNK_VAR) || decl->isKind(PNK_LET));
michael@0 4548 bce->emittingForInit = true;
michael@0 4549 if (!EmitVariables(cx, bce, decl, DefineVars))
michael@0 4550 return false;
michael@0 4551 bce->emittingForInit = false;
michael@0 4552 }
michael@0 4553
michael@0 4554 /* Compile the object expression to the right of 'in'. */
michael@0 4555 if (!EmitTree(cx, bce, forHead->pn_kid3))
michael@0 4556 return false;
michael@0 4557
michael@0 4558 /*
michael@0 4559 * Emit a bytecode to convert top of stack value to the iterator
michael@0 4560 * object depending on the loop variant (for-in, for-each-in, or
michael@0 4561 * destructuring for-in).
michael@0 4562 */
michael@0 4563 JS_ASSERT(pn->isOp(JSOP_ITER));
michael@0 4564 if (Emit2(cx, bce, JSOP_ITER, (uint8_t) pn->pn_iflags) < 0)
michael@0 4565 return false;
michael@0 4566
michael@0 4567 /* Enter the block before the loop body, after evaluating the obj. */
michael@0 4568 StmtInfoBCE letStmt(cx);
michael@0 4569 if (letDecl) {
michael@0 4570 if (!EnterBlockScope(cx, bce, &letStmt, pn1->pn_objbox, 0))
michael@0 4571 return false;
michael@0 4572 }
michael@0 4573
michael@0 4574 LoopStmtInfo stmtInfo(cx);
michael@0 4575 PushLoopStatement(bce, &stmtInfo, STMT_FOR_IN_LOOP, top);
michael@0 4576
michael@0 4577 /* Annotate so IonMonkey can find the loop-closing jump. */
michael@0 4578 int noteIndex = NewSrcNote(cx, bce, SRC_FOR_IN);
michael@0 4579 if (noteIndex < 0)
michael@0 4580 return false;
michael@0 4581
michael@0 4582 /*
michael@0 4583 * Jump down to the loop condition to minimize overhead assuming at
michael@0 4584 * least one iteration, as the other loop forms do.
michael@0 4585 */
michael@0 4586 ptrdiff_t jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
michael@0 4587 if (jmp < 0)
michael@0 4588 return false;
michael@0 4589
michael@0 4590 top = bce->offset();
michael@0 4591 SET_STATEMENT_TOP(&stmtInfo, top);
michael@0 4592 if (EmitLoopHead(cx, bce, nullptr) < 0)
michael@0 4593 return false;
michael@0 4594
michael@0 4595 #ifdef DEBUG
michael@0 4596 int loopDepth = bce->stackDepth;
michael@0 4597 #endif
michael@0 4598
michael@0 4599 /*
michael@0 4600 * Emit code to get the next enumeration value and assign it to the
michael@0 4601 * left hand side.
michael@0 4602 */
michael@0 4603 if (Emit1(cx, bce, JSOP_ITERNEXT) < 0)
michael@0 4604 return false;
michael@0 4605 if (!EmitAssignment(cx, bce, forHead->pn_kid2, JSOP_NOP, nullptr))
michael@0 4606 return false;
michael@0 4607
michael@0 4608 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 4609 return false;
michael@0 4610
michael@0 4611 /* The stack should be balanced around the assignment opcode sequence. */
michael@0 4612 JS_ASSERT(bce->stackDepth == loopDepth);
michael@0 4613
michael@0 4614 /* Emit code for the loop body. */
michael@0 4615 if (!EmitTree(cx, bce, forBody))
michael@0 4616 return false;
michael@0 4617
michael@0 4618 /* Set loop and enclosing "update" offsets, for continue. */
michael@0 4619 StmtInfoBCE *stmt = &stmtInfo;
michael@0 4620 do {
michael@0 4621 stmt->update = bce->offset();
michael@0 4622 } while ((stmt = stmt->down) != nullptr && stmt->type == STMT_LABEL);
michael@0 4623
michael@0 4624 /*
michael@0 4625 * Fixup the goto that starts the loop to jump down to JSOP_MOREITER.
michael@0 4626 */
michael@0 4627 SetJumpOffsetAt(bce, jmp);
michael@0 4628 if (!EmitLoopEntry(cx, bce, nullptr))
michael@0 4629 return false;
michael@0 4630 if (Emit1(cx, bce, JSOP_MOREITER) < 0)
michael@0 4631 return false;
michael@0 4632 ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFNE, top - bce->offset());
michael@0 4633 if (beq < 0)
michael@0 4634 return false;
michael@0 4635
michael@0 4636 /* Set the srcnote offset so we can find the closing jump. */
michael@0 4637 if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, beq - jmp))
michael@0 4638 return false;
michael@0 4639
michael@0 4640 // Fix up breaks and continues.
michael@0 4641 if (!PopStatementBCE(cx, bce))
michael@0 4642 return false;
michael@0 4643
michael@0 4644 if (!bce->tryNoteList.append(JSTRY_ITER, bce->stackDepth, top, bce->offset()))
michael@0 4645 return false;
michael@0 4646 if (Emit1(cx, bce, JSOP_ENDITER) < 0)
michael@0 4647 return false;
michael@0 4648
michael@0 4649 if (letDecl) {
michael@0 4650 if (!LeaveNestedScope(cx, bce, &letStmt))
michael@0 4651 return false;
michael@0 4652 }
michael@0 4653
michael@0 4654 return true;
michael@0 4655 }
michael@0 4656
michael@0 4657 static bool
michael@0 4658 EmitNormalFor(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
michael@0 4659 {
michael@0 4660 LoopStmtInfo stmtInfo(cx);
michael@0 4661 PushLoopStatement(bce, &stmtInfo, STMT_FOR_LOOP, top);
michael@0 4662
michael@0 4663 ParseNode *forHead = pn->pn_left;
michael@0 4664 ParseNode *forBody = pn->pn_right;
michael@0 4665
michael@0 4666 /* C-style for (init; cond; update) ... loop. */
michael@0 4667 JSOp op = JSOP_POP;
michael@0 4668 ParseNode *pn3 = forHead->pn_kid1;
michael@0 4669 if (!pn3) {
michael@0 4670 // No initializer, but emit a nop so that there's somewhere to put the
michael@0 4671 // SRC_FOR annotation that IonBuilder will look for.
michael@0 4672 op = JSOP_NOP;
michael@0 4673 } else {
michael@0 4674 bce->emittingForInit = true;
michael@0 4675 if (pn3->isKind(PNK_ASSIGN)) {
michael@0 4676 JS_ASSERT(pn3->isOp(JSOP_NOP));
michael@0 4677 if (!MaybeEmitGroupAssignment(cx, bce, op, pn3, GroupIsNotDecl, &op))
michael@0 4678 return false;
michael@0 4679 }
michael@0 4680 if (op == JSOP_POP) {
michael@0 4681 if (!UpdateSourceCoordNotes(cx, bce, pn3->pn_pos.begin))
michael@0 4682 return false;
michael@0 4683 if (!EmitTree(cx, bce, pn3))
michael@0 4684 return false;
michael@0 4685 if (pn3->isKind(PNK_VAR) || pn3->isKind(PNK_CONST) || pn3->isKind(PNK_LET)) {
michael@0 4686 /*
michael@0 4687 * Check whether a destructuring-initialized var decl
michael@0 4688 * was optimized to a group assignment. If so, we do
michael@0 4689 * not need to emit a pop below, so switch to a nop,
michael@0 4690 * just for IonBuilder.
michael@0 4691 */
michael@0 4692 JS_ASSERT(pn3->isArity(PN_LIST) || pn3->isArity(PN_BINARY));
michael@0 4693 if (pn3->pn_xflags & PNX_GROUPINIT)
michael@0 4694 op = JSOP_NOP;
michael@0 4695 }
michael@0 4696 }
michael@0 4697 bce->emittingForInit = false;
michael@0 4698 }
michael@0 4699
michael@0 4700 /*
michael@0 4701 * NB: the SRC_FOR note has offsetBias 1 (JSOP_{NOP,POP}_LENGTH).
michael@0 4702 * Use tmp to hold the biased srcnote "top" offset, which differs
michael@0 4703 * from the top local variable by the length of the JSOP_GOTO
michael@0 4704 * emitted in between tmp and top if this loop has a condition.
michael@0 4705 */
michael@0 4706 int noteIndex = NewSrcNote(cx, bce, SRC_FOR);
michael@0 4707 if (noteIndex < 0 || Emit1(cx, bce, op) < 0)
michael@0 4708 return false;
michael@0 4709 ptrdiff_t tmp = bce->offset();
michael@0 4710
michael@0 4711 ptrdiff_t jmp = -1;
michael@0 4712 if (forHead->pn_kid2) {
michael@0 4713 /* Goto the loop condition, which branches back to iterate. */
michael@0 4714 jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
michael@0 4715 if (jmp < 0)
michael@0 4716 return false;
michael@0 4717 } else {
michael@0 4718 if (op != JSOP_NOP && Emit1(cx, bce, JSOP_NOP) < 0)
michael@0 4719 return false;
michael@0 4720 }
michael@0 4721
michael@0 4722 top = bce->offset();
michael@0 4723 SET_STATEMENT_TOP(&stmtInfo, top);
michael@0 4724
michael@0 4725 /* Emit code for the loop body. */
michael@0 4726 if (EmitLoopHead(cx, bce, forBody) < 0)
michael@0 4727 return false;
michael@0 4728 if (jmp == -1 && !EmitLoopEntry(cx, bce, forBody))
michael@0 4729 return false;
michael@0 4730 if (!EmitTree(cx, bce, forBody))
michael@0 4731 return false;
michael@0 4732
michael@0 4733 /* Set the second note offset so we can find the update part. */
michael@0 4734 JS_ASSERT(noteIndex != -1);
michael@0 4735 ptrdiff_t tmp2 = bce->offset();
michael@0 4736
michael@0 4737 /* Set loop and enclosing "update" offsets, for continue. */
michael@0 4738 StmtInfoBCE *stmt = &stmtInfo;
michael@0 4739 do {
michael@0 4740 stmt->update = bce->offset();
michael@0 4741 } while ((stmt = stmt->down) != nullptr && stmt->type == STMT_LABEL);
michael@0 4742
michael@0 4743 /* Check for update code to do before the condition (if any). */
michael@0 4744 pn3 = forHead->pn_kid3;
michael@0 4745 if (pn3) {
michael@0 4746 if (!UpdateSourceCoordNotes(cx, bce, pn3->pn_pos.begin))
michael@0 4747 return false;
michael@0 4748 op = JSOP_POP;
michael@0 4749 if (pn3->isKind(PNK_ASSIGN)) {
michael@0 4750 JS_ASSERT(pn3->isOp(JSOP_NOP));
michael@0 4751 if (!MaybeEmitGroupAssignment(cx, bce, op, pn3, GroupIsNotDecl, &op))
michael@0 4752 return false;
michael@0 4753 }
michael@0 4754 if (op == JSOP_POP && !EmitTree(cx, bce, pn3))
michael@0 4755 return false;
michael@0 4756
michael@0 4757 /* Always emit the POP or NOP to help IonBuilder. */
michael@0 4758 if (Emit1(cx, bce, op) < 0)
michael@0 4759 return false;
michael@0 4760
michael@0 4761 /* Restore the absolute line number for source note readers. */
michael@0 4762 uint32_t lineNum = bce->parser->tokenStream.srcCoords.lineNum(pn->pn_pos.end);
michael@0 4763 if (bce->currentLine() != lineNum) {
michael@0 4764 if (NewSrcNote2(cx, bce, SRC_SETLINE, ptrdiff_t(lineNum)) < 0)
michael@0 4765 return false;
michael@0 4766 bce->current->currentLine = lineNum;
michael@0 4767 bce->current->lastColumn = 0;
michael@0 4768 }
michael@0 4769 }
michael@0 4770
michael@0 4771 ptrdiff_t tmp3 = bce->offset();
michael@0 4772
michael@0 4773 if (forHead->pn_kid2) {
michael@0 4774 /* Fix up the goto from top to target the loop condition. */
michael@0 4775 JS_ASSERT(jmp >= 0);
michael@0 4776 SetJumpOffsetAt(bce, jmp);
michael@0 4777 if (!EmitLoopEntry(cx, bce, forHead->pn_kid2))
michael@0 4778 return false;
michael@0 4779
michael@0 4780 if (!EmitTree(cx, bce, forHead->pn_kid2))
michael@0 4781 return false;
michael@0 4782 }
michael@0 4783
michael@0 4784 /* Set the first note offset so we can find the loop condition. */
michael@0 4785 if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, tmp3 - tmp))
michael@0 4786 return false;
michael@0 4787 if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 1, tmp2 - tmp))
michael@0 4788 return false;
michael@0 4789 /* The third note offset helps us find the loop-closing jump. */
michael@0 4790 if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 2, bce->offset() - tmp))
michael@0 4791 return false;
michael@0 4792
michael@0 4793 /* If no loop condition, just emit a loop-closing jump. */
michael@0 4794 op = forHead->pn_kid2 ? JSOP_IFNE : JSOP_GOTO;
michael@0 4795 if (EmitJump(cx, bce, op, top - bce->offset()) < 0)
michael@0 4796 return false;
michael@0 4797
michael@0 4798 if (!bce->tryNoteList.append(JSTRY_LOOP, bce->stackDepth, top, bce->offset()))
michael@0 4799 return false;
michael@0 4800
michael@0 4801 /* Now fixup all breaks and continues. */
michael@0 4802 return PopStatementBCE(cx, bce);
michael@0 4803 }
michael@0 4804
michael@0 4805 static inline bool
michael@0 4806 EmitFor(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
michael@0 4807 {
michael@0 4808 if (pn->pn_left->isKind(PNK_FORIN))
michael@0 4809 return EmitForIn(cx, bce, pn, top);
michael@0 4810
michael@0 4811 if (pn->pn_left->isKind(PNK_FOROF))
michael@0 4812 return EmitForOf(cx, bce, pn, top);
michael@0 4813
michael@0 4814 JS_ASSERT(pn->pn_left->isKind(PNK_FORHEAD));
michael@0 4815 return EmitNormalFor(cx, bce, pn, top);
michael@0 4816 }
michael@0 4817
michael@0 4818 static MOZ_NEVER_INLINE bool
michael@0 4819 EmitFunc(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
michael@0 4820 {
michael@0 4821 FunctionBox *funbox = pn->pn_funbox;
michael@0 4822 RootedFunction fun(cx, funbox->function());
michael@0 4823 JS_ASSERT_IF(fun->isInterpretedLazy(), fun->lazyScript());
michael@0 4824
michael@0 4825 /*
michael@0 4826 * Set the EMITTEDFUNCTION flag in function definitions once they have been
michael@0 4827 * emitted. Function definitions that need hoisting to the top of the
michael@0 4828 * function will be seen by EmitFunc in two places.
michael@0 4829 */
michael@0 4830 if (pn->pn_dflags & PND_EMITTEDFUNCTION) {
michael@0 4831 JS_ASSERT_IF(fun->hasScript(), fun->nonLazyScript());
michael@0 4832 JS_ASSERT(pn->functionIsHoisted());
michael@0 4833 JS_ASSERT(bce->sc->isFunctionBox());
michael@0 4834 return true;
michael@0 4835 }
michael@0 4836
michael@0 4837 pn->pn_dflags |= PND_EMITTEDFUNCTION;
michael@0 4838
michael@0 4839 /*
michael@0 4840 * Mark as singletons any function which will only be executed once, or
michael@0 4841 * which is inner to a lambda we only expect to run once. In the latter
michael@0 4842 * case, if the lambda runs multiple times then CloneFunctionObject will
michael@0 4843 * make a deep clone of its contents.
michael@0 4844 */
michael@0 4845 if (fun->isInterpreted()) {
michael@0 4846 bool singleton =
michael@0 4847 bce->script->compileAndGo() &&
michael@0 4848 fun->isInterpreted() &&
michael@0 4849 (bce->checkSingletonContext() ||
michael@0 4850 (!bce->isInLoop() && bce->isRunOnceLambda()));
michael@0 4851 if (!JSFunction::setTypeForScriptedFunction(cx, fun, singleton))
michael@0 4852 return false;
michael@0 4853
michael@0 4854 if (fun->isInterpretedLazy()) {
michael@0 4855 if (!fun->lazyScript()->sourceObject()) {
michael@0 4856 JSObject *scope = bce->staticScope;
michael@0 4857 if (!scope && bce->sc->isFunctionBox())
michael@0 4858 scope = bce->sc->asFunctionBox()->function();
michael@0 4859 JSObject *source = bce->script->sourceObject();
michael@0 4860 fun->lazyScript()->setParent(scope, &source->as<ScriptSourceObject>());
michael@0 4861 }
michael@0 4862 if (bce->emittingRunOnceLambda)
michael@0 4863 fun->lazyScript()->setTreatAsRunOnce();
michael@0 4864 } else {
michael@0 4865 SharedContext *outersc = bce->sc;
michael@0 4866
michael@0 4867 if (outersc->isFunctionBox() && outersc->asFunctionBox()->mightAliasLocals())
michael@0 4868 funbox->setMightAliasLocals(); // inherit mightAliasLocals from parent
michael@0 4869 JS_ASSERT_IF(outersc->strict, funbox->strict);
michael@0 4870
michael@0 4871 // Inherit most things (principals, version, etc) from the parent.
michael@0 4872 Rooted<JSScript*> parent(cx, bce->script);
michael@0 4873 CompileOptions options(cx, bce->parser->options());
michael@0 4874 options.setOriginPrincipals(parent->originPrincipals())
michael@0 4875 .setCompileAndGo(parent->compileAndGo())
michael@0 4876 .setSelfHostingMode(parent->selfHosted())
michael@0 4877 .setNoScriptRval(false)
michael@0 4878 .setForEval(false)
michael@0 4879 .setVersion(parent->getVersion());
michael@0 4880
michael@0 4881 Rooted<JSObject*> enclosingScope(cx, EnclosingStaticScope(bce));
michael@0 4882 Rooted<JSObject*> sourceObject(cx, bce->script->sourceObject());
michael@0 4883 Rooted<JSScript*> script(cx, JSScript::Create(cx, enclosingScope, false, options,
michael@0 4884 parent->staticLevel() + 1,
michael@0 4885 sourceObject,
michael@0 4886 funbox->bufStart, funbox->bufEnd));
michael@0 4887 if (!script)
michael@0 4888 return false;
michael@0 4889
michael@0 4890 script->bindings = funbox->bindings;
michael@0 4891
michael@0 4892 uint32_t lineNum = bce->parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin);
michael@0 4893 BytecodeEmitter bce2(bce, bce->parser, funbox, script, bce->insideEval,
michael@0 4894 bce->evalCaller, bce->hasGlobalScope, lineNum,
michael@0 4895 bce->emitterMode);
michael@0 4896 if (!bce2.init())
michael@0 4897 return false;
michael@0 4898
michael@0 4899 /* We measured the max scope depth when we parsed the function. */
michael@0 4900 if (!EmitFunctionScript(cx, &bce2, pn->pn_body))
michael@0 4901 return false;
michael@0 4902
michael@0 4903 if (funbox->usesArguments && funbox->usesApply)
michael@0 4904 script->setUsesArgumentsAndApply();
michael@0 4905 }
michael@0 4906 } else {
michael@0 4907 JS_ASSERT(IsAsmJSModuleNative(fun->native()));
michael@0 4908 }
michael@0 4909
michael@0 4910 /* Make the function object a literal in the outer script's pool. */
michael@0 4911 unsigned index = bce->objectList.add(pn->pn_funbox);
michael@0 4912
michael@0 4913 /* Non-hoisted functions simply emit their respective op. */
michael@0 4914 if (!pn->functionIsHoisted()) {
michael@0 4915 /* JSOP_LAMBDA_ARROW is always preceded by JSOP_THIS. */
michael@0 4916 MOZ_ASSERT(fun->isArrow() == (pn->getOp() == JSOP_LAMBDA_ARROW));
michael@0 4917 if (fun->isArrow() && Emit1(cx, bce, JSOP_THIS) < 0)
michael@0 4918 return false;
michael@0 4919 return EmitIndex32(cx, pn->getOp(), index, bce);
michael@0 4920 }
michael@0 4921
michael@0 4922 /*
michael@0 4923 * For a script we emit the code as we parse. Thus the bytecode for
michael@0 4924 * top-level functions should go in the prolog to predefine their
michael@0 4925 * names in the variable object before the already-generated main code
michael@0 4926 * is executed. This extra work for top-level scripts is not necessary
michael@0 4927 * when we emit the code for a function. It is fully parsed prior to
michael@0 4928 * invocation of the emitter and calls to EmitTree for function
michael@0 4929 * definitions can be scheduled before generating the rest of code.
michael@0 4930 */
michael@0 4931 if (!bce->sc->isFunctionBox()) {
michael@0 4932 JS_ASSERT(pn->pn_cookie.isFree());
michael@0 4933 JS_ASSERT(pn->getOp() == JSOP_NOP);
michael@0 4934 JS_ASSERT(!bce->topStmt);
michael@0 4935 bce->switchToProlog();
michael@0 4936 if (!EmitIndex32(cx, JSOP_DEFFUN, index, bce))
michael@0 4937 return false;
michael@0 4938 if (!UpdateSourceCoordNotes(cx, bce, pn->pn_pos.begin))
michael@0 4939 return false;
michael@0 4940 bce->switchToMain();
michael@0 4941 } else {
michael@0 4942 #ifdef DEBUG
michael@0 4943 BindingIter bi(bce->script);
michael@0 4944 while (bi->name() != fun->atom())
michael@0 4945 bi++;
michael@0 4946 JS_ASSERT(bi->kind() == Binding::VARIABLE || bi->kind() == Binding::CONSTANT ||
michael@0 4947 bi->kind() == Binding::ARGUMENT);
michael@0 4948 JS_ASSERT(bi.frameIndex() < JS_BIT(20));
michael@0 4949 #endif
michael@0 4950 pn->pn_index = index;
michael@0 4951 if (!EmitIndexOp(cx, JSOP_LAMBDA, index, bce))
michael@0 4952 return false;
michael@0 4953 JS_ASSERT(pn->getOp() == JSOP_GETLOCAL || pn->getOp() == JSOP_GETARG);
michael@0 4954 JSOp setOp = pn->getOp() == JSOP_GETLOCAL ? JSOP_SETLOCAL : JSOP_SETARG;
michael@0 4955 if (!EmitVarOp(cx, pn, setOp, bce))
michael@0 4956 return false;
michael@0 4957 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 4958 return false;
michael@0 4959 }
michael@0 4960
michael@0 4961 return true;
michael@0 4962 }
michael@0 4963
michael@0 4964 static bool
michael@0 4965 EmitDo(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
michael@0 4966 {
michael@0 4967 /* Emit an annotated nop so IonBuilder can recognize the 'do' loop. */
michael@0 4968 ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_WHILE);
michael@0 4969 if (noteIndex < 0 || Emit1(cx, bce, JSOP_NOP) < 0)
michael@0 4970 return false;
michael@0 4971
michael@0 4972 ptrdiff_t noteIndex2 = NewSrcNote(cx, bce, SRC_WHILE);
michael@0 4973 if (noteIndex2 < 0)
michael@0 4974 return false;
michael@0 4975
michael@0 4976 /* Compile the loop body. */
michael@0 4977 ptrdiff_t top = EmitLoopHead(cx, bce, pn->pn_left);
michael@0 4978 if (top < 0)
michael@0 4979 return false;
michael@0 4980
michael@0 4981 LoopStmtInfo stmtInfo(cx);
michael@0 4982 PushLoopStatement(bce, &stmtInfo, STMT_DO_LOOP, top);
michael@0 4983
michael@0 4984 if (!EmitLoopEntry(cx, bce, nullptr))
michael@0 4985 return false;
michael@0 4986
michael@0 4987 if (!EmitTree(cx, bce, pn->pn_left))
michael@0 4988 return false;
michael@0 4989
michael@0 4990 /* Set loop and enclosing label update offsets, for continue. */
michael@0 4991 ptrdiff_t off = bce->offset();
michael@0 4992 StmtInfoBCE *stmt = &stmtInfo;
michael@0 4993 do {
michael@0 4994 stmt->update = off;
michael@0 4995 } while ((stmt = stmt->down) != nullptr && stmt->type == STMT_LABEL);
michael@0 4996
michael@0 4997 /* Compile the loop condition, now that continues know where to go. */
michael@0 4998 if (!EmitTree(cx, bce, pn->pn_right))
michael@0 4999 return false;
michael@0 5000
michael@0 5001 ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFNE, top - bce->offset());
michael@0 5002 if (beq < 0)
michael@0 5003 return false;
michael@0 5004
michael@0 5005 if (!bce->tryNoteList.append(JSTRY_LOOP, bce->stackDepth, top, bce->offset()))
michael@0 5006 return false;
michael@0 5007
michael@0 5008 /*
michael@0 5009 * Update the annotations with the update and back edge positions, for
michael@0 5010 * IonBuilder.
michael@0 5011 *
michael@0 5012 * Be careful: We must set noteIndex2 before noteIndex in case the noteIndex
michael@0 5013 * note gets bigger.
michael@0 5014 */
michael@0 5015 if (!SetSrcNoteOffset(cx, bce, noteIndex2, 0, beq - top))
michael@0 5016 return false;
michael@0 5017 if (!SetSrcNoteOffset(cx, bce, noteIndex, 0, 1 + (off - top)))
michael@0 5018 return false;
michael@0 5019
michael@0 5020 return PopStatementBCE(cx, bce);
michael@0 5021 }
michael@0 5022
michael@0 5023 static bool
michael@0 5024 EmitWhile(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
michael@0 5025 {
michael@0 5026 /*
michael@0 5027 * Minimize bytecodes issued for one or more iterations by jumping to
michael@0 5028 * the condition below the body and closing the loop if the condition
michael@0 5029 * is true with a backward branch. For iteration count i:
michael@0 5030 *
michael@0 5031 * i test at the top test at the bottom
michael@0 5032 * = =============== ==================
michael@0 5033 * 0 ifeq-pass goto; ifne-fail
michael@0 5034 * 1 ifeq-fail; goto; ifne-pass goto; ifne-pass; ifne-fail
michael@0 5035 * 2 2*(ifeq-fail; goto); ifeq-pass goto; 2*ifne-pass; ifne-fail
michael@0 5036 * . . .
michael@0 5037 * N N*(ifeq-fail; goto); ifeq-pass goto; N*ifne-pass; ifne-fail
michael@0 5038 */
michael@0 5039 LoopStmtInfo stmtInfo(cx);
michael@0 5040 PushLoopStatement(bce, &stmtInfo, STMT_WHILE_LOOP, top);
michael@0 5041
michael@0 5042 ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_WHILE);
michael@0 5043 if (noteIndex < 0)
michael@0 5044 return false;
michael@0 5045
michael@0 5046 ptrdiff_t jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
michael@0 5047 if (jmp < 0)
michael@0 5048 return false;
michael@0 5049
michael@0 5050 top = EmitLoopHead(cx, bce, pn->pn_right);
michael@0 5051 if (top < 0)
michael@0 5052 return false;
michael@0 5053
michael@0 5054 if (!EmitTree(cx, bce, pn->pn_right))
michael@0 5055 return false;
michael@0 5056
michael@0 5057 SetJumpOffsetAt(bce, jmp);
michael@0 5058 if (!EmitLoopEntry(cx, bce, pn->pn_left))
michael@0 5059 return false;
michael@0 5060 if (!EmitTree(cx, bce, pn->pn_left))
michael@0 5061 return false;
michael@0 5062
michael@0 5063 ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFNE, top - bce->offset());
michael@0 5064 if (beq < 0)
michael@0 5065 return false;
michael@0 5066
michael@0 5067 if (!bce->tryNoteList.append(JSTRY_LOOP, bce->stackDepth, top, bce->offset()))
michael@0 5068 return false;
michael@0 5069
michael@0 5070 if (!SetSrcNoteOffset(cx, bce, noteIndex, 0, beq - jmp))
michael@0 5071 return false;
michael@0 5072
michael@0 5073 return PopStatementBCE(cx, bce);
michael@0 5074 }
michael@0 5075
michael@0 5076 static bool
michael@0 5077 EmitBreak(ExclusiveContext *cx, BytecodeEmitter *bce, PropertyName *label)
michael@0 5078 {
michael@0 5079 StmtInfoBCE *stmt = bce->topStmt;
michael@0 5080 SrcNoteType noteType;
michael@0 5081 if (label) {
michael@0 5082 while (stmt->type != STMT_LABEL || stmt->label != label)
michael@0 5083 stmt = stmt->down;
michael@0 5084 noteType = SRC_BREAK2LABEL;
michael@0 5085 } else {
michael@0 5086 while (!stmt->isLoop() && stmt->type != STMT_SWITCH)
michael@0 5087 stmt = stmt->down;
michael@0 5088 noteType = (stmt->type == STMT_SWITCH) ? SRC_SWITCHBREAK : SRC_BREAK;
michael@0 5089 }
michael@0 5090
michael@0 5091 return EmitGoto(cx, bce, stmt, &stmt->breaks, noteType) >= 0;
michael@0 5092 }
michael@0 5093
michael@0 5094 static bool
michael@0 5095 EmitContinue(ExclusiveContext *cx, BytecodeEmitter *bce, PropertyName *label)
michael@0 5096 {
michael@0 5097 StmtInfoBCE *stmt = bce->topStmt;
michael@0 5098 if (label) {
michael@0 5099 /* Find the loop statement enclosed by the matching label. */
michael@0 5100 StmtInfoBCE *loop = nullptr;
michael@0 5101 while (stmt->type != STMT_LABEL || stmt->label != label) {
michael@0 5102 if (stmt->isLoop())
michael@0 5103 loop = stmt;
michael@0 5104 stmt = stmt->down;
michael@0 5105 }
michael@0 5106 stmt = loop;
michael@0 5107 } else {
michael@0 5108 while (!stmt->isLoop())
michael@0 5109 stmt = stmt->down;
michael@0 5110 }
michael@0 5111
michael@0 5112 return EmitGoto(cx, bce, stmt, &stmt->continues, SRC_CONTINUE) >= 0;
michael@0 5113 }
michael@0 5114
michael@0 5115 static bool
michael@0 5116 EmitReturn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
michael@0 5117 {
michael@0 5118 if (!UpdateSourceCoordNotes(cx, bce, pn->pn_pos.begin))
michael@0 5119 return false;
michael@0 5120
michael@0 5121 if (bce->sc->isFunctionBox() && bce->sc->asFunctionBox()->isStarGenerator()) {
michael@0 5122 if (!EmitPrepareIteratorResult(cx, bce))
michael@0 5123 return false;
michael@0 5124 }
michael@0 5125
michael@0 5126 /* Push a return value */
michael@0 5127 if (ParseNode *pn2 = pn->pn_kid) {
michael@0 5128 if (!EmitTree(cx, bce, pn2))
michael@0 5129 return false;
michael@0 5130 } else {
michael@0 5131 /* No explicit return value provided */
michael@0 5132 if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
michael@0 5133 return false;
michael@0 5134 }
michael@0 5135
michael@0 5136 if (bce->sc->isFunctionBox() && bce->sc->asFunctionBox()->isStarGenerator()) {
michael@0 5137 if (!EmitFinishIteratorResult(cx, bce, true))
michael@0 5138 return false;
michael@0 5139 }
michael@0 5140
michael@0 5141 /*
michael@0 5142 * EmitNonLocalJumpFixup may add fixup bytecode to close open try
michael@0 5143 * blocks having finally clauses and to exit intermingled let blocks.
michael@0 5144 * We can't simply transfer control flow to our caller in that case,
michael@0 5145 * because we must gosub to those finally clauses from inner to outer,
michael@0 5146 * with the correct stack pointer (i.e., after popping any with,
michael@0 5147 * for/in, etc., slots nested inside the finally's try).
michael@0 5148 *
michael@0 5149 * In this case we mutate JSOP_RETURN into JSOP_SETRVAL and add an
michael@0 5150 * extra JSOP_RETRVAL after the fixups.
michael@0 5151 */
michael@0 5152 ptrdiff_t top = bce->offset();
michael@0 5153
michael@0 5154 if (Emit1(cx, bce, JSOP_RETURN) < 0)
michael@0 5155 return false;
michael@0 5156
michael@0 5157 NonLocalExitScope nle(cx, bce);
michael@0 5158
michael@0 5159 if (!nle.prepareForNonLocalJump(nullptr))
michael@0 5160 return false;
michael@0 5161
michael@0 5162 if (top + static_cast<ptrdiff_t>(JSOP_RETURN_LENGTH) != bce->offset()) {
michael@0 5163 bce->code()[top] = JSOP_SETRVAL;
michael@0 5164 if (Emit1(cx, bce, JSOP_RETRVAL) < 0)
michael@0 5165 return false;
michael@0 5166 }
michael@0 5167
michael@0 5168 return true;
michael@0 5169 }
michael@0 5170
michael@0 5171 static bool
michael@0 5172 EmitYieldStar(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *iter)
michael@0 5173 {
michael@0 5174 JS_ASSERT(bce->sc->isFunctionBox());
michael@0 5175 JS_ASSERT(bce->sc->asFunctionBox()->isStarGenerator());
michael@0 5176
michael@0 5177 if (!EmitTree(cx, bce, iter)) // ITERABLE
michael@0 5178 return false;
michael@0 5179
michael@0 5180 // Convert iterable to iterator.
michael@0 5181 if (Emit1(cx, bce, JSOP_DUP) < 0) // ITERABLE ITERABLE
michael@0 5182 return false;
michael@0 5183 if (!EmitAtomOp(cx, cx->names().std_iterator, JSOP_CALLPROP, bce)) // ITERABLE @@ITERATOR
michael@0 5184 return false;
michael@0 5185 if (Emit1(cx, bce, JSOP_SWAP) < 0) // @@ITERATOR ITERABLE
michael@0 5186 return false;
michael@0 5187 if (EmitCall(cx, bce, JSOP_CALL, 0) < 0) // ITER
michael@0 5188 return false;
michael@0 5189 CheckTypeSet(cx, bce, JSOP_CALL);
michael@0 5190
michael@0 5191 int depth = bce->stackDepth;
michael@0 5192 JS_ASSERT(depth >= 1);
michael@0 5193
michael@0 5194 // Initial send value is undefined.
michael@0 5195 if (Emit1(cx, bce, JSOP_UNDEFINED) < 0) // ITER RECEIVED
michael@0 5196 return false;
michael@0 5197 ptrdiff_t initialSend = -1;
michael@0 5198 if (EmitBackPatchOp(cx, bce, &initialSend) < 0) // goto initialSend
michael@0 5199 return false;
michael@0 5200
michael@0 5201 // Try prologue. // ITER RESULT
michael@0 5202 StmtInfoBCE stmtInfo(cx);
michael@0 5203 PushStatementBCE(bce, &stmtInfo, STMT_TRY, bce->offset());
michael@0 5204 ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_TRY);
michael@0 5205 if (noteIndex < 0 || Emit1(cx, bce, JSOP_TRY) < 0)
michael@0 5206 return false;
michael@0 5207 ptrdiff_t tryStart = bce->offset(); // tryStart:
michael@0 5208 JS_ASSERT(bce->stackDepth == depth + 1);
michael@0 5209
michael@0 5210 // Yield RESULT as-is, without re-boxing.
michael@0 5211 if (Emit1(cx, bce, JSOP_YIELD) < 0) // ITER RECEIVED
michael@0 5212 return false;
michael@0 5213
michael@0 5214 // Try epilogue.
michael@0 5215 if (!SetSrcNoteOffset(cx, bce, noteIndex, 0, bce->offset() - tryStart + JSOP_TRY_LENGTH))
michael@0 5216 return false;
michael@0 5217 ptrdiff_t subsequentSend = -1;
michael@0 5218 if (EmitBackPatchOp(cx, bce, &subsequentSend) < 0) // goto subsequentSend
michael@0 5219 return false;
michael@0 5220 ptrdiff_t tryEnd = bce->offset(); // tryEnd:
michael@0 5221
michael@0 5222 // Catch location.
michael@0 5223 // THROW? = 'throw' in ITER // ITER
michael@0 5224 bce->stackDepth = (uint32_t) depth;
michael@0 5225 if (Emit1(cx, bce, JSOP_EXCEPTION) < 0) // ITER EXCEPTION
michael@0 5226 return false;
michael@0 5227 if (Emit1(cx, bce, JSOP_SWAP) < 0) // EXCEPTION ITER
michael@0 5228 return false;
michael@0 5229 if (Emit1(cx, bce, JSOP_DUP) < 0) // EXCEPTION ITER ITER
michael@0 5230 return false;
michael@0 5231 if (!EmitAtomOp(cx, cx->names().throw_, JSOP_STRING, bce)) // EXCEPTION ITER ITER "throw"
michael@0 5232 return false;
michael@0 5233 if (Emit1(cx, bce, JSOP_SWAP) < 0) // EXCEPTION ITER "throw" ITER
michael@0 5234 return false;
michael@0 5235 if (Emit1(cx, bce, JSOP_IN) < 0) // EXCEPTION ITER THROW?
michael@0 5236 return false;
michael@0 5237 // if (THROW?) goto delegate
michael@0 5238 ptrdiff_t checkThrow = EmitJump(cx, bce, JSOP_IFNE, 0); // EXCEPTION ITER
michael@0 5239 if (checkThrow < 0)
michael@0 5240 return false;
michael@0 5241 if (Emit1(cx, bce, JSOP_POP) < 0) // EXCEPTION
michael@0 5242 return false;
michael@0 5243 if (Emit1(cx, bce, JSOP_THROW) < 0) // throw EXCEPTION
michael@0 5244 return false;
michael@0 5245
michael@0 5246 SetJumpOffsetAt(bce, checkThrow); // delegate:
michael@0 5247 // RESULT = ITER.throw(EXCEPTION) // EXCEPTION ITER
michael@0 5248 bce->stackDepth = (uint32_t) depth + 1;
michael@0 5249 if (Emit1(cx, bce, JSOP_DUP) < 0) // EXCEPTION ITER ITER
michael@0 5250 return false;
michael@0 5251 if (Emit1(cx, bce, JSOP_DUP) < 0) // EXCEPTION ITER ITER ITER
michael@0 5252 return false;
michael@0 5253 if (!EmitAtomOp(cx, cx->names().throw_, JSOP_CALLPROP, bce)) // EXCEPTION ITER ITER THROW
michael@0 5254 return false;
michael@0 5255 if (Emit1(cx, bce, JSOP_SWAP) < 0) // EXCEPTION ITER THROW ITER
michael@0 5256 return false;
michael@0 5257 if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)3) < 0) // ITER THROW ITER EXCEPTION
michael@0 5258 return false;
michael@0 5259 if (EmitCall(cx, bce, JSOP_CALL, 1) < 0) // ITER RESULT
michael@0 5260 return false;
michael@0 5261 CheckTypeSet(cx, bce, JSOP_CALL);
michael@0 5262 JS_ASSERT(bce->stackDepth == depth + 1);
michael@0 5263 ptrdiff_t checkResult = -1;
michael@0 5264 if (EmitBackPatchOp(cx, bce, &checkResult) < 0) // goto checkResult
michael@0 5265 return false;
michael@0 5266
michael@0 5267 // Catch epilogue.
michael@0 5268 if (!PopStatementBCE(cx, bce))
michael@0 5269 return false;
michael@0 5270 // This is a peace offering to ReconstructPCStack. See the note in EmitTry.
michael@0 5271 if (Emit1(cx, bce, JSOP_NOP) < 0)
michael@0 5272 return false;
michael@0 5273 if (!bce->tryNoteList.append(JSTRY_CATCH, depth, tryStart, tryEnd))
michael@0 5274 return false;
michael@0 5275
michael@0 5276 // After the try/catch block: send the received value to the iterator.
michael@0 5277 if (!BackPatch(cx, bce, initialSend, bce->code().end(), JSOP_GOTO)) // initialSend:
michael@0 5278 return false;
michael@0 5279 if (!BackPatch(cx, bce, subsequentSend, bce->code().end(), JSOP_GOTO)) // subsequentSend:
michael@0 5280 return false;
michael@0 5281
michael@0 5282 // Send location.
michael@0 5283 // result = iter.next(received) // ITER RECEIVED
michael@0 5284 if (Emit1(cx, bce, JSOP_SWAP) < 0) // RECEIVED ITER
michael@0 5285 return false;
michael@0 5286 if (Emit1(cx, bce, JSOP_DUP) < 0) // RECEIVED ITER ITER
michael@0 5287 return false;
michael@0 5288 if (Emit1(cx, bce, JSOP_DUP) < 0) // RECEIVED ITER ITER ITER
michael@0 5289 return false;
michael@0 5290 if (!EmitAtomOp(cx, cx->names().next, JSOP_CALLPROP, bce)) // RECEIVED ITER ITER NEXT
michael@0 5291 return false;
michael@0 5292 if (Emit1(cx, bce, JSOP_SWAP) < 0) // RECEIVED ITER NEXT ITER
michael@0 5293 return false;
michael@0 5294 if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)3) < 0) // ITER NEXT ITER RECEIVED
michael@0 5295 return false;
michael@0 5296 if (EmitCall(cx, bce, JSOP_CALL, 1) < 0) // ITER RESULT
michael@0 5297 return false;
michael@0 5298 CheckTypeSet(cx, bce, JSOP_CALL);
michael@0 5299 JS_ASSERT(bce->stackDepth == depth + 1);
michael@0 5300
michael@0 5301 if (!BackPatch(cx, bce, checkResult, bce->code().end(), JSOP_GOTO)) // checkResult:
michael@0 5302 return false;
michael@0 5303 // if (!result.done) goto tryStart; // ITER RESULT
michael@0 5304 if (Emit1(cx, bce, JSOP_DUP) < 0) // ITER RESULT RESULT
michael@0 5305 return false;
michael@0 5306 if (!EmitAtomOp(cx, cx->names().done, JSOP_GETPROP, bce)) // ITER RESULT DONE
michael@0 5307 return false;
michael@0 5308 // if (!DONE) goto tryStart;
michael@0 5309 if (EmitJump(cx, bce, JSOP_IFEQ, tryStart - bce->offset()) < 0) // ITER RESULT
michael@0 5310 return false;
michael@0 5311
michael@0 5312 // result.value
michael@0 5313 if (Emit1(cx, bce, JSOP_SWAP) < 0) // RESULT ITER
michael@0 5314 return false;
michael@0 5315 if (Emit1(cx, bce, JSOP_POP) < 0) // RESULT
michael@0 5316 return false;
michael@0 5317 if (!EmitAtomOp(cx, cx->names().value, JSOP_GETPROP, bce)) // VALUE
michael@0 5318 return false;
michael@0 5319
michael@0 5320 JS_ASSERT(bce->stackDepth == depth);
michael@0 5321
michael@0 5322 return true;
michael@0 5323 }
michael@0 5324
michael@0 5325 static bool
michael@0 5326 EmitStatementList(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
michael@0 5327 {
michael@0 5328 JS_ASSERT(pn->isArity(PN_LIST));
michael@0 5329
michael@0 5330 StmtInfoBCE stmtInfo(cx);
michael@0 5331 PushStatementBCE(bce, &stmtInfo, STMT_BLOCK, top);
michael@0 5332
michael@0 5333 ParseNode *pnchild = pn->pn_head;
michael@0 5334
michael@0 5335 if (pn->pn_xflags & PNX_DESTRUCT)
michael@0 5336 pnchild = pnchild->pn_next;
michael@0 5337
michael@0 5338 for (ParseNode *pn2 = pnchild; pn2; pn2 = pn2->pn_next) {
michael@0 5339 if (!EmitTree(cx, bce, pn2))
michael@0 5340 return false;
michael@0 5341 }
michael@0 5342
michael@0 5343 return PopStatementBCE(cx, bce);
michael@0 5344 }
michael@0 5345
michael@0 5346 static bool
michael@0 5347 EmitStatement(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
michael@0 5348 {
michael@0 5349 JS_ASSERT(pn->isKind(PNK_SEMI));
michael@0 5350
michael@0 5351 ParseNode *pn2 = pn->pn_kid;
michael@0 5352 if (!pn2)
michael@0 5353 return true;
michael@0 5354
michael@0 5355 if (!UpdateSourceCoordNotes(cx, bce, pn->pn_pos.begin))
michael@0 5356 return false;
michael@0 5357
michael@0 5358 /*
michael@0 5359 * Top-level or called-from-a-native JS_Execute/EvaluateScript,
michael@0 5360 * debugger, and eval frames may need the value of the ultimate
michael@0 5361 * expression statement as the script's result, despite the fact
michael@0 5362 * that it appears useless to the compiler.
michael@0 5363 *
michael@0 5364 * API users may also set the JSOPTION_NO_SCRIPT_RVAL option when
michael@0 5365 * calling JS_Compile* to suppress JSOP_SETRVAL.
michael@0 5366 */
michael@0 5367 bool wantval = false;
michael@0 5368 bool useful = false;
michael@0 5369 if (bce->sc->isFunctionBox()) {
michael@0 5370 JS_ASSERT(!bce->script->noScriptRval());
michael@0 5371 } else {
michael@0 5372 useful = wantval = !bce->script->noScriptRval();
michael@0 5373 }
michael@0 5374
michael@0 5375 /* Don't eliminate expressions with side effects. */
michael@0 5376 if (!useful) {
michael@0 5377 if (!CheckSideEffects(cx, bce, pn2, &useful))
michael@0 5378 return false;
michael@0 5379
michael@0 5380 /*
michael@0 5381 * Don't eliminate apparently useless expressions if they are
michael@0 5382 * labeled expression statements. The pc->topStmt->update test
michael@0 5383 * catches the case where we are nesting in EmitTree for a labeled
michael@0 5384 * compound statement.
michael@0 5385 */
michael@0 5386 if (bce->topStmt &&
michael@0 5387 bce->topStmt->type == STMT_LABEL &&
michael@0 5388 bce->topStmt->update >= bce->offset())
michael@0 5389 {
michael@0 5390 useful = true;
michael@0 5391 }
michael@0 5392 }
michael@0 5393
michael@0 5394 if (useful) {
michael@0 5395 JSOp op = wantval ? JSOP_SETRVAL : JSOP_POP;
michael@0 5396 JS_ASSERT_IF(pn2->isKind(PNK_ASSIGN), pn2->isOp(JSOP_NOP));
michael@0 5397 if (!wantval &&
michael@0 5398 pn2->isKind(PNK_ASSIGN) &&
michael@0 5399 !MaybeEmitGroupAssignment(cx, bce, op, pn2, GroupIsNotDecl, &op))
michael@0 5400 {
michael@0 5401 return false;
michael@0 5402 }
michael@0 5403 if (op != JSOP_NOP) {
michael@0 5404 if (!EmitTree(cx, bce, pn2))
michael@0 5405 return false;
michael@0 5406 if (Emit1(cx, bce, op) < 0)
michael@0 5407 return false;
michael@0 5408 }
michael@0 5409 } else if (!pn->isDirectivePrologueMember()) {
michael@0 5410 /* Don't complain about directive prologue members; just don't emit their code. */
michael@0 5411 bce->current->currentLine = bce->parser->tokenStream.srcCoords.lineNum(pn2->pn_pos.begin);
michael@0 5412 bce->current->lastColumn = 0;
michael@0 5413 if (!bce->reportStrictWarning(pn2, JSMSG_USELESS_EXPR))
michael@0 5414 return false;
michael@0 5415 }
michael@0 5416
michael@0 5417 return true;
michael@0 5418 }
michael@0 5419
michael@0 5420 static bool
michael@0 5421 EmitDelete(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
michael@0 5422 {
michael@0 5423 /*
michael@0 5424 * Under ECMA 3, deleting a non-reference returns true -- but alas we
michael@0 5425 * must evaluate the operand if it appears it might have side effects.
michael@0 5426 */
michael@0 5427 ParseNode *pn2 = pn->pn_kid;
michael@0 5428 switch (pn2->getKind()) {
michael@0 5429 case PNK_NAME:
michael@0 5430 {
michael@0 5431 if (!BindNameToSlot(cx, bce, pn2))
michael@0 5432 return false;
michael@0 5433 JSOp op = pn2->getOp();
michael@0 5434 if (op == JSOP_FALSE) {
michael@0 5435 if (Emit1(cx, bce, op) < 0)
michael@0 5436 return false;
michael@0 5437 } else {
michael@0 5438 if (!EmitAtomOp(cx, pn2, op, bce))
michael@0 5439 return false;
michael@0 5440 }
michael@0 5441 break;
michael@0 5442 }
michael@0 5443 case PNK_DOT:
michael@0 5444 if (!EmitPropOp(cx, pn2, JSOP_DELPROP, bce))
michael@0 5445 return false;
michael@0 5446 break;
michael@0 5447 case PNK_ELEM:
michael@0 5448 if (!EmitElemOp(cx, pn2, JSOP_DELELEM, bce))
michael@0 5449 return false;
michael@0 5450 break;
michael@0 5451 default:
michael@0 5452 {
michael@0 5453 /*
michael@0 5454 * If useless, just emit JSOP_TRUE; otherwise convert delete foo()
michael@0 5455 * to foo(), true (a comma expression).
michael@0 5456 */
michael@0 5457 bool useful = false;
michael@0 5458 if (!CheckSideEffects(cx, bce, pn2, &useful))
michael@0 5459 return false;
michael@0 5460
michael@0 5461 if (useful) {
michael@0 5462 JS_ASSERT_IF(pn2->isKind(PNK_CALL), !(pn2->pn_xflags & PNX_SETCALL));
michael@0 5463 if (!EmitTree(cx, bce, pn2))
michael@0 5464 return false;
michael@0 5465 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 5466 return false;
michael@0 5467 }
michael@0 5468
michael@0 5469 if (Emit1(cx, bce, JSOP_TRUE) < 0)
michael@0 5470 return false;
michael@0 5471 }
michael@0 5472 }
michael@0 5473
michael@0 5474 return true;
michael@0 5475 }
michael@0 5476
michael@0 5477 static bool
michael@0 5478 EmitArray(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, uint32_t count);
michael@0 5479
michael@0 5480 static bool
michael@0 5481 EmitCallOrNew(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
michael@0 5482 {
michael@0 5483 bool callop = pn->isKind(PNK_CALL);
michael@0 5484
michael@0 5485 /*
michael@0 5486 * Emit callable invocation or operator new (constructor call) code.
michael@0 5487 * First, emit code for the left operand to evaluate the callable or
michael@0 5488 * constructable object expression.
michael@0 5489 *
michael@0 5490 * For operator new, we emit JSOP_GETPROP instead of JSOP_CALLPROP, etc.
michael@0 5491 * This is necessary to interpose the lambda-initialized method read
michael@0 5492 * barrier -- see the code in jsinterp.cpp for JSOP_LAMBDA followed by
michael@0 5493 * JSOP_{SET,INIT}PROP.
michael@0 5494 *
michael@0 5495 * Then (or in a call case that has no explicit reference-base
michael@0 5496 * object) we emit JSOP_UNDEFINED to produce the undefined |this|
michael@0 5497 * value required for calls (which non-strict mode functions
michael@0 5498 * will box into the global object).
michael@0 5499 */
michael@0 5500 uint32_t argc = pn->pn_count - 1;
michael@0 5501
michael@0 5502 if (argc >= ARGC_LIMIT) {
michael@0 5503 bce->parser->tokenStream.reportError(callop
michael@0 5504 ? JSMSG_TOO_MANY_FUN_ARGS
michael@0 5505 : JSMSG_TOO_MANY_CON_ARGS);
michael@0 5506 return false;
michael@0 5507 }
michael@0 5508
michael@0 5509 bool emitArgs = true;
michael@0 5510 ParseNode *pn2 = pn->pn_head;
michael@0 5511 bool spread = JOF_OPTYPE(pn->getOp()) == JOF_BYTE;
michael@0 5512 switch (pn2->getKind()) {
michael@0 5513 case PNK_NAME:
michael@0 5514 if (bce->emitterMode == BytecodeEmitter::SelfHosting &&
michael@0 5515 pn2->name() == cx->names().callFunction &&
michael@0 5516 !spread)
michael@0 5517 {
michael@0 5518 /*
michael@0 5519 * Special-casing of callFunction to emit bytecode that directly
michael@0 5520 * invokes the callee with the correct |this| object and arguments.
michael@0 5521 * callFunction(fun, thisArg, arg0, arg1) thus becomes:
michael@0 5522 * - emit lookup for fun
michael@0 5523 * - emit lookup for thisArg
michael@0 5524 * - emit lookups for arg0, arg1
michael@0 5525 *
michael@0 5526 * argc is set to the amount of actually emitted args and the
michael@0 5527 * emitting of args below is disabled by setting emitArgs to false.
michael@0 5528 */
michael@0 5529 if (pn->pn_count < 3) {
michael@0 5530 bce->reportError(pn, JSMSG_MORE_ARGS_NEEDED, "callFunction", "1", "s");
michael@0 5531 return false;
michael@0 5532 }
michael@0 5533 ParseNode *funNode = pn2->pn_next;
michael@0 5534 if (!EmitTree(cx, bce, funNode))
michael@0 5535 return false;
michael@0 5536 ParseNode *thisArg = funNode->pn_next;
michael@0 5537 if (!EmitTree(cx, bce, thisArg))
michael@0 5538 return false;
michael@0 5539 bool oldEmittingForInit = bce->emittingForInit;
michael@0 5540 bce->emittingForInit = false;
michael@0 5541 for (ParseNode *argpn = thisArg->pn_next; argpn; argpn = argpn->pn_next) {
michael@0 5542 if (!EmitTree(cx, bce, argpn))
michael@0 5543 return false;
michael@0 5544 }
michael@0 5545 bce->emittingForInit = oldEmittingForInit;
michael@0 5546 argc -= 2;
michael@0 5547 emitArgs = false;
michael@0 5548 break;
michael@0 5549 }
michael@0 5550 if (!EmitNameOp(cx, bce, pn2, callop))
michael@0 5551 return false;
michael@0 5552 break;
michael@0 5553 case PNK_DOT:
michael@0 5554 if (!EmitPropOp(cx, pn2, callop ? JSOP_CALLPROP : JSOP_GETPROP, bce))
michael@0 5555 return false;
michael@0 5556 break;
michael@0 5557 case PNK_ELEM:
michael@0 5558 if (!EmitElemOp(cx, pn2, callop ? JSOP_CALLELEM : JSOP_GETELEM, bce))
michael@0 5559 return false;
michael@0 5560 break;
michael@0 5561 case PNK_FUNCTION:
michael@0 5562 /*
michael@0 5563 * Top level lambdas which are immediately invoked should be
michael@0 5564 * treated as only running once. Every time they execute we will
michael@0 5565 * create new types and scripts for their contents, to increase
michael@0 5566 * the quality of type information within them and enable more
michael@0 5567 * backend optimizations. Note that this does not depend on the
michael@0 5568 * lambda being invoked at most once (it may be named or be
michael@0 5569 * accessed via foo.caller indirection), as multiple executions
michael@0 5570 * will just cause the inner scripts to be repeatedly cloned.
michael@0 5571 */
michael@0 5572 JS_ASSERT(!bce->emittingRunOnceLambda);
michael@0 5573 if (bce->checkSingletonContext() || (!bce->isInLoop() && bce->isRunOnceLambda())) {
michael@0 5574 bce->emittingRunOnceLambda = true;
michael@0 5575 if (!EmitTree(cx, bce, pn2))
michael@0 5576 return false;
michael@0 5577 bce->emittingRunOnceLambda = false;
michael@0 5578 } else {
michael@0 5579 if (!EmitTree(cx, bce, pn2))
michael@0 5580 return false;
michael@0 5581 }
michael@0 5582 callop = false;
michael@0 5583 break;
michael@0 5584 default:
michael@0 5585 if (!EmitTree(cx, bce, pn2))
michael@0 5586 return false;
michael@0 5587 callop = false; /* trigger JSOP_UNDEFINED after */
michael@0 5588 break;
michael@0 5589 }
michael@0 5590 if (!callop) {
michael@0 5591 JSOp thisop = pn->isKind(PNK_GENEXP) ? JSOP_THIS : JSOP_UNDEFINED;
michael@0 5592 if (Emit1(cx, bce, thisop) < 0)
michael@0 5593 return false;
michael@0 5594 }
michael@0 5595
michael@0 5596 if (emitArgs) {
michael@0 5597 /*
michael@0 5598 * Emit code for each argument in order, then emit the JSOP_*CALL or
michael@0 5599 * JSOP_NEW bytecode with a two-byte immediate telling how many args
michael@0 5600 * were pushed on the operand stack.
michael@0 5601 */
michael@0 5602 bool oldEmittingForInit = bce->emittingForInit;
michael@0 5603 bce->emittingForInit = false;
michael@0 5604 if (!spread) {
michael@0 5605 for (ParseNode *pn3 = pn2->pn_next; pn3; pn3 = pn3->pn_next) {
michael@0 5606 if (!EmitTree(cx, bce, pn3))
michael@0 5607 return false;
michael@0 5608 }
michael@0 5609 } else {
michael@0 5610 if (!EmitArray(cx, bce, pn2->pn_next, argc))
michael@0 5611 return false;
michael@0 5612 }
michael@0 5613 bce->emittingForInit = oldEmittingForInit;
michael@0 5614 }
michael@0 5615
michael@0 5616 if (!spread) {
michael@0 5617 if (EmitCall(cx, bce, pn->getOp(), argc) < 0)
michael@0 5618 return false;
michael@0 5619 } else {
michael@0 5620 if (Emit1(cx, bce, pn->getOp()) < 0)
michael@0 5621 return false;
michael@0 5622 }
michael@0 5623 CheckTypeSet(cx, bce, pn->getOp());
michael@0 5624 if (pn->isOp(JSOP_EVAL) || pn->isOp(JSOP_SPREADEVAL)) {
michael@0 5625 uint32_t lineNum = bce->parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin);
michael@0 5626 EMIT_UINT16_IMM_OP(JSOP_LINENO, lineNum);
michael@0 5627 }
michael@0 5628 if (pn->pn_xflags & PNX_SETCALL) {
michael@0 5629 if (Emit1(cx, bce, JSOP_SETCALL) < 0)
michael@0 5630 return false;
michael@0 5631 }
michael@0 5632 return true;
michael@0 5633 }
michael@0 5634
michael@0 5635 static bool
michael@0 5636 EmitLogical(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
michael@0 5637 {
michael@0 5638 /*
michael@0 5639 * JSOP_OR converts the operand on the stack to boolean, leaves the original
michael@0 5640 * value on the stack and jumps if true; otherwise it falls into the next
michael@0 5641 * bytecode, which pops the left operand and then evaluates the right operand.
michael@0 5642 * The jump goes around the right operand evaluation.
michael@0 5643 *
michael@0 5644 * JSOP_AND converts the operand on the stack to boolean and jumps if false;
michael@0 5645 * otherwise it falls into the right operand's bytecode.
michael@0 5646 */
michael@0 5647
michael@0 5648 if (pn->isArity(PN_BINARY)) {
michael@0 5649 if (!EmitTree(cx, bce, pn->pn_left))
michael@0 5650 return false;
michael@0 5651 ptrdiff_t top = EmitJump(cx, bce, JSOP_BACKPATCH, 0);
michael@0 5652 if (top < 0)
michael@0 5653 return false;
michael@0 5654 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 5655 return false;
michael@0 5656 if (!EmitTree(cx, bce, pn->pn_right))
michael@0 5657 return false;
michael@0 5658 ptrdiff_t off = bce->offset();
michael@0 5659 jsbytecode *pc = bce->code(top);
michael@0 5660 SET_JUMP_OFFSET(pc, off - top);
michael@0 5661 *pc = pn->getOp();
michael@0 5662 return true;
michael@0 5663 }
michael@0 5664
michael@0 5665 JS_ASSERT(pn->isArity(PN_LIST));
michael@0 5666 JS_ASSERT(pn->pn_head->pn_next->pn_next);
michael@0 5667
michael@0 5668 /* Left-associative operator chain: avoid too much recursion. */
michael@0 5669 ParseNode *pn2 = pn->pn_head;
michael@0 5670 if (!EmitTree(cx, bce, pn2))
michael@0 5671 return false;
michael@0 5672 ptrdiff_t top = EmitJump(cx, bce, JSOP_BACKPATCH, 0);
michael@0 5673 if (top < 0)
michael@0 5674 return false;
michael@0 5675 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 5676 return false;
michael@0 5677
michael@0 5678 /* Emit nodes between the head and the tail. */
michael@0 5679 ptrdiff_t jmp = top;
michael@0 5680 while ((pn2 = pn2->pn_next)->pn_next) {
michael@0 5681 if (!EmitTree(cx, bce, pn2))
michael@0 5682 return false;
michael@0 5683 ptrdiff_t off = EmitJump(cx, bce, JSOP_BACKPATCH, 0);
michael@0 5684 if (off < 0)
michael@0 5685 return false;
michael@0 5686 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 5687 return false;
michael@0 5688 SET_JUMP_OFFSET(bce->code(jmp), off - jmp);
michael@0 5689 jmp = off;
michael@0 5690 }
michael@0 5691 if (!EmitTree(cx, bce, pn2))
michael@0 5692 return false;
michael@0 5693
michael@0 5694 pn2 = pn->pn_head;
michael@0 5695 ptrdiff_t off = bce->offset();
michael@0 5696 do {
michael@0 5697 jsbytecode *pc = bce->code(top);
michael@0 5698 ptrdiff_t tmp = GET_JUMP_OFFSET(pc);
michael@0 5699 SET_JUMP_OFFSET(pc, off - top);
michael@0 5700 *pc = pn->getOp();
michael@0 5701 top += tmp;
michael@0 5702 } while ((pn2 = pn2->pn_next)->pn_next);
michael@0 5703
michael@0 5704 return true;
michael@0 5705 }
michael@0 5706
michael@0 5707 /*
michael@0 5708 * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
michael@0 5709 * the comment on EmitSwitch.
michael@0 5710 */
michael@0 5711 MOZ_NEVER_INLINE static bool
michael@0 5712 EmitIncOrDec(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
michael@0 5713 {
michael@0 5714 /* Emit lvalue-specialized code for ++/-- operators. */
michael@0 5715 ParseNode *pn2 = pn->pn_kid;
michael@0 5716 switch (pn2->getKind()) {
michael@0 5717 case PNK_DOT:
michael@0 5718 if (!EmitPropIncDec(cx, pn, bce))
michael@0 5719 return false;
michael@0 5720 break;
michael@0 5721 case PNK_ELEM:
michael@0 5722 if (!EmitElemIncDec(cx, pn, bce))
michael@0 5723 return false;
michael@0 5724 break;
michael@0 5725 case PNK_CALL:
michael@0 5726 JS_ASSERT(pn2->pn_xflags & PNX_SETCALL);
michael@0 5727 if (!EmitTree(cx, bce, pn2))
michael@0 5728 return false;
michael@0 5729 break;
michael@0 5730 default:
michael@0 5731 JS_ASSERT(pn2->isKind(PNK_NAME));
michael@0 5732 pn2->setOp(JSOP_SETNAME);
michael@0 5733 if (!BindNameToSlot(cx, bce, pn2))
michael@0 5734 return false;
michael@0 5735 JSOp op = pn2->getOp();
michael@0 5736 bool maySet;
michael@0 5737 switch (op) {
michael@0 5738 case JSOP_SETLOCAL:
michael@0 5739 case JSOP_SETARG:
michael@0 5740 case JSOP_SETALIASEDVAR:
michael@0 5741 case JSOP_SETNAME:
michael@0 5742 case JSOP_SETGNAME:
michael@0 5743 maySet = true;
michael@0 5744 break;
michael@0 5745 default:
michael@0 5746 maySet = false;
michael@0 5747 }
michael@0 5748 if (op == JSOP_CALLEE) {
michael@0 5749 if (Emit1(cx, bce, op) < 0)
michael@0 5750 return false;
michael@0 5751 } else if (!pn2->pn_cookie.isFree()) {
michael@0 5752 if (maySet) {
michael@0 5753 if (!EmitVarIncDec(cx, pn, bce))
michael@0 5754 return false;
michael@0 5755 } else {
michael@0 5756 if (!EmitVarOp(cx, pn2, op, bce))
michael@0 5757 return false;
michael@0 5758 }
michael@0 5759 } else {
michael@0 5760 JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
michael@0 5761 if (maySet) {
michael@0 5762 if (!EmitNameIncDec(cx, pn, bce))
michael@0 5763 return false;
michael@0 5764 } else {
michael@0 5765 if (!EmitAtomOp(cx, pn2, op, bce))
michael@0 5766 return false;
michael@0 5767 }
michael@0 5768 break;
michael@0 5769 }
michael@0 5770 if (pn2->isConst()) {
michael@0 5771 if (Emit1(cx, bce, JSOP_POS) < 0)
michael@0 5772 return false;
michael@0 5773 bool post;
michael@0 5774 JSOp binop = GetIncDecInfo(pn->getKind(), &post);
michael@0 5775 if (!post) {
michael@0 5776 if (Emit1(cx, bce, JSOP_ONE) < 0)
michael@0 5777 return false;
michael@0 5778 if (Emit1(cx, bce, binop) < 0)
michael@0 5779 return false;
michael@0 5780 }
michael@0 5781 }
michael@0 5782 }
michael@0 5783 return true;
michael@0 5784 }
michael@0 5785
michael@0 5786 /*
michael@0 5787 * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
michael@0 5788 * the comment on EmitSwitch.
michael@0 5789 */
michael@0 5790 MOZ_NEVER_INLINE static bool
michael@0 5791 EmitLabeledStatement(ExclusiveContext *cx, BytecodeEmitter *bce, const LabeledStatement *pn)
michael@0 5792 {
michael@0 5793 /*
michael@0 5794 * Emit a JSOP_LABEL instruction. The argument is the offset to the statement
michael@0 5795 * following the labeled statement.
michael@0 5796 */
michael@0 5797 jsatomid index;
michael@0 5798 if (!bce->makeAtomIndex(pn->label(), &index))
michael@0 5799 return false;
michael@0 5800
michael@0 5801 ptrdiff_t top = EmitJump(cx, bce, JSOP_LABEL, 0);
michael@0 5802 if (top < 0)
michael@0 5803 return false;
michael@0 5804
michael@0 5805 /* Emit code for the labeled statement. */
michael@0 5806 StmtInfoBCE stmtInfo(cx);
michael@0 5807 PushStatementBCE(bce, &stmtInfo, STMT_LABEL, bce->offset());
michael@0 5808 stmtInfo.label = pn->label();
michael@0 5809 if (!EmitTree(cx, bce, pn->statement()))
michael@0 5810 return false;
michael@0 5811 if (!PopStatementBCE(cx, bce))
michael@0 5812 return false;
michael@0 5813
michael@0 5814 /* Patch the JSOP_LABEL offset. */
michael@0 5815 SetJumpOffsetAt(bce, top);
michael@0 5816 return true;
michael@0 5817 }
michael@0 5818
michael@0 5819 static bool
michael@0 5820 EmitSyntheticStatements(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
michael@0 5821 {
michael@0 5822 JS_ASSERT(pn->isArity(PN_LIST));
michael@0 5823 StmtInfoBCE stmtInfo(cx);
michael@0 5824 PushStatementBCE(bce, &stmtInfo, STMT_SEQ, top);
michael@0 5825 ParseNode *pn2 = pn->pn_head;
michael@0 5826 if (pn->pn_xflags & PNX_DESTRUCT)
michael@0 5827 pn2 = pn2->pn_next;
michael@0 5828 for (; pn2; pn2 = pn2->pn_next) {
michael@0 5829 if (!EmitTree(cx, bce, pn2))
michael@0 5830 return false;
michael@0 5831 }
michael@0 5832 return PopStatementBCE(cx, bce);
michael@0 5833 }
michael@0 5834
michael@0 5835 static bool
michael@0 5836 EmitConditionalExpression(ExclusiveContext *cx, BytecodeEmitter *bce, ConditionalExpression &conditional)
michael@0 5837 {
michael@0 5838 /* Emit the condition, then branch if false to the else part. */
michael@0 5839 if (!EmitTree(cx, bce, &conditional.condition()))
michael@0 5840 return false;
michael@0 5841 ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_COND);
michael@0 5842 if (noteIndex < 0)
michael@0 5843 return false;
michael@0 5844 ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFEQ, 0);
michael@0 5845 if (beq < 0 || !EmitTree(cx, bce, &conditional.thenExpression()))
michael@0 5846 return false;
michael@0 5847
michael@0 5848 /* Jump around else, fixup the branch, emit else, fixup jump. */
michael@0 5849 ptrdiff_t jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
michael@0 5850 if (jmp < 0)
michael@0 5851 return false;
michael@0 5852 SetJumpOffsetAt(bce, beq);
michael@0 5853
michael@0 5854 /*
michael@0 5855 * Because each branch pushes a single value, but our stack budgeting
michael@0 5856 * analysis ignores branches, we now have to adjust bce->stackDepth to
michael@0 5857 * ignore the value pushed by the first branch. Execution will follow
michael@0 5858 * only one path, so we must decrement bce->stackDepth.
michael@0 5859 *
michael@0 5860 * Failing to do this will foil code, such as let expression and block
michael@0 5861 * code generation, which must use the stack depth to compute local
michael@0 5862 * stack indexes correctly.
michael@0 5863 */
michael@0 5864 JS_ASSERT(bce->stackDepth > 0);
michael@0 5865 bce->stackDepth--;
michael@0 5866 if (!EmitTree(cx, bce, &conditional.elseExpression()))
michael@0 5867 return false;
michael@0 5868 SetJumpOffsetAt(bce, jmp);
michael@0 5869 return SetSrcNoteOffset(cx, bce, noteIndex, 0, jmp - beq);
michael@0 5870 }
michael@0 5871
michael@0 5872 /*
michael@0 5873 * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
michael@0 5874 * the comment on EmitSwitch.
michael@0 5875 */
michael@0 5876 MOZ_NEVER_INLINE static bool
michael@0 5877 EmitObject(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
michael@0 5878 {
michael@0 5879 if (pn->pn_xflags & PNX_DESTRUCT) {
michael@0 5880 bce->reportError(pn, JSMSG_BAD_OBJECT_INIT);
michael@0 5881 return false;
michael@0 5882 }
michael@0 5883
michael@0 5884 if (!(pn->pn_xflags & PNX_NONCONST) && pn->pn_head && bce->checkSingletonContext())
michael@0 5885 return EmitSingletonInitialiser(cx, bce, pn);
michael@0 5886
michael@0 5887 /*
michael@0 5888 * Emit code for {p:a, '%q':b, 2:c} that is equivalent to constructing
michael@0 5889 * a new object and defining (in source order) each property on the object
michael@0 5890 * (or mutating the object's [[Prototype]], in the case of __proto__).
michael@0 5891 */
michael@0 5892 ptrdiff_t offset = bce->offset();
michael@0 5893 if (!EmitNewInit(cx, bce, JSProto_Object))
michael@0 5894 return false;
michael@0 5895
michael@0 5896 /*
michael@0 5897 * Try to construct the shape of the object as we go, so we can emit a
michael@0 5898 * JSOP_NEWOBJECT with the final shape instead.
michael@0 5899 */
michael@0 5900 RootedObject obj(cx);
michael@0 5901 if (bce->script->compileAndGo()) {
michael@0 5902 gc::AllocKind kind = GuessObjectGCKind(pn->pn_count);
michael@0 5903 obj = NewBuiltinClassInstance(cx, &JSObject::class_, kind, TenuredObject);
michael@0 5904 if (!obj)
michael@0 5905 return false;
michael@0 5906 }
michael@0 5907
michael@0 5908 for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
michael@0 5909 if (!UpdateSourceCoordNotes(cx, bce, pn2->pn_pos.begin))
michael@0 5910 return false;
michael@0 5911
michael@0 5912 /* Emit an index for t[2] for later consumption by JSOP_INITELEM. */
michael@0 5913 ParseNode *pn3 = pn2->pn_left;
michael@0 5914 bool isIndex = false;
michael@0 5915 if (pn3->isKind(PNK_NUMBER)) {
michael@0 5916 if (!EmitNumberOp(cx, pn3->pn_dval, bce))
michael@0 5917 return false;
michael@0 5918 isIndex = true;
michael@0 5919 } else {
michael@0 5920 // The parser already checked for atoms representing indexes and
michael@0 5921 // used PNK_NUMBER instead, but also watch for ids which TI treats
michael@0 5922 // as indexes for simpliciation of downstream analysis.
michael@0 5923 JS_ASSERT(pn3->isKind(PNK_NAME) || pn3->isKind(PNK_STRING));
michael@0 5924 jsid id = NameToId(pn3->pn_atom->asPropertyName());
michael@0 5925 if (id != types::IdToTypeId(id)) {
michael@0 5926 if (!EmitTree(cx, bce, pn3))
michael@0 5927 return false;
michael@0 5928 isIndex = true;
michael@0 5929 }
michael@0 5930 }
michael@0 5931
michael@0 5932 /* Emit code for the property initializer. */
michael@0 5933 if (!EmitTree(cx, bce, pn2->pn_right))
michael@0 5934 return false;
michael@0 5935
michael@0 5936 JSOp op = pn2->getOp();
michael@0 5937 JS_ASSERT(op == JSOP_INITPROP ||
michael@0 5938 op == JSOP_INITPROP_GETTER ||
michael@0 5939 op == JSOP_INITPROP_SETTER);
michael@0 5940
michael@0 5941 if (op == JSOP_INITPROP_GETTER || op == JSOP_INITPROP_SETTER)
michael@0 5942 obj = nullptr;
michael@0 5943
michael@0 5944 if (isIndex) {
michael@0 5945 obj = nullptr;
michael@0 5946 switch (op) {
michael@0 5947 case JSOP_INITPROP: op = JSOP_INITELEM; break;
michael@0 5948 case JSOP_INITPROP_GETTER: op = JSOP_INITELEM_GETTER; break;
michael@0 5949 case JSOP_INITPROP_SETTER: op = JSOP_INITELEM_SETTER; break;
michael@0 5950 default: MOZ_ASSUME_UNREACHABLE("Invalid op");
michael@0 5951 }
michael@0 5952 if (Emit1(cx, bce, op) < 0)
michael@0 5953 return false;
michael@0 5954 } else {
michael@0 5955 JS_ASSERT(pn3->isKind(PNK_NAME) || pn3->isKind(PNK_STRING));
michael@0 5956
michael@0 5957 // If we have { __proto__: expr }, implement prototype mutation.
michael@0 5958 if (op == JSOP_INITPROP && pn3->pn_atom == cx->names().proto) {
michael@0 5959 obj = nullptr;
michael@0 5960 if (Emit1(cx, bce, JSOP_MUTATEPROTO) < 0)
michael@0 5961 return false;
michael@0 5962 continue;
michael@0 5963 }
michael@0 5964
michael@0 5965 jsatomid index;
michael@0 5966 if (!bce->makeAtomIndex(pn3->pn_atom, &index))
michael@0 5967 return false;
michael@0 5968
michael@0 5969 MOZ_ASSERT(op == JSOP_INITPROP ||
michael@0 5970 op == JSOP_INITPROP_GETTER ||
michael@0 5971 op == JSOP_INITPROP_SETTER);
michael@0 5972
michael@0 5973 if (obj) {
michael@0 5974 JS_ASSERT(!obj->inDictionaryMode());
michael@0 5975 Rooted<jsid> id(cx, AtomToId(pn3->pn_atom));
michael@0 5976 RootedValue undefinedValue(cx, UndefinedValue());
michael@0 5977 if (!DefineNativeProperty(cx, obj, id, undefinedValue, nullptr,
michael@0 5978 nullptr, JSPROP_ENUMERATE))
michael@0 5979 {
michael@0 5980 return false;
michael@0 5981 }
michael@0 5982 if (obj->inDictionaryMode())
michael@0 5983 obj = nullptr;
michael@0 5984 }
michael@0 5985
michael@0 5986 if (!EmitIndex32(cx, op, index, bce))
michael@0 5987 return false;
michael@0 5988 }
michael@0 5989 }
michael@0 5990
michael@0 5991 if (Emit1(cx, bce, JSOP_ENDINIT) < 0)
michael@0 5992 return false;
michael@0 5993
michael@0 5994 if (obj) {
michael@0 5995 /*
michael@0 5996 * The object survived and has a predictable shape: update the original
michael@0 5997 * bytecode.
michael@0 5998 */
michael@0 5999 ObjectBox *objbox = bce->parser->newObjectBox(obj);
michael@0 6000 if (!objbox)
michael@0 6001 return false;
michael@0 6002
michael@0 6003 static_assert(JSOP_NEWINIT_LENGTH == JSOP_NEWOBJECT_LENGTH,
michael@0 6004 "newinit and newobject must have equal length to edit in-place");
michael@0 6005
michael@0 6006 uint32_t index = bce->objectList.add(objbox);
michael@0 6007 jsbytecode *code = bce->code(offset);
michael@0 6008 code[0] = JSOP_NEWOBJECT;
michael@0 6009 code[1] = jsbytecode(index >> 24);
michael@0 6010 code[2] = jsbytecode(index >> 16);
michael@0 6011 code[3] = jsbytecode(index >> 8);
michael@0 6012 code[4] = jsbytecode(index);
michael@0 6013 }
michael@0 6014
michael@0 6015 return true;
michael@0 6016 }
michael@0 6017
michael@0 6018 static bool
michael@0 6019 EmitArrayComp(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
michael@0 6020 {
michael@0 6021 if (!EmitNewInit(cx, bce, JSProto_Array))
michael@0 6022 return false;
michael@0 6023
michael@0 6024 /*
michael@0 6025 * Pass the new array's stack index to the PNK_ARRAYPUSH case via
michael@0 6026 * bce->arrayCompDepth, then simply traverse the PNK_FOR node and
michael@0 6027 * its kids under pn2 to generate this comprehension.
michael@0 6028 */
michael@0 6029 JS_ASSERT(bce->stackDepth > 0);
michael@0 6030 uint32_t saveDepth = bce->arrayCompDepth;
michael@0 6031 bce->arrayCompDepth = (uint32_t) (bce->stackDepth - 1);
michael@0 6032 if (!EmitTree(cx, bce, pn->pn_head))
michael@0 6033 return false;
michael@0 6034 bce->arrayCompDepth = saveDepth;
michael@0 6035
michael@0 6036 /* Emit the usual op needed for decompilation. */
michael@0 6037 return Emit1(cx, bce, JSOP_ENDINIT) >= 0;
michael@0 6038 }
michael@0 6039
michael@0 6040 static bool
michael@0 6041 EmitArray(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, uint32_t count)
michael@0 6042 {
michael@0 6043 /*
michael@0 6044 * Emit code for [a, b, c] that is equivalent to constructing a new
michael@0 6045 * array and in source order evaluating each element value and adding
michael@0 6046 * it to the array, without invoking latent setters. We use the
michael@0 6047 * JSOP_NEWINIT and JSOP_INITELEM_ARRAY bytecodes to ignore setters and
michael@0 6048 * to avoid dup'ing and popping the array as each element is added, as
michael@0 6049 * JSOP_SETELEM/JSOP_SETPROP would do.
michael@0 6050 */
michael@0 6051
michael@0 6052 int32_t nspread = 0;
michael@0 6053 for (ParseNode *elt = pn; elt; elt = elt->pn_next) {
michael@0 6054 if (elt->isKind(PNK_SPREAD))
michael@0 6055 nspread++;
michael@0 6056 }
michael@0 6057
michael@0 6058 ptrdiff_t off = EmitN(cx, bce, JSOP_NEWARRAY, 3);
michael@0 6059 if (off < 0)
michael@0 6060 return false;
michael@0 6061 CheckTypeSet(cx, bce, JSOP_NEWARRAY);
michael@0 6062 jsbytecode *pc = bce->code(off);
michael@0 6063
michael@0 6064 // For arrays with spread, this is a very pessimistic allocation, the
michael@0 6065 // minimum possible final size.
michael@0 6066 SET_UINT24(pc, count - nspread);
michael@0 6067
michael@0 6068 ParseNode *pn2 = pn;
michael@0 6069 jsatomid atomIndex;
michael@0 6070 if (nspread && !EmitNumberOp(cx, 0, bce))
michael@0 6071 return false;
michael@0 6072 for (atomIndex = 0; pn2; atomIndex++, pn2 = pn2->pn_next) {
michael@0 6073 if (!UpdateSourceCoordNotes(cx, bce, pn2->pn_pos.begin))
michael@0 6074 return false;
michael@0 6075 if (pn2->isKind(PNK_ELISION)) {
michael@0 6076 if (Emit1(cx, bce, JSOP_HOLE) < 0)
michael@0 6077 return false;
michael@0 6078 } else {
michael@0 6079 ParseNode *expr = pn2->isKind(PNK_SPREAD) ? pn2->pn_kid : pn2;
michael@0 6080 if (!EmitTree(cx, bce, expr))
michael@0 6081 return false;
michael@0 6082 }
michael@0 6083 if (pn2->isKind(PNK_SPREAD)) {
michael@0 6084 if (Emit1(cx, bce, JSOP_SPREAD) < 0)
michael@0 6085 return false;
michael@0 6086 } else if (nspread) {
michael@0 6087 if (Emit1(cx, bce, JSOP_INITELEM_INC) < 0)
michael@0 6088 return false;
michael@0 6089 } else {
michael@0 6090 off = EmitN(cx, bce, JSOP_INITELEM_ARRAY, 3);
michael@0 6091 if (off < 0)
michael@0 6092 return false;
michael@0 6093 SET_UINT24(bce->code(off), atomIndex);
michael@0 6094 }
michael@0 6095 }
michael@0 6096 JS_ASSERT(atomIndex == count);
michael@0 6097 if (nspread) {
michael@0 6098 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 6099 return false;
michael@0 6100 }
michael@0 6101
michael@0 6102 /* Emit an op to finish the array and aid in decompilation. */
michael@0 6103 return Emit1(cx, bce, JSOP_ENDINIT) >= 0;
michael@0 6104 }
michael@0 6105
michael@0 6106 static bool
michael@0 6107 EmitUnary(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
michael@0 6108 {
michael@0 6109 if (!UpdateSourceCoordNotes(cx, bce, pn->pn_pos.begin))
michael@0 6110 return false;
michael@0 6111 /* Unary op, including unary +/-. */
michael@0 6112 JSOp op = pn->getOp();
michael@0 6113 ParseNode *pn2 = pn->pn_kid;
michael@0 6114
michael@0 6115 if (op == JSOP_TYPEOF && !pn2->isKind(PNK_NAME))
michael@0 6116 op = JSOP_TYPEOFEXPR;
michael@0 6117
michael@0 6118 bool oldEmittingForInit = bce->emittingForInit;
michael@0 6119 bce->emittingForInit = false;
michael@0 6120 if (!EmitTree(cx, bce, pn2))
michael@0 6121 return false;
michael@0 6122
michael@0 6123 bce->emittingForInit = oldEmittingForInit;
michael@0 6124 return Emit1(cx, bce, op) >= 0;
michael@0 6125 }
michael@0 6126
michael@0 6127 static bool
michael@0 6128 EmitDefaults(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
michael@0 6129 {
michael@0 6130 JS_ASSERT(pn->isKind(PNK_ARGSBODY));
michael@0 6131
michael@0 6132 ParseNode *arg, *pnlast = pn->last();
michael@0 6133 for (arg = pn->pn_head; arg != pnlast; arg = arg->pn_next) {
michael@0 6134 if (!(arg->pn_dflags & PND_DEFAULT) || !arg->isKind(PNK_NAME))
michael@0 6135 continue;
michael@0 6136 if (!BindNameToSlot(cx, bce, arg))
michael@0 6137 return false;
michael@0 6138 if (!EmitVarOp(cx, arg, JSOP_GETARG, bce))
michael@0 6139 return false;
michael@0 6140 if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
michael@0 6141 return false;
michael@0 6142 if (Emit1(cx, bce, JSOP_STRICTEQ) < 0)
michael@0 6143 return false;
michael@0 6144 // Emit source note to enable ion compilation.
michael@0 6145 if (NewSrcNote(cx, bce, SRC_IF) < 0)
michael@0 6146 return false;
michael@0 6147 ptrdiff_t jump = EmitJump(cx, bce, JSOP_IFEQ, 0);
michael@0 6148 if (jump < 0)
michael@0 6149 return false;
michael@0 6150 if (!EmitTree(cx, bce, arg->expr()))
michael@0 6151 return false;
michael@0 6152 if (!EmitVarOp(cx, arg, JSOP_SETARG, bce))
michael@0 6153 return false;
michael@0 6154 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 6155 return false;
michael@0 6156 SET_JUMP_OFFSET(bce->code(jump), bce->offset() - jump);
michael@0 6157 }
michael@0 6158
michael@0 6159 return true;
michael@0 6160 }
michael@0 6161
michael@0 6162 bool
michael@0 6163 frontend::EmitTree(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
michael@0 6164 {
michael@0 6165 JS_CHECK_RECURSION(cx, return false);
michael@0 6166
michael@0 6167 EmitLevelManager elm(bce);
michael@0 6168
michael@0 6169 bool ok = true;
michael@0 6170 ptrdiff_t top = bce->offset();
michael@0 6171 pn->pn_offset = top;
michael@0 6172
michael@0 6173 /* Emit notes to tell the current bytecode's source line number. */
michael@0 6174 if (!UpdateLineNumberNotes(cx, bce, pn->pn_pos.begin))
michael@0 6175 return false;
michael@0 6176
michael@0 6177 switch (pn->getKind()) {
michael@0 6178 case PNK_FUNCTION:
michael@0 6179 ok = EmitFunc(cx, bce, pn);
michael@0 6180 break;
michael@0 6181
michael@0 6182 case PNK_ARGSBODY:
michael@0 6183 {
michael@0 6184 RootedFunction fun(cx, bce->sc->asFunctionBox()->function());
michael@0 6185 ParseNode *pnlast = pn->last();
michael@0 6186
michael@0 6187 // Carefully emit everything in the right order:
michael@0 6188 // 1. Destructuring
michael@0 6189 // 2. Functions
michael@0 6190 // 3. Defaults
michael@0 6191 ParseNode *pnchild = pnlast->pn_head;
michael@0 6192 if (pnlast->pn_xflags & PNX_DESTRUCT) {
michael@0 6193 // Assign the destructuring arguments before defining any functions,
michael@0 6194 // see bug 419662.
michael@0 6195 JS_ASSERT(pnchild->isKind(PNK_SEMI));
michael@0 6196 JS_ASSERT(pnchild->pn_kid->isKind(PNK_VAR) || pnchild->pn_kid->isKind(PNK_CONST));
michael@0 6197 if (!EmitTree(cx, bce, pnchild))
michael@0 6198 return false;
michael@0 6199 pnchild = pnchild->pn_next;
michael@0 6200 }
michael@0 6201 if (pnlast->pn_xflags & PNX_FUNCDEFS) {
michael@0 6202 // This block contains top-level function definitions. To ensure
michael@0 6203 // that we emit the bytecode defining them before the rest of code
michael@0 6204 // in the block we use a separate pass over functions. During the
michael@0 6205 // main pass later the emitter will add JSOP_NOP with source notes
michael@0 6206 // for the function to preserve the original functions position
michael@0 6207 // when decompiling.
michael@0 6208 //
michael@0 6209 // Currently this is used only for functions, as compile-as-we go
michael@0 6210 // mode for scripts does not allow separate emitter passes.
michael@0 6211 for (ParseNode *pn2 = pnchild; pn2; pn2 = pn2->pn_next) {
michael@0 6212 if (pn2->isKind(PNK_FUNCTION) && pn2->functionIsHoisted()) {
michael@0 6213 if (!EmitTree(cx, bce, pn2))
michael@0 6214 return false;
michael@0 6215 }
michael@0 6216 }
michael@0 6217 }
michael@0 6218 bool hasDefaults = bce->sc->asFunctionBox()->hasDefaults();
michael@0 6219 if (hasDefaults) {
michael@0 6220 ParseNode *rest = nullptr;
michael@0 6221 bool restIsDefn = false;
michael@0 6222 if (fun->hasRest()) {
michael@0 6223 JS_ASSERT(!bce->sc->asFunctionBox()->argumentsHasLocalBinding());
michael@0 6224
michael@0 6225 // Defaults with a rest parameter need special handling. The
michael@0 6226 // rest parameter needs to be undefined while defaults are being
michael@0 6227 // processed. To do this, we create the rest argument and let it
michael@0 6228 // sit on the stack while processing defaults. The rest
michael@0 6229 // parameter's slot is set to undefined for the course of
michael@0 6230 // default processing.
michael@0 6231 rest = pn->pn_head;
michael@0 6232 while (rest->pn_next != pnlast)
michael@0 6233 rest = rest->pn_next;
michael@0 6234 restIsDefn = rest->isDefn();
michael@0 6235 if (Emit1(cx, bce, JSOP_REST) < 0)
michael@0 6236 return false;
michael@0 6237 CheckTypeSet(cx, bce, JSOP_REST);
michael@0 6238
michael@0 6239 // Only set the rest parameter if it's not aliased by a nested
michael@0 6240 // function in the body.
michael@0 6241 if (restIsDefn) {
michael@0 6242 if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
michael@0 6243 return false;
michael@0 6244 if (!BindNameToSlot(cx, bce, rest))
michael@0 6245 return false;
michael@0 6246 if (!EmitVarOp(cx, rest, JSOP_SETARG, bce))
michael@0 6247 return false;
michael@0 6248 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 6249 return false;
michael@0 6250 }
michael@0 6251 }
michael@0 6252 if (!EmitDefaults(cx, bce, pn))
michael@0 6253 return false;
michael@0 6254 if (fun->hasRest()) {
michael@0 6255 if (restIsDefn && !EmitVarOp(cx, rest, JSOP_SETARG, bce))
michael@0 6256 return false;
michael@0 6257 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 6258 return false;
michael@0 6259 }
michael@0 6260 }
michael@0 6261 for (ParseNode *pn2 = pn->pn_head; pn2 != pnlast; pn2 = pn2->pn_next) {
michael@0 6262 // Only bind the parameter if it's not aliased by a nested function
michael@0 6263 // in the body.
michael@0 6264 if (!pn2->isDefn())
michael@0 6265 continue;
michael@0 6266 if (!BindNameToSlot(cx, bce, pn2))
michael@0 6267 return false;
michael@0 6268 if (pn2->pn_next == pnlast && fun->hasRest() && !hasDefaults) {
michael@0 6269 // Fill rest parameter. We handled the case with defaults above.
michael@0 6270 JS_ASSERT(!bce->sc->asFunctionBox()->argumentsHasLocalBinding());
michael@0 6271 bce->switchToProlog();
michael@0 6272 if (Emit1(cx, bce, JSOP_REST) < 0)
michael@0 6273 return false;
michael@0 6274 CheckTypeSet(cx, bce, JSOP_REST);
michael@0 6275 if (!EmitVarOp(cx, pn2, JSOP_SETARG, bce))
michael@0 6276 return false;
michael@0 6277 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 6278 return false;
michael@0 6279 bce->switchToMain();
michael@0 6280 }
michael@0 6281 }
michael@0 6282 ok = EmitTree(cx, bce, pnlast);
michael@0 6283 break;
michael@0 6284 }
michael@0 6285
michael@0 6286 case PNK_IF:
michael@0 6287 ok = EmitIf(cx, bce, pn);
michael@0 6288 break;
michael@0 6289
michael@0 6290 case PNK_SWITCH:
michael@0 6291 ok = EmitSwitch(cx, bce, pn);
michael@0 6292 break;
michael@0 6293
michael@0 6294 case PNK_WHILE:
michael@0 6295 ok = EmitWhile(cx, bce, pn, top);
michael@0 6296 break;
michael@0 6297
michael@0 6298 case PNK_DOWHILE:
michael@0 6299 ok = EmitDo(cx, bce, pn);
michael@0 6300 break;
michael@0 6301
michael@0 6302 case PNK_FOR:
michael@0 6303 ok = EmitFor(cx, bce, pn, top);
michael@0 6304 break;
michael@0 6305
michael@0 6306 case PNK_BREAK:
michael@0 6307 ok = EmitBreak(cx, bce, pn->as<BreakStatement>().label());
michael@0 6308 break;
michael@0 6309
michael@0 6310 case PNK_CONTINUE:
michael@0 6311 ok = EmitContinue(cx, bce, pn->as<ContinueStatement>().label());
michael@0 6312 break;
michael@0 6313
michael@0 6314 case PNK_WITH:
michael@0 6315 ok = EmitWith(cx, bce, pn);
michael@0 6316 break;
michael@0 6317
michael@0 6318 case PNK_TRY:
michael@0 6319 if (!EmitTry(cx, bce, pn))
michael@0 6320 return false;
michael@0 6321 break;
michael@0 6322
michael@0 6323 case PNK_CATCH:
michael@0 6324 if (!EmitCatch(cx, bce, pn))
michael@0 6325 return false;
michael@0 6326 break;
michael@0 6327
michael@0 6328 case PNK_VAR:
michael@0 6329 case PNK_CONST:
michael@0 6330 if (!EmitVariables(cx, bce, pn, InitializeVars))
michael@0 6331 return false;
michael@0 6332 break;
michael@0 6333
michael@0 6334 case PNK_RETURN:
michael@0 6335 ok = EmitReturn(cx, bce, pn);
michael@0 6336 break;
michael@0 6337
michael@0 6338 case PNK_YIELD_STAR:
michael@0 6339 ok = EmitYieldStar(cx, bce, pn->pn_kid);
michael@0 6340 break;
michael@0 6341
michael@0 6342 case PNK_YIELD:
michael@0 6343 JS_ASSERT(bce->sc->isFunctionBox());
michael@0 6344 if (bce->sc->asFunctionBox()->isStarGenerator()) {
michael@0 6345 if (!EmitPrepareIteratorResult(cx, bce))
michael@0 6346 return false;
michael@0 6347 }
michael@0 6348 if (pn->pn_kid) {
michael@0 6349 if (!EmitTree(cx, bce, pn->pn_kid))
michael@0 6350 return false;
michael@0 6351 } else {
michael@0 6352 if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
michael@0 6353 return false;
michael@0 6354 }
michael@0 6355 if (bce->sc->asFunctionBox()->isStarGenerator()) {
michael@0 6356 if (!EmitFinishIteratorResult(cx, bce, false))
michael@0 6357 return false;
michael@0 6358 }
michael@0 6359 if (Emit1(cx, bce, JSOP_YIELD) < 0)
michael@0 6360 return false;
michael@0 6361 break;
michael@0 6362
michael@0 6363 case PNK_STATEMENTLIST:
michael@0 6364 ok = EmitStatementList(cx, bce, pn, top);
michael@0 6365 break;
michael@0 6366
michael@0 6367 case PNK_SEQ:
michael@0 6368 ok = EmitSyntheticStatements(cx, bce, pn, top);
michael@0 6369 break;
michael@0 6370
michael@0 6371 case PNK_SEMI:
michael@0 6372 ok = EmitStatement(cx, bce, pn);
michael@0 6373 break;
michael@0 6374
michael@0 6375 case PNK_LABEL:
michael@0 6376 ok = EmitLabeledStatement(cx, bce, &pn->as<LabeledStatement>());
michael@0 6377 break;
michael@0 6378
michael@0 6379 case PNK_COMMA:
michael@0 6380 {
michael@0 6381 for (ParseNode *pn2 = pn->pn_head; ; pn2 = pn2->pn_next) {
michael@0 6382 if (!UpdateSourceCoordNotes(cx, bce, pn2->pn_pos.begin))
michael@0 6383 return false;
michael@0 6384 if (!EmitTree(cx, bce, pn2))
michael@0 6385 return false;
michael@0 6386 if (!pn2->pn_next)
michael@0 6387 break;
michael@0 6388 if (Emit1(cx, bce, JSOP_POP) < 0)
michael@0 6389 return false;
michael@0 6390 }
michael@0 6391 break;
michael@0 6392 }
michael@0 6393
michael@0 6394 case PNK_ASSIGN:
michael@0 6395 case PNK_ADDASSIGN:
michael@0 6396 case PNK_SUBASSIGN:
michael@0 6397 case PNK_BITORASSIGN:
michael@0 6398 case PNK_BITXORASSIGN:
michael@0 6399 case PNK_BITANDASSIGN:
michael@0 6400 case PNK_LSHASSIGN:
michael@0 6401 case PNK_RSHASSIGN:
michael@0 6402 case PNK_URSHASSIGN:
michael@0 6403 case PNK_MULASSIGN:
michael@0 6404 case PNK_DIVASSIGN:
michael@0 6405 case PNK_MODASSIGN:
michael@0 6406 if (!EmitAssignment(cx, bce, pn->pn_left, pn->getOp(), pn->pn_right))
michael@0 6407 return false;
michael@0 6408 break;
michael@0 6409
michael@0 6410 case PNK_CONDITIONAL:
michael@0 6411 ok = EmitConditionalExpression(cx, bce, pn->as<ConditionalExpression>());
michael@0 6412 break;
michael@0 6413
michael@0 6414 case PNK_OR:
michael@0 6415 case PNK_AND:
michael@0 6416 ok = EmitLogical(cx, bce, pn);
michael@0 6417 break;
michael@0 6418
michael@0 6419 case PNK_ADD:
michael@0 6420 case PNK_SUB:
michael@0 6421 case PNK_BITOR:
michael@0 6422 case PNK_BITXOR:
michael@0 6423 case PNK_BITAND:
michael@0 6424 case PNK_STRICTEQ:
michael@0 6425 case PNK_EQ:
michael@0 6426 case PNK_STRICTNE:
michael@0 6427 case PNK_NE:
michael@0 6428 case PNK_LT:
michael@0 6429 case PNK_LE:
michael@0 6430 case PNK_GT:
michael@0 6431 case PNK_GE:
michael@0 6432 case PNK_IN:
michael@0 6433 case PNK_INSTANCEOF:
michael@0 6434 case PNK_LSH:
michael@0 6435 case PNK_RSH:
michael@0 6436 case PNK_URSH:
michael@0 6437 case PNK_STAR:
michael@0 6438 case PNK_DIV:
michael@0 6439 case PNK_MOD:
michael@0 6440 if (pn->isArity(PN_LIST)) {
michael@0 6441 /* Left-associative operator chain: avoid too much recursion. */
michael@0 6442 ParseNode *pn2 = pn->pn_head;
michael@0 6443 if (!EmitTree(cx, bce, pn2))
michael@0 6444 return false;
michael@0 6445 JSOp op = pn->getOp();
michael@0 6446 while ((pn2 = pn2->pn_next) != nullptr) {
michael@0 6447 if (!EmitTree(cx, bce, pn2))
michael@0 6448 return false;
michael@0 6449 if (Emit1(cx, bce, op) < 0)
michael@0 6450 return false;
michael@0 6451 }
michael@0 6452 } else {
michael@0 6453 /* Binary operators that evaluate both operands unconditionally. */
michael@0 6454 if (!EmitTree(cx, bce, pn->pn_left))
michael@0 6455 return false;
michael@0 6456 if (!EmitTree(cx, bce, pn->pn_right))
michael@0 6457 return false;
michael@0 6458 if (Emit1(cx, bce, pn->getOp()) < 0)
michael@0 6459 return false;
michael@0 6460 }
michael@0 6461 break;
michael@0 6462
michael@0 6463 case PNK_THROW:
michael@0 6464 case PNK_TYPEOF:
michael@0 6465 case PNK_VOID:
michael@0 6466 case PNK_NOT:
michael@0 6467 case PNK_BITNOT:
michael@0 6468 case PNK_POS:
michael@0 6469 case PNK_NEG:
michael@0 6470 ok = EmitUnary(cx, bce, pn);
michael@0 6471 break;
michael@0 6472
michael@0 6473 case PNK_PREINCREMENT:
michael@0 6474 case PNK_PREDECREMENT:
michael@0 6475 case PNK_POSTINCREMENT:
michael@0 6476 case PNK_POSTDECREMENT:
michael@0 6477 ok = EmitIncOrDec(cx, bce, pn);
michael@0 6478 break;
michael@0 6479
michael@0 6480 case PNK_DELETE:
michael@0 6481 ok = EmitDelete(cx, bce, pn);
michael@0 6482 break;
michael@0 6483
michael@0 6484 case PNK_DOT:
michael@0 6485 ok = EmitPropOp(cx, pn, JSOP_GETPROP, bce);
michael@0 6486 break;
michael@0 6487
michael@0 6488 case PNK_ELEM:
michael@0 6489 ok = EmitElemOp(cx, pn, JSOP_GETELEM, bce);
michael@0 6490 break;
michael@0 6491
michael@0 6492 case PNK_NEW:
michael@0 6493 case PNK_CALL:
michael@0 6494 case PNK_GENEXP:
michael@0 6495 ok = EmitCallOrNew(cx, bce, pn);
michael@0 6496 break;
michael@0 6497
michael@0 6498 case PNK_LEXICALSCOPE:
michael@0 6499 ok = EmitLexicalScope(cx, bce, pn);
michael@0 6500 break;
michael@0 6501
michael@0 6502 case PNK_LET:
michael@0 6503 ok = pn->isArity(PN_BINARY)
michael@0 6504 ? EmitLet(cx, bce, pn)
michael@0 6505 : EmitVariables(cx, bce, pn, InitializeVars);
michael@0 6506 break;
michael@0 6507
michael@0 6508 case PNK_IMPORT:
michael@0 6509 case PNK_EXPORT:
michael@0 6510 // TODO: Implement emitter support for modules
michael@0 6511 bce->reportError(nullptr, JSMSG_MODULES_NOT_IMPLEMENTED);
michael@0 6512 return false;
michael@0 6513
michael@0 6514 case PNK_ARRAYPUSH: {
michael@0 6515 /*
michael@0 6516 * The array object's stack index is in bce->arrayCompDepth. See below
michael@0 6517 * under the array initialiser code generator for array comprehension
michael@0 6518 * special casing. Note that the array object is a pure stack value,
michael@0 6519 * unaliased by blocks, so we can EmitUnaliasedVarOp.
michael@0 6520 */
michael@0 6521 if (!EmitTree(cx, bce, pn->pn_kid))
michael@0 6522 return false;
michael@0 6523 if (!EmitDupAt(cx, bce, bce->arrayCompDepth))
michael@0 6524 return false;
michael@0 6525 if (Emit1(cx, bce, JSOP_ARRAYPUSH) < 0)
michael@0 6526 return false;
michael@0 6527 break;
michael@0 6528 }
michael@0 6529
michael@0 6530 case PNK_ARRAY:
michael@0 6531 if (!(pn->pn_xflags & PNX_NONCONST) && pn->pn_head && bce->checkSingletonContext())
michael@0 6532 ok = EmitSingletonInitialiser(cx, bce, pn);
michael@0 6533 else
michael@0 6534 ok = EmitArray(cx, bce, pn->pn_head, pn->pn_count);
michael@0 6535 break;
michael@0 6536
michael@0 6537 case PNK_ARRAYCOMP:
michael@0 6538 ok = EmitArrayComp(cx, bce, pn);
michael@0 6539 break;
michael@0 6540
michael@0 6541 case PNK_OBJECT:
michael@0 6542 ok = EmitObject(cx, bce, pn);
michael@0 6543 break;
michael@0 6544
michael@0 6545 case PNK_NAME:
michael@0 6546 if (!EmitNameOp(cx, bce, pn, false))
michael@0 6547 return false;
michael@0 6548 break;
michael@0 6549
michael@0 6550 case PNK_STRING:
michael@0 6551 ok = EmitAtomOp(cx, pn, pn->getOp(), bce);
michael@0 6552 break;
michael@0 6553
michael@0 6554 case PNK_NUMBER:
michael@0 6555 ok = EmitNumberOp(cx, pn->pn_dval, bce);
michael@0 6556 break;
michael@0 6557
michael@0 6558 case PNK_REGEXP:
michael@0 6559 ok = EmitRegExp(cx, bce->regexpList.add(pn->as<RegExpLiteral>().objbox()), bce);
michael@0 6560 break;
michael@0 6561
michael@0 6562 case PNK_TRUE:
michael@0 6563 case PNK_FALSE:
michael@0 6564 case PNK_THIS:
michael@0 6565 case PNK_NULL:
michael@0 6566 if (Emit1(cx, bce, pn->getOp()) < 0)
michael@0 6567 return false;
michael@0 6568 break;
michael@0 6569
michael@0 6570 case PNK_DEBUGGER:
michael@0 6571 if (!UpdateSourceCoordNotes(cx, bce, pn->pn_pos.begin))
michael@0 6572 return false;
michael@0 6573 if (Emit1(cx, bce, JSOP_DEBUGGER) < 0)
michael@0 6574 return false;
michael@0 6575 break;
michael@0 6576
michael@0 6577 case PNK_NOP:
michael@0 6578 JS_ASSERT(pn->getArity() == PN_NULLARY);
michael@0 6579 break;
michael@0 6580
michael@0 6581 default:
michael@0 6582 JS_ASSERT(0);
michael@0 6583 }
michael@0 6584
michael@0 6585 /* bce->emitLevel == 1 means we're last on the stack, so finish up. */
michael@0 6586 if (ok && bce->emitLevel == 1) {
michael@0 6587 if (!UpdateSourceCoordNotes(cx, bce, pn->pn_pos.end))
michael@0 6588 return false;
michael@0 6589 }
michael@0 6590
michael@0 6591 return ok;
michael@0 6592 }
michael@0 6593
michael@0 6594 static int
michael@0 6595 AllocSrcNote(ExclusiveContext *cx, SrcNotesVector &notes)
michael@0 6596 {
michael@0 6597 // Start it off moderately large to avoid repeated resizings early on.
michael@0 6598 if (notes.capacity() == 0 && !notes.reserve(1024))
michael@0 6599 return -1;
michael@0 6600
michael@0 6601 jssrcnote dummy = 0;
michael@0 6602 if (!notes.append(dummy)) {
michael@0 6603 js_ReportOutOfMemory(cx);
michael@0 6604 return -1;
michael@0 6605 }
michael@0 6606 return notes.length() - 1;
michael@0 6607 }
michael@0 6608
michael@0 6609 int
michael@0 6610 frontend::NewSrcNote(ExclusiveContext *cx, BytecodeEmitter *bce, SrcNoteType type)
michael@0 6611 {
michael@0 6612 SrcNotesVector &notes = bce->notes();
michael@0 6613 int index;
michael@0 6614
michael@0 6615 index = AllocSrcNote(cx, notes);
michael@0 6616 if (index < 0)
michael@0 6617 return -1;
michael@0 6618
michael@0 6619 /*
michael@0 6620 * Compute delta from the last annotated bytecode's offset. If it's too
michael@0 6621 * big to fit in sn, allocate one or more xdelta notes and reset sn.
michael@0 6622 */
michael@0 6623 ptrdiff_t offset = bce->offset();
michael@0 6624 ptrdiff_t delta = offset - bce->lastNoteOffset();
michael@0 6625 bce->current->lastNoteOffset = offset;
michael@0 6626 if (delta >= SN_DELTA_LIMIT) {
michael@0 6627 do {
michael@0 6628 ptrdiff_t xdelta = Min(delta, SN_XDELTA_MASK);
michael@0 6629 SN_MAKE_XDELTA(&notes[index], xdelta);
michael@0 6630 delta -= xdelta;
michael@0 6631 index = AllocSrcNote(cx, notes);
michael@0 6632 if (index < 0)
michael@0 6633 return -1;
michael@0 6634 } while (delta >= SN_DELTA_LIMIT);
michael@0 6635 }
michael@0 6636
michael@0 6637 /*
michael@0 6638 * Initialize type and delta, then allocate the minimum number of notes
michael@0 6639 * needed for type's arity. Usually, we won't need more, but if an offset
michael@0 6640 * does take two bytes, SetSrcNoteOffset will grow notes.
michael@0 6641 */
michael@0 6642 SN_MAKE_NOTE(&notes[index], type, delta);
michael@0 6643 for (int n = (int)js_SrcNoteSpec[type].arity; n > 0; n--) {
michael@0 6644 if (NewSrcNote(cx, bce, SRC_NULL) < 0)
michael@0 6645 return -1;
michael@0 6646 }
michael@0 6647 return index;
michael@0 6648 }
michael@0 6649
michael@0 6650 int
michael@0 6651 frontend::NewSrcNote2(ExclusiveContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t offset)
michael@0 6652 {
michael@0 6653 int index;
michael@0 6654
michael@0 6655 index = NewSrcNote(cx, bce, type);
michael@0 6656 if (index >= 0) {
michael@0 6657 if (!SetSrcNoteOffset(cx, bce, index, 0, offset))
michael@0 6658 return -1;
michael@0 6659 }
michael@0 6660 return index;
michael@0 6661 }
michael@0 6662
michael@0 6663 int
michael@0 6664 frontend::NewSrcNote3(ExclusiveContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t offset1,
michael@0 6665 ptrdiff_t offset2)
michael@0 6666 {
michael@0 6667 int index;
michael@0 6668
michael@0 6669 index = NewSrcNote(cx, bce, type);
michael@0 6670 if (index >= 0) {
michael@0 6671 if (!SetSrcNoteOffset(cx, bce, index, 0, offset1))
michael@0 6672 return -1;
michael@0 6673 if (!SetSrcNoteOffset(cx, bce, index, 1, offset2))
michael@0 6674 return -1;
michael@0 6675 }
michael@0 6676 return index;
michael@0 6677 }
michael@0 6678
michael@0 6679 bool
michael@0 6680 frontend::AddToSrcNoteDelta(ExclusiveContext *cx, BytecodeEmitter *bce, jssrcnote *sn, ptrdiff_t delta)
michael@0 6681 {
michael@0 6682 /*
michael@0 6683 * Called only from FinishTakingSrcNotes to add to main script note
michael@0 6684 * deltas, and only by a small positive amount.
michael@0 6685 */
michael@0 6686 JS_ASSERT(bce->current == &bce->main);
michael@0 6687 JS_ASSERT((unsigned) delta < (unsigned) SN_XDELTA_LIMIT);
michael@0 6688
michael@0 6689 ptrdiff_t base = SN_DELTA(sn);
michael@0 6690 ptrdiff_t limit = SN_IS_XDELTA(sn) ? SN_XDELTA_LIMIT : SN_DELTA_LIMIT;
michael@0 6691 ptrdiff_t newdelta = base + delta;
michael@0 6692 if (newdelta < limit) {
michael@0 6693 SN_SET_DELTA(sn, newdelta);
michael@0 6694 } else {
michael@0 6695 jssrcnote xdelta;
michael@0 6696 SN_MAKE_XDELTA(&xdelta, delta);
michael@0 6697 if (!(sn = bce->main.notes.insert(sn, xdelta)))
michael@0 6698 return false;
michael@0 6699 }
michael@0 6700 return true;
michael@0 6701 }
michael@0 6702
michael@0 6703 static bool
michael@0 6704 SetSrcNoteOffset(ExclusiveContext *cx, BytecodeEmitter *bce, unsigned index, unsigned which,
michael@0 6705 ptrdiff_t offset)
michael@0 6706 {
michael@0 6707 if (size_t(offset) > SN_MAX_OFFSET) {
michael@0 6708 ReportStatementTooLarge(bce->parser->tokenStream, bce->topStmt);
michael@0 6709 return false;
michael@0 6710 }
michael@0 6711
michael@0 6712 SrcNotesVector &notes = bce->notes();
michael@0 6713
michael@0 6714 /* Find the offset numbered which (i.e., skip exactly which offsets). */
michael@0 6715 jssrcnote *sn = notes.begin() + index;
michael@0 6716 JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
michael@0 6717 JS_ASSERT((int) which < js_SrcNoteSpec[SN_TYPE(sn)].arity);
michael@0 6718 for (sn++; which; sn++, which--) {
michael@0 6719 if (*sn & SN_4BYTE_OFFSET_FLAG)
michael@0 6720 sn += 3;
michael@0 6721 }
michael@0 6722
michael@0 6723 /*
michael@0 6724 * See if the new offset requires three bytes either by being too big or if
michael@0 6725 * the offset has already been inflated (in which case, we need to stay big
michael@0 6726 * to not break the srcnote encoding if this isn't the last srcnote).
michael@0 6727 */
michael@0 6728 if (offset > (ptrdiff_t)SN_4BYTE_OFFSET_MASK || (*sn & SN_4BYTE_OFFSET_FLAG)) {
michael@0 6729 /* Maybe this offset was already set to a three-byte value. */
michael@0 6730 if (!(*sn & SN_4BYTE_OFFSET_FLAG)) {
michael@0 6731 /* Insert two dummy bytes that will be overwritten shortly. */
michael@0 6732 jssrcnote dummy = 0;
michael@0 6733 if (!(sn = notes.insert(sn, dummy)) ||
michael@0 6734 !(sn = notes.insert(sn, dummy)) ||
michael@0 6735 !(sn = notes.insert(sn, dummy)))
michael@0 6736 {
michael@0 6737 js_ReportOutOfMemory(cx);
michael@0 6738 return false;
michael@0 6739 }
michael@0 6740 }
michael@0 6741 *sn++ = (jssrcnote)(SN_4BYTE_OFFSET_FLAG | (offset >> 24));
michael@0 6742 *sn++ = (jssrcnote)(offset >> 16);
michael@0 6743 *sn++ = (jssrcnote)(offset >> 8);
michael@0 6744 }
michael@0 6745 *sn = (jssrcnote)offset;
michael@0 6746 return true;
michael@0 6747 }
michael@0 6748
michael@0 6749 /*
michael@0 6750 * Finish taking source notes in cx's notePool.
michael@0 6751 * If successful, the final source note count is stored in the out outparam.
michael@0 6752 */
michael@0 6753 bool
michael@0 6754 frontend::FinishTakingSrcNotes(ExclusiveContext *cx, BytecodeEmitter *bce, uint32_t *out)
michael@0 6755 {
michael@0 6756 JS_ASSERT(bce->current == &bce->main);
michael@0 6757
michael@0 6758 unsigned prologCount = bce->prolog.notes.length();
michael@0 6759 if (prologCount && bce->prolog.currentLine != bce->firstLine) {
michael@0 6760 bce->switchToProlog();
michael@0 6761 if (NewSrcNote2(cx, bce, SRC_SETLINE, (ptrdiff_t)bce->firstLine) < 0)
michael@0 6762 return false;
michael@0 6763 bce->switchToMain();
michael@0 6764 } else {
michael@0 6765 /*
michael@0 6766 * Either no prolog srcnotes, or no line number change over prolog.
michael@0 6767 * We don't need a SRC_SETLINE, but we may need to adjust the offset
michael@0 6768 * of the first main note, by adding to its delta and possibly even
michael@0 6769 * prepending SRC_XDELTA notes to it to account for prolog bytecodes
michael@0 6770 * that came at and after the last annotated bytecode.
michael@0 6771 */
michael@0 6772 ptrdiff_t offset = bce->prologOffset() - bce->prolog.lastNoteOffset;
michael@0 6773 JS_ASSERT(offset >= 0);
michael@0 6774 if (offset > 0 && bce->main.notes.length() != 0) {
michael@0 6775 /* NB: Use as much of the first main note's delta as we can. */
michael@0 6776 jssrcnote *sn = bce->main.notes.begin();
michael@0 6777 ptrdiff_t delta = SN_IS_XDELTA(sn)
michael@0 6778 ? SN_XDELTA_MASK - (*sn & SN_XDELTA_MASK)
michael@0 6779 : SN_DELTA_MASK - (*sn & SN_DELTA_MASK);
michael@0 6780 if (offset < delta)
michael@0 6781 delta = offset;
michael@0 6782 for (;;) {
michael@0 6783 if (!AddToSrcNoteDelta(cx, bce, sn, delta))
michael@0 6784 return false;
michael@0 6785 offset -= delta;
michael@0 6786 if (offset == 0)
michael@0 6787 break;
michael@0 6788 delta = Min(offset, SN_XDELTA_MASK);
michael@0 6789 sn = bce->main.notes.begin();
michael@0 6790 }
michael@0 6791 }
michael@0 6792 }
michael@0 6793
michael@0 6794 // The prolog count might have changed, so we can't reuse prologCount.
michael@0 6795 // The + 1 is to account for the final SN_MAKE_TERMINATOR that is appended
michael@0 6796 // when the notes are copied to their final destination by CopySrcNotes.
michael@0 6797 *out = bce->prolog.notes.length() + bce->main.notes.length() + 1;
michael@0 6798 return true;
michael@0 6799 }
michael@0 6800
michael@0 6801 void
michael@0 6802 frontend::CopySrcNotes(BytecodeEmitter *bce, jssrcnote *destination, uint32_t nsrcnotes)
michael@0 6803 {
michael@0 6804 unsigned prologCount = bce->prolog.notes.length();
michael@0 6805 unsigned mainCount = bce->main.notes.length();
michael@0 6806 unsigned totalCount = prologCount + mainCount;
michael@0 6807 MOZ_ASSERT(totalCount == nsrcnotes - 1);
michael@0 6808 if (prologCount)
michael@0 6809 PodCopy(destination, bce->prolog.notes.begin(), prologCount);
michael@0 6810 PodCopy(destination + prologCount, bce->main.notes.begin(), mainCount);
michael@0 6811 SN_MAKE_TERMINATOR(&destination[totalCount]);
michael@0 6812 }
michael@0 6813
michael@0 6814 void
michael@0 6815 CGConstList::finish(ConstArray *array)
michael@0 6816 {
michael@0 6817 JS_ASSERT(length() == array->length);
michael@0 6818
michael@0 6819 for (unsigned i = 0; i < length(); i++)
michael@0 6820 array->vector[i] = list[i];
michael@0 6821 }
michael@0 6822
michael@0 6823 /*
michael@0 6824 * Find the index of the given object for code generator.
michael@0 6825 *
michael@0 6826 * Since the emitter refers to each parsed object only once, for the index we
michael@0 6827 * use the number of already indexes objects. We also add the object to a list
michael@0 6828 * to convert the list to a fixed-size array when we complete code generation,
michael@0 6829 * see js::CGObjectList::finish below.
michael@0 6830 *
michael@0 6831 * Most of the objects go to BytecodeEmitter::objectList but for regexp we use
michael@0 6832 * a separated BytecodeEmitter::regexpList. In this way the emitted index can
michael@0 6833 * be directly used to store and fetch a reference to a cloned RegExp object
michael@0 6834 * that shares the same JSRegExp private data created for the object literal in
michael@0 6835 * objbox. We need a cloned object to hold lastIndex and other direct
michael@0 6836 * properties that should not be shared among threads sharing a precompiled
michael@0 6837 * function or script.
michael@0 6838 *
michael@0 6839 * If the code being compiled is function code, allocate a reserved slot in
michael@0 6840 * the cloned function object that shares its precompiled script with other
michael@0 6841 * cloned function objects and with the compiler-created clone-parent. There
michael@0 6842 * are nregexps = script->regexps()->length such reserved slots in each
michael@0 6843 * function object cloned from fun->object. NB: during compilation, a funobj
michael@0 6844 * slots element must never be allocated, because JSObject::allocSlot could
michael@0 6845 * hand out one of the slots that should be given to a regexp clone.
michael@0 6846 *
michael@0 6847 * If the code being compiled is global code, the cloned regexp are stored in
michael@0 6848 * fp->vars slot and to protect regexp slots from GC we set fp->nvars to
michael@0 6849 * nregexps.
michael@0 6850 *
michael@0 6851 * The slots initially contain undefined or null. We populate them lazily when
michael@0 6852 * JSOP_REGEXP is executed for the first time.
michael@0 6853 *
michael@0 6854 * Why clone regexp objects? ECMA specifies that when a regular expression
michael@0 6855 * literal is scanned, a RegExp object is created. In the spec, compilation
michael@0 6856 * and execution happen indivisibly, but in this implementation and many of
michael@0 6857 * its embeddings, code is precompiled early and re-executed in multiple
michael@0 6858 * threads, or using multiple global objects, or both, for efficiency.
michael@0 6859 *
michael@0 6860 * In such cases, naively following ECMA leads to wrongful sharing of RegExp
michael@0 6861 * objects, which makes for collisions on the lastIndex property (especially
michael@0 6862 * for global regexps) and on any ad-hoc properties. Also, __proto__ refers to
michael@0 6863 * the pre-compilation prototype, a pigeon-hole problem for instanceof tests.
michael@0 6864 */
michael@0 6865 unsigned
michael@0 6866 CGObjectList::add(ObjectBox *objbox)
michael@0 6867 {
michael@0 6868 JS_ASSERT(!objbox->emitLink);
michael@0 6869 objbox->emitLink = lastbox;
michael@0 6870 lastbox = objbox;
michael@0 6871 return length++;
michael@0 6872 }
michael@0 6873
michael@0 6874 unsigned
michael@0 6875 CGObjectList::indexOf(JSObject *obj)
michael@0 6876 {
michael@0 6877 JS_ASSERT(length > 0);
michael@0 6878 unsigned index = length - 1;
michael@0 6879 for (ObjectBox *box = lastbox; box->object != obj; box = box->emitLink)
michael@0 6880 index--;
michael@0 6881 return index;
michael@0 6882 }
michael@0 6883
michael@0 6884 void
michael@0 6885 CGObjectList::finish(ObjectArray *array)
michael@0 6886 {
michael@0 6887 JS_ASSERT(length <= INDEX_LIMIT);
michael@0 6888 JS_ASSERT(length == array->length);
michael@0 6889
michael@0 6890 js::HeapPtrObject *cursor = array->vector + array->length;
michael@0 6891 ObjectBox *objbox = lastbox;
michael@0 6892 do {
michael@0 6893 --cursor;
michael@0 6894 JS_ASSERT(!*cursor);
michael@0 6895 *cursor = objbox->object;
michael@0 6896 } while ((objbox = objbox->emitLink) != nullptr);
michael@0 6897 JS_ASSERT(cursor == array->vector);
michael@0 6898 }
michael@0 6899
michael@0 6900 ObjectBox*
michael@0 6901 CGObjectList::find(uint32_t index)
michael@0 6902 {
michael@0 6903 JS_ASSERT(index < length);
michael@0 6904 ObjectBox *box = lastbox;
michael@0 6905 for (unsigned n = length - 1; n > index; n--)
michael@0 6906 box = box->emitLink;
michael@0 6907 return box;
michael@0 6908 }
michael@0 6909
michael@0 6910 bool
michael@0 6911 CGTryNoteList::append(JSTryNoteKind kind, uint32_t stackDepth, size_t start, size_t end)
michael@0 6912 {
michael@0 6913 JS_ASSERT(start <= end);
michael@0 6914 JS_ASSERT(size_t(uint32_t(start)) == start);
michael@0 6915 JS_ASSERT(size_t(uint32_t(end)) == end);
michael@0 6916
michael@0 6917 JSTryNote note;
michael@0 6918 note.kind = kind;
michael@0 6919 note.stackDepth = stackDepth;
michael@0 6920 note.start = uint32_t(start);
michael@0 6921 note.length = uint32_t(end - start);
michael@0 6922
michael@0 6923 return list.append(note);
michael@0 6924 }
michael@0 6925
michael@0 6926 void
michael@0 6927 CGTryNoteList::finish(TryNoteArray *array)
michael@0 6928 {
michael@0 6929 JS_ASSERT(length() == array->length);
michael@0 6930
michael@0 6931 for (unsigned i = 0; i < length(); i++)
michael@0 6932 array->vector[i] = list[i];
michael@0 6933 }
michael@0 6934
michael@0 6935 bool
michael@0 6936 CGBlockScopeList::append(uint32_t scopeObject, uint32_t offset, uint32_t parent)
michael@0 6937 {
michael@0 6938 BlockScopeNote note;
michael@0 6939 mozilla::PodZero(&note);
michael@0 6940
michael@0 6941 note.index = scopeObject;
michael@0 6942 note.start = offset;
michael@0 6943 note.parent = parent;
michael@0 6944
michael@0 6945 return list.append(note);
michael@0 6946 }
michael@0 6947
michael@0 6948 uint32_t
michael@0 6949 CGBlockScopeList::findEnclosingScope(uint32_t index)
michael@0 6950 {
michael@0 6951 JS_ASSERT(index < length());
michael@0 6952 JS_ASSERT(list[index].index != BlockScopeNote::NoBlockScopeIndex);
michael@0 6953
michael@0 6954 DebugOnly<uint32_t> pos = list[index].start;
michael@0 6955 while (index--) {
michael@0 6956 JS_ASSERT(list[index].start <= pos);
michael@0 6957 if (list[index].length == 0) {
michael@0 6958 // We are looking for the nearest enclosing live scope. If the
michael@0 6959 // scope contains POS, it should still be open, so its length should
michael@0 6960 // be zero.
michael@0 6961 return list[index].index;
michael@0 6962 } else {
michael@0 6963 // Conversely, if the length is not zero, it should not contain
michael@0 6964 // POS.
michael@0 6965 JS_ASSERT(list[index].start + list[index].length <= pos);
michael@0 6966 }
michael@0 6967 }
michael@0 6968
michael@0 6969 return BlockScopeNote::NoBlockScopeIndex;
michael@0 6970 }
michael@0 6971
michael@0 6972 void
michael@0 6973 CGBlockScopeList::recordEnd(uint32_t index, uint32_t offset)
michael@0 6974 {
michael@0 6975 JS_ASSERT(index < length());
michael@0 6976 JS_ASSERT(offset >= list[index].start);
michael@0 6977 JS_ASSERT(list[index].length == 0);
michael@0 6978
michael@0 6979 list[index].length = offset - list[index].start;
michael@0 6980 }
michael@0 6981
michael@0 6982 void
michael@0 6983 CGBlockScopeList::finish(BlockScopeArray *array)
michael@0 6984 {
michael@0 6985 JS_ASSERT(length() == array->length);
michael@0 6986
michael@0 6987 for (unsigned i = 0; i < length(); i++)
michael@0 6988 array->vector[i] = list[i];
michael@0 6989 }
michael@0 6990
michael@0 6991 /*
michael@0 6992 * We should try to get rid of offsetBias (always 0 or 1, where 1 is
michael@0 6993 * JSOP_{NOP,POP}_LENGTH), which is used only by SRC_FOR.
michael@0 6994 */
michael@0 6995 const JSSrcNoteSpec js_SrcNoteSpec[] = {
michael@0 6996 #define DEFINE_SRC_NOTE_SPEC(sym, name, arity) { name, arity },
michael@0 6997 FOR_EACH_SRC_NOTE_TYPE(DEFINE_SRC_NOTE_SPEC)
michael@0 6998 #undef DEFINE_SRC_NOTE_SPEC
michael@0 6999 };
michael@0 7000
michael@0 7001 static int
michael@0 7002 SrcNoteArity(jssrcnote *sn)
michael@0 7003 {
michael@0 7004 JS_ASSERT(SN_TYPE(sn) < SRC_LAST);
michael@0 7005 return js_SrcNoteSpec[SN_TYPE(sn)].arity;
michael@0 7006 }
michael@0 7007
michael@0 7008 JS_FRIEND_API(unsigned)
michael@0 7009 js_SrcNoteLength(jssrcnote *sn)
michael@0 7010 {
michael@0 7011 unsigned arity;
michael@0 7012 jssrcnote *base;
michael@0 7013
michael@0 7014 arity = SrcNoteArity(sn);
michael@0 7015 for (base = sn++; arity; sn++, arity--) {
michael@0 7016 if (*sn & SN_4BYTE_OFFSET_FLAG)
michael@0 7017 sn += 3;
michael@0 7018 }
michael@0 7019 return sn - base;
michael@0 7020 }
michael@0 7021
michael@0 7022 JS_FRIEND_API(ptrdiff_t)
michael@0 7023 js_GetSrcNoteOffset(jssrcnote *sn, unsigned which)
michael@0 7024 {
michael@0 7025 /* Find the offset numbered which (i.e., skip exactly which offsets). */
michael@0 7026 JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
michael@0 7027 JS_ASSERT((int) which < SrcNoteArity(sn));
michael@0 7028 for (sn++; which; sn++, which--) {
michael@0 7029 if (*sn & SN_4BYTE_OFFSET_FLAG)
michael@0 7030 sn += 3;
michael@0 7031 }
michael@0 7032 if (*sn & SN_4BYTE_OFFSET_FLAG) {
michael@0 7033 return (ptrdiff_t)(((uint32_t)(sn[0] & SN_4BYTE_OFFSET_MASK) << 24)
michael@0 7034 | (sn[1] << 16)
michael@0 7035 | (sn[2] << 8)
michael@0 7036 | sn[3]);
michael@0 7037 }
michael@0 7038 return (ptrdiff_t)*sn;
michael@0 7039 }

mercurial