Back to index

lightning-sunbird  0.9+nobinonly
Classes | Defines | Typedefs | Enumerations | Functions | Variables
jsemit.h File Reference
#include "jsstddef.h"
#include "jstypes.h"
#include "jsatom.h"
#include "jsopcode.h"
#include "jsprvtd.h"
#include "jspubtd.h"
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Classes

struct  JSStmtInfo
struct  JSTreeContext
struct  JSSpanDep
struct  JSJumpTarget
struct  JSCodeGenerator
struct  JSSrcNoteSpec
struct  JSCodeGenerator.prolog
struct  JSCodeGenerator.main
struct  JSCodeGenerator.current

Defines

#define STMT_TYPE_IN_RANGE(t, b, e)   ((uint)((t) - (b)) <= (uintN)((e) - (b)))
#define STMT_TYPE_MAYBE_SCOPE(type)
#define STMT_TYPE_LINKS_SCOPE(type)   STMT_TYPE_IN_RANGE(type, STMT_WITH, STMT_CATCH)
#define STMT_TYPE_IS_TRYING(type)   STMT_TYPE_IN_RANGE(type, STMT_TRY, STMT_SUBROUTINE)
#define STMT_TYPE_IS_LOOP(type)   ((type) >= STMT_DO_LOOP)
#define STMT_MAYBE_SCOPE(stmt)   STMT_TYPE_MAYBE_SCOPE((stmt)->type)
#define STMT_LINKS_SCOPE(stmt)
#define STMT_IS_TRYING(stmt)   STMT_TYPE_IS_TRYING((stmt)->type)
#define STMT_IS_LOOP(stmt)   STMT_TYPE_IS_LOOP((stmt)->type)
#define SIF_SCOPE   0x0001 /* statement has its own lexical scope */
#define SIF_BODY_BLOCK   0x0002 /* STMT_BLOCK type is a function body */
#define CATCHNOTE(stmt)   ((stmt).update)
#define GOSUBS(stmt)   ((stmt).breaks)
#define GUARDJUMP(stmt)   ((stmt).continues)
#define AT_TOP_LEVEL(tc)   (!(tc)->topStmt || ((tc)->topStmt->flags & SIF_BODY_BLOCK))
#define SET_STATEMENT_TOP(stmt, top)   ((stmt)->update = (top), (stmt)->breaks = (stmt)->continues = (-1))
#define TCF_COMPILING   0x01 /* generating bytecode; this tc is a cg */
#define TCF_IN_FUNCTION   0x02 /* parsing inside function body */
#define TCF_RETURN_EXPR   0x04 /* function has 'return expr;' */
#define TCF_RETURN_VOID   0x08 /* function has 'return;' */
#define TCF_RETURN_FLAGS   0x0C /* propagate these out of blocks */
#define TCF_IN_FOR_INIT   0x10 /* parsing init expr of for; exclude 'in' */
#define TCF_FUN_CLOSURE_VS_VAR   0x20 /* function and var with same name */
#define TCF_FUN_USES_NONLOCALS   0x40 /* function refers to non-local names */
#define TCF_FUN_HEAVYWEIGHT   0x80 /* function needs Call object per call */
#define TCF_FUN_IS_GENERATOR   0x100 /* parsed yield statement in function */
#define TCF_FUN_FLAGS   0x1E0 /* flags to propagate from FunctionBody */
#define TCF_HAS_DEFXMLNS   0x200 /* default xml namespace = ...; parsed */
#define TCF_HAS_FUNCTION_STMT   0x400 /* block contains a function statement */
#define TREE_CONTEXT_INIT(tc)
#define TREE_CONTEXT_FINISH(tc)   ((void)0)
#define JT_LEFT   0
#define JT_RIGHT   1
#define JT_OTHER_DIR(dir)   (1 - (dir))
#define JT_IMBALANCE(dir)   (((dir) << 1) - 1)
#define JT_DIR(imbalance)   (((imbalance) + 1) >> 1)
#define JT_TAG_BIT   ((jsword) 1)
#define JT_UNTAG_SHIFT   1
#define JT_SET_TAG(jt)   ((JSJumpTarget *)((jsword)(jt) | JT_TAG_BIT))
#define JT_CLR_TAG(jt)   ((JSJumpTarget *)((jsword)(jt) & ~JT_TAG_BIT))
#define JT_HAS_TAG(jt)   ((jsword)(jt) & JT_TAG_BIT)
#define BITS_PER_PTRDIFF   (sizeof(ptrdiff_t) * JS_BITS_PER_BYTE)
#define BITS_PER_BPDELTA   (BITS_PER_PTRDIFF - 1 - JT_UNTAG_SHIFT)
#define BPDELTA_MAX   (((ptrdiff_t)1 << BITS_PER_BPDELTA) - 1)
#define BPDELTA_TO_JT(bp)   ((JSJumpTarget *)((bp) << JT_UNTAG_SHIFT))
#define JT_TO_BPDELTA(jt)   ((ptrdiff_t)((jsword)(jt) >> JT_UNTAG_SHIFT))
#define SD_SET_TARGET(sd, jt)   ((sd)->target = JT_SET_TAG(jt))
#define SD_GET_TARGET(sd)
#define SD_SET_BPDELTA(sd, bp)   ((sd)->target = BPDELTA_TO_JT(bp))
#define SD_GET_BPDELTA(sd)
#define SD_SPAN(sd, pivot)
#define CG_BASE(cg)   ((cg)->current->base)
#define CG_LIMIT(cg)   ((cg)->current->limit)
#define CG_NEXT(cg)   ((cg)->current->next)
#define CG_CODE(cg, offset)   (CG_BASE(cg) + (offset))
#define CG_OFFSET(cg)   PTRDIFF(CG_NEXT(cg), CG_BASE(cg), jsbytecode)
#define CG_NOTES(cg)   ((cg)->current->notes)
#define CG_NOTE_COUNT(cg)   ((cg)->current->noteCount)
#define CG_NOTE_MASK(cg)   ((cg)->current->noteMask)
#define CG_LAST_NOTE_OFFSET(cg)   ((cg)->current->lastNoteOffset)
#define CG_CURRENT_LINE(cg)   ((cg)->current->currentLine)
#define CG_PROLOG_BASE(cg)   ((cg)->prolog.base)
#define CG_PROLOG_LIMIT(cg)   ((cg)->prolog.limit)
#define CG_PROLOG_NEXT(cg)   ((cg)->prolog.next)
#define CG_PROLOG_CODE(cg, poff)   (CG_PROLOG_BASE(cg) + (poff))
#define CG_PROLOG_OFFSET(cg)
#define CG_SWITCH_TO_MAIN(cg)   ((cg)->current = &(cg)->main)
#define CG_SWITCH_TO_PROLOG(cg)   ((cg)->current = &(cg)->prolog)
#define CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, off)
#define CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, off)   CHECK_AND_SET_JUMP_OFFSET(cx, cg, CG_CODE(cg,off), CG_OFFSET(cg) - (off))
#define js_InWithStatement(tc)   js_InStatement(tc, STMT_WITH)
#define SRC_DECL_VAR   0
#define SRC_DECL_CONST   1
#define SRC_DECL_LET   2
#define SRC_DECL_NONE   3
#define SN_TYPE_BITS   5
#define SN_DELTA_BITS   3
#define SN_XDELTA_BITS   6
#define SN_TYPE_MASK   (JS_BITMASK(SN_TYPE_BITS) << SN_DELTA_BITS)
#define SN_DELTA_MASK   ((ptrdiff_t)JS_BITMASK(SN_DELTA_BITS))
#define SN_XDELTA_MASK   ((ptrdiff_t)JS_BITMASK(SN_XDELTA_BITS))
#define SN_MAKE_NOTE(sn, t, d)
#define SN_MAKE_XDELTA(sn, d)
#define SN_IS_XDELTA(sn)   ((*(sn) >> SN_DELTA_BITS) >= SRC_XDELTA)
#define SN_TYPE(sn)
#define SN_SET_TYPE(sn, type)   SN_MAKE_NOTE(sn, type, SN_DELTA(sn))
#define SN_IS_GETTABLE(sn)   (SN_TYPE(sn) < SRC_NEWLINE)
#define SN_DELTA(sn)
#define SN_SET_DELTA(sn, delta)
#define SN_DELTA_LIMIT   ((ptrdiff_t)JS_BIT(SN_DELTA_BITS))
#define SN_XDELTA_LIMIT   ((ptrdiff_t)JS_BIT(SN_XDELTA_BITS))
#define SN_3BYTE_OFFSET_FLAG   0x80
#define SN_3BYTE_OFFSET_MASK   0x7f
#define SN_LENGTH(sn)
#define SN_NEXT(sn)   ((sn) + SN_LENGTH(sn))
#define SN_MAKE_TERMINATOR(sn)   (*(sn) = SRC_NULL)
#define SN_IS_TERMINATOR(sn)   (*(sn) == SRC_NULL)
#define CG_COUNT_FINAL_SRCNOTES(cg, cnt)
#define CG_COUNT_FINAL_TRYNOTES(cg, cnt)

Typedefs

typedef JS_BEGIN_EXTERN_C enum
JSStmtType 
JSStmtType
typedef struct JSStmtInfo
typedef struct JSSpanDep
typedef struct JSJumpTarget
typedef enum JSSrcNoteType JSSrcNoteType
typedef struct JSSrcNoteSpec JSSrcNoteSpec

Enumerations

enum  JSStmtType {
  STMT_LABEL, STMT_IF, STMT_ELSE, STMT_BODY,
  STMT_BLOCK, STMT_SWITCH, STMT_WITH, STMT_CATCH,
  STMT_TRY, STMT_FINALLY, STMT_SUBROUTINE, STMT_DO_LOOP,
  STMT_FOR_LOOP, STMT_FOR_IN_LOOP, STMT_WHILE_LOOP
}
enum  JSSrcNoteType {
  SRC_NULL = 0, SRC_IF = 1, SRC_INITPROP = 1, SRC_IF_ELSE = 2,
  SRC_WHILE = 3, SRC_FOR = 4, SRC_CONTINUE = 5, SRC_DECL = 6,
  SRC_DESTRUCT = 6, SRC_PCDELTA = 7, SRC_GROUPASSIGN = 7, SRC_ASSIGNOP = 8,
  SRC_COND = 9, SRC_BRACE = 10, SRC_HIDDEN = 11, SRC_PCBASE = 12,
  SRC_METHODBASE = 13, SRC_LABEL = 13, SRC_LABELBRACE = 14, SRC_ENDBRACE = 15,
  SRC_BREAK2LABEL = 16, SRC_CONT2LABEL = 17, SRC_SWITCH = 18, SRC_FUNCDEF = 19,
  SRC_CATCH = 20, SRC_EXTENDED = 21, SRC_NEWLINE = 22, SRC_SETLINE = 23,
  SRC_XDELTA = 24
}

Functions

 JS_FRIEND_API (JSBool) js_InitCodeGenerator(JSContext *cx
 JS_FRIEND_API (void) js_FinishCodeGenerator(JSContext *cx
ptrdiff_t js_Emit1 (JSContext *cx, JSCodeGenerator *cg, JSOp op)
ptrdiff_t js_Emit2 (JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1)
ptrdiff_t js_Emit3 (JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1, jsbytecode op2)
ptrdiff_t js_EmitN (JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra)
JSBool js_SetJumpOffset (JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, ptrdiff_t off)
JSBool js_InStatement (JSTreeContext *tc, JSStmtType type)
JSBool js_IsGlobalReference (JSTreeContext *tc, JSAtom *atom, JSBool *loopyp)
void js_PushStatement (JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type, ptrdiff_t top)
void js_PushBlockScope (JSTreeContext *tc, JSStmtInfo *stmt, JSAtom *blockAtom, ptrdiff_t top)
void js_PopStatement (JSTreeContext *tc)
JSBool js_PopStatementCG (JSContext *cx, JSCodeGenerator *cg)
JSBool js_DefineCompileTimeConstant (JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, JSParseNode *pn)
JSBool js_LookupCompileTimeConstant (JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, jsval *vp)
JSStmtInfojs_LexicalLookup (JSTreeContext *tc, JSAtom *atom, jsint *slotp, JSBool letdecl)
JSBool js_EmitTree (JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
JSBool js_EmitFunctionBytecode (JSContext *cx, JSCodeGenerator *cg, JSParseNode *body)
JSBool js_EmitFunctionBody (JSContext *cx, JSCodeGenerator *cg, JSParseNode *body, JSFunction *fun)
 JS_FRIEND_DATA (JSSrcNoteSpec) js_SrcNoteSpec[]
 JS_FRIEND_API (uintN) js_SrcNoteLength(jssrcnote *sn)
intN js_NewSrcNote (JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type)
intN js_NewSrcNote2 (JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, ptrdiff_t offset)
intN js_NewSrcNote3 (JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, ptrdiff_t offset1, ptrdiff_t offset2)
jssrcnotejs_AddToSrcNoteDelta (JSContext *cx, JSCodeGenerator *cg, jssrcnote *sn, ptrdiff_t delta)
 JS_FRIEND_API (ptrdiff_t) js_GetSrcNoteOffset(jssrcnote *sn
JSBool js_SetSrcNoteOffset (JSContext *cx, JSCodeGenerator *cg, uintN index, uintN which, ptrdiff_t offset)
JSBool js_FinishTakingSrcNotes (JSContext *cx, JSCodeGenerator *cg, jssrcnote *notes)
JSBool js_AllocTryNotes (JSContext *cx, JSCodeGenerator *cg)
JSTryNotejs_NewTryNote (JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start, ptrdiff_t end, ptrdiff_t catchStart)
void js_FinishTakingTryNotes (JSContext *cx, JSCodeGenerator *cg, JSTryNote *notes)

Variables

JSCodeGeneratorcg
JSCodeGenerator JSArenaPoolcodePool
JSCodeGenerator JSArenaPool
JSArenaPool
notePool
JSCodeGenerator JSArenaPool
JSArenaPool const char * 
filename
JSCodeGenerator JSArenaPool
JSArenaPool const char uintN 
lineno
JSCodeGenerator JSArenaPool
JSArenaPool const char uintN
JSPrincipals
principals
uintN which

Class Documentation

struct JSStmtInfo

Definition at line 124 of file jsemit.h.

Collaboration diagram for JSStmtInfo:
Class Members
JSAtom * atom
ptrdiff_t breaks
ptrdiff_t continues
JSStmtInfo * down
JSStmtInfo * downScope
uint16 flags
uint16 type
ptrdiff_t update
struct JSTreeContext

Definition at line 156 of file jsemit.h.

Collaboration diagram for JSTreeContext:
Class Members
JSObject * blockChain
JSParseNode * blockNode
JSAtomList decls
uint16 flags
uint32 globalUses
uint32 loopyGlobalUses
JSParseNode * nodeList
uint16 numGlobalVars
JSStmtInfo * topScopeStmt
JSStmtInfo * topStmt
uint32 tryCount
struct JSSpanDep

Definition at line 205 of file jsemit.h.

Collaboration diagram for JSSpanDep:
Class Members
ptrdiff_t before
ptrdiff_t offset
JSJumpTarget * target
ptrdiff_t top
struct JSJumpTarget

Definition at line 218 of file jsemit.h.

Collaboration diagram for JSJumpTarget:
Class Members
int balance
JSJumpTarget * kids
ptrdiff_t offset
struct JSCodeGenerator

Definition at line 259 of file jsemit.h.

Collaboration diagram for JSCodeGenerator:
Class Members
uintN arrayCompSlot
JSAtomList atomList
void * codeMark
JSArenaPool * codePool
JSAtomList constList
struct JSCodeGenerator current
uintN emitLevel
const char * filename
uintN firstLine
JSJumpTarget * jtFreeList
JSJumpTarget * jumpTargets
struct JSCodeGenerator main
uintN maxStackDepth
void * noteMark
JSArenaPool * notePool
uintN numJumpTargets
uintN numSpanDeps
JSCodeGenerator * parent
JSPrincipals * principals
struct JSCodeGenerator prolog
JSSpanDep * spanDeps
ptrdiff_t spanDepTodo
intN stackDepth
void * tempMark
JSTreeContext treeContext
JSTryNote * tryBase
JSTryNote * tryNext
size_t tryNoteSpace
struct JSSrcNoteSpec

Definition at line 623 of file jsemit.h.

Class Members
uint8 arity
int8 isSpanDep
const char * name
uint8 offsetBias
struct JSCodeGenerator.prolog

Definition at line 268 of file jsemit.h.

Class Members
jsbytecode * base
uintN currentLine
ptrdiff_t lastNoteOffset
jsbytecode * limit
jsbytecode * next
uintN noteCount
uintN noteMask
jssrcnote * notes
struct JSCodeGenerator.main

Definition at line 268 of file jsemit.h.

Class Members
jsbytecode * base
uintN currentLine
ptrdiff_t lastNoteOffset
jsbytecode * limit
jsbytecode * next
uintN noteCount
uintN noteMask
jssrcnote * notes
struct JSCodeGenerator.current

Definition at line 268 of file jsemit.h.

Class Members
jsbytecode * base
uintN currentLine
ptrdiff_t lastNoteOffset
jsbytecode * limit
jsbytecode * next
uintN noteCount
uintN noteMask
jssrcnote * notes

Define Documentation

#define AT_TOP_LEVEL (   tc)    (!(tc)->topStmt || ((tc)->topStmt->flags & SIF_BODY_BLOCK))

Definition at line 150 of file jsemit.h.

Definition at line 242 of file jsemit.h.

#define BITS_PER_PTRDIFF   (sizeof(ptrdiff_t) * JS_BITS_PER_BYTE)

Definition at line 241 of file jsemit.h.

Definition at line 243 of file jsemit.h.

Definition at line 244 of file jsemit.h.

#define CATCHNOTE (   stmt)    ((stmt).update)

Definition at line 146 of file jsemit.h.

#define CG_BASE (   cg)    ((cg)->current->base)

Definition at line 306 of file jsemit.h.

#define CG_CODE (   cg,
  offset 
)    (CG_BASE(cg) + (offset))

Definition at line 309 of file jsemit.h.

#define CG_COUNT_FINAL_SRCNOTES (   cg,
  cnt 
)
Value:
JS_BEGIN_MACRO                                                            \
        ptrdiff_t diff_ = CG_PROLOG_OFFSET(cg) - (cg)->prolog.lastNoteOffset; \
        cnt = (cg)->prolog.noteCount + (cg)->main.noteCount + 1;              \
        if ((cg)->prolog.noteCount &&                                         \
            (cg)->prolog.currentLine != (cg)->firstLine) {                    \
            if (diff_ > SN_DELTA_MASK)                                        \
                cnt += JS_HOWMANY(diff_ - SN_DELTA_MASK, SN_XDELTA_MASK);     \
            cnt += 2 + (((cg)->firstLine > SN_3BYTE_OFFSET_MASK) << 1);       \
        } else if (diff_ > 0) {                                               \
            if (cg->main.noteCount) {                                         \
                jssrcnote *sn_ = (cg)->main.notes;                            \
                diff_ -= SN_IS_XDELTA(sn_)                                    \
                         ? SN_XDELTA_MASK - (*sn_ & SN_XDELTA_MASK)           \
                         : SN_DELTA_MASK - (*sn_ & SN_DELTA_MASK);            \
            }                                                                 \
            if (diff_ > 0)                                                    \
                cnt += JS_HOWMANY(diff_, SN_XDELTA_MASK);                     \
        }                                                                     \
    JS_END_MACRO

Definition at line 686 of file jsemit.h.

#define CG_COUNT_FINAL_TRYNOTES (   cg,
  cnt 
)
Value:
JS_BEGIN_MACRO                                                            \
        cnt = ((cg)->tryNext > (cg)->tryBase)                                 \
              ? PTRDIFF(cg->tryNext, cg->tryBase, JSTryNote) + 1              \
              : 0;                                                            \
    JS_END_MACRO

Definition at line 731 of file jsemit.h.

#define CG_CURRENT_LINE (   cg)    ((cg)->current->currentLine)

Definition at line 316 of file jsemit.h.

#define CG_LAST_NOTE_OFFSET (   cg)    ((cg)->current->lastNoteOffset)

Definition at line 315 of file jsemit.h.

#define CG_LIMIT (   cg)    ((cg)->current->limit)

Definition at line 307 of file jsemit.h.

#define CG_NEXT (   cg)    ((cg)->current->next)

Definition at line 308 of file jsemit.h.

#define CG_NOTE_COUNT (   cg)    ((cg)->current->noteCount)

Definition at line 313 of file jsemit.h.

#define CG_NOTE_MASK (   cg)    ((cg)->current->noteMask)

Definition at line 314 of file jsemit.h.

#define CG_NOTES (   cg)    ((cg)->current->notes)

Definition at line 312 of file jsemit.h.

Definition at line 310 of file jsemit.h.

#define CG_PROLOG_BASE (   cg)    ((cg)->prolog.base)

Definition at line 318 of file jsemit.h.

#define CG_PROLOG_CODE (   cg,
  poff 
)    (CG_PROLOG_BASE(cg) + (poff))

Definition at line 321 of file jsemit.h.

#define CG_PROLOG_LIMIT (   cg)    ((cg)->prolog.limit)

Definition at line 319 of file jsemit.h.

#define CG_PROLOG_NEXT (   cg)    ((cg)->prolog.next)

Definition at line 320 of file jsemit.h.

Value:

Definition at line 322 of file jsemit.h.

#define CG_SWITCH_TO_MAIN (   cg)    ((cg)->current = &(cg)->main)

Definition at line 325 of file jsemit.h.

#define CG_SWITCH_TO_PROLOG (   cg)    ((cg)->current = &(cg)->prolog)

Definition at line 326 of file jsemit.h.

#define CHECK_AND_SET_JUMP_OFFSET (   cx,
  cg,
  pc,
  off 
)
Value:
JS_BEGIN_MACRO                                                            \
        if (!js_SetJumpOffset(cx, cg, pc, off))                               \
            return JS_FALSE;                                                  \
    JS_END_MACRO

Definition at line 378 of file jsemit.h.

#define CHECK_AND_SET_JUMP_OFFSET_AT (   cx,
  cg,
  off 
)    CHECK_AND_SET_JUMP_OFFSET(cx, cg, CG_CODE(cg,off), CG_OFFSET(cg) - (off))

Definition at line 384 of file jsemit.h.

#define GOSUBS (   stmt)    ((stmt).breaks)

Definition at line 147 of file jsemit.h.

#define GUARDJUMP (   stmt)    ((stmt).continues)

Definition at line 148 of file jsemit.h.

Definition at line 396 of file jsemit.h.

#define JT_CLR_TAG (   jt)    ((JSJumpTarget *)((jsword)(jt) & ~JT_TAG_BIT))

Definition at line 238 of file jsemit.h.

#define JT_DIR (   imbalance)    (((imbalance) + 1) >> 1)

Definition at line 228 of file jsemit.h.

#define JT_HAS_TAG (   jt)    ((jsword)(jt) & JT_TAG_BIT)

Definition at line 239 of file jsemit.h.

#define JT_IMBALANCE (   dir)    (((dir) << 1) - 1)

Definition at line 227 of file jsemit.h.

#define JT_LEFT   0

Definition at line 224 of file jsemit.h.

#define JT_OTHER_DIR (   dir)    (1 - (dir))

Definition at line 226 of file jsemit.h.

#define JT_RIGHT   1

Definition at line 225 of file jsemit.h.

#define JT_SET_TAG (   jt)    ((JSJumpTarget *)((jsword)(jt) | JT_TAG_BIT))

Definition at line 237 of file jsemit.h.

#define JT_TAG_BIT   ((jsword) 1)

Definition at line 235 of file jsemit.h.

#define JT_TO_BPDELTA (   jt)    ((ptrdiff_t)((jsword)(jt) >> JT_UNTAG_SHIFT))

Definition at line 245 of file jsemit.h.

Definition at line 236 of file jsemit.h.

Value:
(JS_ASSERT(!JT_HAS_TAG((sd)->target)),        \
                                 JT_TO_BPDELTA((sd)->target))

Definition at line 251 of file jsemit.h.

#define SD_GET_TARGET (   sd)
Value:
(JS_ASSERT(JT_HAS_TAG((sd)->target)),         \
                                 JT_CLR_TAG((sd)->target))

Definition at line 248 of file jsemit.h.

#define SD_SET_BPDELTA (   sd,
  bp 
)    ((sd)->target = BPDELTA_TO_JT(bp))

Definition at line 250 of file jsemit.h.

#define SD_SET_TARGET (   sd,
  jt 
)    ((sd)->target = JT_SET_TAG(jt))

Definition at line 247 of file jsemit.h.

#define SD_SPAN (   sd,
  pivot 
)
Value:
(SD_GET_TARGET(sd)                            \
                                 ? JT_CLR_TAG((sd)->target)->offset - (pivot) \
                                 : 0)

Definition at line 255 of file jsemit.h.

#define SET_STATEMENT_TOP (   stmt,
  top 
)    ((stmt)->update = (top), (stmt)->breaks = (stmt)->continues = (-1))

Definition at line 153 of file jsemit.h.

#define SIF_BODY_BLOCK   0x0002 /* STMT_BLOCK type is a function body */

Definition at line 136 of file jsemit.h.

#define SIF_SCOPE   0x0001 /* statement has its own lexical scope */

Definition at line 135 of file jsemit.h.

Definition at line 620 of file jsemit.h.

Definition at line 621 of file jsemit.h.

#define SN_DELTA (   sn)
Value:

Definition at line 605 of file jsemit.h.

Definition at line 586 of file jsemit.h.

Definition at line 612 of file jsemit.h.

Definition at line 589 of file jsemit.h.

Definition at line 603 of file jsemit.h.

#define SN_IS_TERMINATOR (   sn)    (*(sn) == SRC_NULL)

Definition at line 640 of file jsemit.h.

#define SN_IS_XDELTA (   sn)    ((*(sn) >> SN_DELTA_BITS) >= SRC_XDELTA)

Definition at line 599 of file jsemit.h.

Value:
((js_SrcNoteSpec[SN_TYPE(sn)].arity == 0) ? 1 \
                                 : js_SrcNoteLength(sn))

Definition at line 634 of file jsemit.h.

#define SN_MAKE_NOTE (   sn,
  t,
  d 
)
Value:
(*(sn) = (jssrcnote)                          \
                                          (((t) << SN_DELTA_BITS)             \
                                           | ((d) & SN_DELTA_MASK)))

Definition at line 592 of file jsemit.h.

#define SN_MAKE_TERMINATOR (   sn)    (*(sn) = SRC_NULL)

Definition at line 639 of file jsemit.h.

#define SN_MAKE_XDELTA (   sn,
  d 
)
Value:
(*(sn) = (jssrcnote)                          \
                                          ((SRC_XDELTA << SN_DELTA_BITS)      \
                                           | ((d) & SN_XDELTA_MASK)))

Definition at line 595 of file jsemit.h.

#define SN_NEXT (   sn)    ((sn) + SN_LENGTH(sn))

Definition at line 636 of file jsemit.h.

#define SN_SET_DELTA (   sn,
  delta 
)
Value:

Definition at line 608 of file jsemit.h.

#define SN_SET_TYPE (   sn,
  type 
)    SN_MAKE_NOTE(sn, type, SN_DELTA(sn))

Definition at line 602 of file jsemit.h.

#define SN_TYPE (   sn)
Value:

Definition at line 600 of file jsemit.h.

Definition at line 585 of file jsemit.h.

Definition at line 588 of file jsemit.h.

Definition at line 587 of file jsemit.h.

Definition at line 613 of file jsemit.h.

Definition at line 590 of file jsemit.h.

Definition at line 581 of file jsemit.h.

Definition at line 582 of file jsemit.h.

Definition at line 583 of file jsemit.h.

Definition at line 580 of file jsemit.h.

#define STMT_IS_LOOP (   stmt)    STMT_TYPE_IS_LOOP((stmt)->type)

Definition at line 120 of file jsemit.h.

#define STMT_IS_TRYING (   stmt)    STMT_TYPE_IS_TRYING((stmt)->type)

Definition at line 119 of file jsemit.h.

#define STMT_LINKS_SCOPE (   stmt)
Value:
(STMT_TYPE_LINKS_SCOPE((stmt)->type) ||       \
                                 ((stmt)->flags & SIF_SCOPE))

Definition at line 117 of file jsemit.h.

#define STMT_MAYBE_SCOPE (   stmt)    STMT_TYPE_MAYBE_SCOPE((stmt)->type)

Definition at line 116 of file jsemit.h.

#define STMT_TYPE_IN_RANGE (   t,
  b,
  e 
)    ((uint)((t) - (b)) <= (uintN)((e) - (b)))

Definition at line 83 of file jsemit.h.

Definition at line 114 of file jsemit.h.

Definition at line 111 of file jsemit.h.

Definition at line 108 of file jsemit.h.

Value:
(type != STMT_WITH &&                                                     \
     STMT_TYPE_IN_RANGE(type, STMT_BLOCK, STMT_SUBROUTINE))

Definition at line 104 of file jsemit.h.

#define TCF_COMPILING   0x01 /* generating bytecode; this tc is a cg */

Definition at line 173 of file jsemit.h.

#define TCF_FUN_CLOSURE_VS_VAR   0x20 /* function and var with same name */

Definition at line 179 of file jsemit.h.

#define TCF_FUN_FLAGS   0x1E0 /* flags to propagate from FunctionBody */

Definition at line 183 of file jsemit.h.

#define TCF_FUN_HEAVYWEIGHT   0x80 /* function needs Call object per call */

Definition at line 181 of file jsemit.h.

#define TCF_FUN_IS_GENERATOR   0x100 /* parsed yield statement in function */

Definition at line 182 of file jsemit.h.

#define TCF_FUN_USES_NONLOCALS   0x40 /* function refers to non-local names */

Definition at line 180 of file jsemit.h.

#define TCF_HAS_DEFXMLNS   0x200 /* default xml namespace = ...; parsed */

Definition at line 184 of file jsemit.h.

#define TCF_HAS_FUNCTION_STMT   0x400 /* block contains a function statement */

Definition at line 185 of file jsemit.h.

#define TCF_IN_FOR_INIT   0x10 /* parsing init expr of for; exclude 'in' */

Definition at line 178 of file jsemit.h.

#define TCF_IN_FUNCTION   0x02 /* parsing inside function body */

Definition at line 174 of file jsemit.h.

#define TCF_RETURN_EXPR   0x04 /* function has 'return expr;' */

Definition at line 175 of file jsemit.h.

#define TCF_RETURN_FLAGS   0x0C /* propagate these out of blocks */

Definition at line 177 of file jsemit.h.

#define TCF_RETURN_VOID   0x08 /* function has 'return;' */

Definition at line 176 of file jsemit.h.

#define TREE_CONTEXT_FINISH (   tc)    ((void)0)

Definition at line 195 of file jsemit.h.

Value:
((tc)->flags = (tc)->numGlobalVars = 0,                                   \
     (tc)->tryCount = (tc)->globalUses = (tc)->loopyGlobalUses = 0,           \
     (tc)->topStmt = (tc)->topScopeStmt = NULL,                               \
     (tc)->blockChain = NULL,                                                 \
     ATOM_LIST_INIT(&(tc)->decls),                                            \
     (tc)->nodeList = NULL, (tc)->blockNode = NULL)

Definition at line 187 of file jsemit.h.


Typedef Documentation

typedef struct JSJumpTarget

Definition at line 203 of file jsemit.h.

typedef struct JSSpanDep

Definition at line 202 of file jsemit.h.

typedef struct JSSrcNoteSpec JSSrcNoteSpec
typedef struct JSStmtInfo

Definition at line 122 of file jsemit.h.


Enumeration Type Documentation

Enumerator:
SRC_NULL 
SRC_IF 
SRC_INITPROP 
SRC_IF_ELSE 
SRC_WHILE 
SRC_FOR 
SRC_CONTINUE 
SRC_DECL 
SRC_DESTRUCT 
SRC_PCDELTA 
SRC_GROUPASSIGN 
SRC_ASSIGNOP 
SRC_COND 
SRC_BRACE 
SRC_HIDDEN 
SRC_PCBASE 
SRC_METHODBASE 
SRC_LABEL 
SRC_LABELBRACE 
SRC_ENDBRACE 
SRC_BREAK2LABEL 
SRC_CONT2LABEL 
SRC_SWITCH 
SRC_FUNCDEF 
SRC_CATCH 
SRC_EXTENDED 
SRC_NEWLINE 
SRC_SETLINE 
SRC_XDELTA 

Definition at line 525 of file jsemit.h.

                           {
    SRC_NULL        = 0,        /* terminates a note vector */
    SRC_IF          = 1,        /* JSOP_IFEQ bytecode is from an if-then */
    SRC_INITPROP    = 1,        /* disjoint meaning applied to JSOP_INITELEM or
                                   to an index label in a regular (structuring)
                                   or a destructuring object initialiser */
    SRC_IF_ELSE     = 2,        /* JSOP_IFEQ bytecode is from an if-then-else */
    SRC_WHILE       = 3,        /* JSOP_IFEQ is from a while loop */
    SRC_FOR         = 4,        /* JSOP_NOP or JSOP_POP in for loop head */
    SRC_CONTINUE    = 5,        /* JSOP_GOTO is a continue, not a break;
                                   also used on JSOP_ENDINIT if extra comma
                                   at end of array literal: [1,2,,] */
    SRC_DECL        = 6,        /* type of a declaration (var, const, let*) */
    SRC_DESTRUCT    = 6,        /* JSOP_DUP starting a destructuring assignment
                                   operation, with SRC_DECL_* offset operand */
    SRC_PCDELTA     = 7,        /* distance forward from comma-operator to
                                   next POP, or from CONDSWITCH to first CASE
                                   opcode, etc. -- always a forward delta */
    SRC_GROUPASSIGN = 7,        /* SRC_DESTRUCT variant for [a, b] = [c, d] */
    SRC_ASSIGNOP    = 8,        /* += or another assign-op follows */
    SRC_COND        = 9,        /* JSOP_IFEQ is from conditional ?: operator */
    SRC_BRACE       = 10,       /* mandatory brace, for scope or to avoid
                                   dangling else */
    SRC_HIDDEN      = 11,       /* opcode shouldn't be decompiled */
    SRC_PCBASE      = 12,       /* distance back from annotated getprop or
                                   setprop op to left-most obj.prop.subprop
                                   bytecode -- always a backward delta */
    SRC_METHODBASE  = 13,       /* SRC_PCBASE variant for obj.function::foo
                                   gets and sets; disjoint from SRC_LABEL by
                                   bytecode to which it applies */
    SRC_LABEL       = 13,       /* JSOP_NOP for label: with atomid immediate */
    SRC_LABELBRACE  = 14,       /* JSOP_NOP for label: {...} begin brace */
    SRC_ENDBRACE    = 15,       /* JSOP_NOP for label: {...} end brace */
    SRC_BREAK2LABEL = 16,       /* JSOP_GOTO for 'break label' with atomid */
    SRC_CONT2LABEL  = 17,       /* JSOP_GOTO for 'continue label' with atomid */
    SRC_SWITCH      = 18,       /* JSOP_*SWITCH with offset to end of switch,
                                   2nd off to first JSOP_CASE if condswitch */
    SRC_FUNCDEF     = 19,       /* JSOP_NOP for function f() with atomid */
    SRC_CATCH       = 20,       /* catch block has guard */
    SRC_EXTENDED    = 21,       /* extended source note, 32-159, in next byte */
    SRC_NEWLINE     = 22,       /* bytecode follows a source newline */
    SRC_SETLINE     = 23,       /* a file-absolute source line number note */
    SRC_XDELTA      = 24        /* 24-31 are for extended delta notes */
} JSSrcNoteType;
enum JSStmtType
Enumerator:
STMT_LABEL 
STMT_IF 
STMT_ELSE 
STMT_BODY 
STMT_BLOCK 
STMT_SWITCH 
STMT_WITH 
STMT_CATCH 
STMT_TRY 
STMT_FINALLY 
STMT_SUBROUTINE 
STMT_DO_LOOP 
STMT_FOR_LOOP 
STMT_FOR_IN_LOOP 
STMT_WHILE_LOOP 

Definition at line 64 of file jsemit.h.

                        {
    STMT_LABEL,                 /* labeled statement:  L: s */
    STMT_IF,                    /* if (then) statement */
    STMT_ELSE,                  /* else clause of if statement */
    STMT_BODY,                  /* synthetic body of function with
                                   destructuring formal parameters */
    STMT_BLOCK,                 /* compound statement: { s1[;... sN] } */
    STMT_SWITCH,                /* switch statement */
    STMT_WITH,                  /* with statement */
    STMT_CATCH,                 /* catch block */
    STMT_TRY,                   /* try block */
    STMT_FINALLY,               /* finally block */
    STMT_SUBROUTINE,            /* gosub-target subroutine body */
    STMT_DO_LOOP,               /* do/while loop statement */
    STMT_FOR_LOOP,              /* for loop statement */
    STMT_FOR_IN_LOOP,           /* for/in loop statement */
    STMT_WHILE_LOOP             /* while loop statement */
} JSStmtType;

Function Documentation

Definition at line 6569 of file jsemit.c.

                       {
        JS_ReportOutOfMemory(cx);
        return JS_FALSE;
    }
    CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1;
    return JS_TRUE;
}

jssrcnote *
js_AddToSrcNoteDelta(JSContext *cx, JSCodeGenerator *cg, jssrcnote *sn,
                     ptrdiff_t delta)
{
    ptrdiff_t base, limit, newdelta, diff;
    intN index;

    /*
     * Called only from OptimizeSpanDeps and js_FinishTakingSrcNotes to add to
     * main script note deltas, and only by a small positive amount.
     */
    JS_ASSERT(cg->current == &cg->main);
    JS_ASSERT((unsigned) delta < (unsigned) SN_XDELTA_LIMIT);

    base = SN_DELTA(sn);
    limit = SN_IS_XDELTA(sn) ? SN_XDELTA_LIMIT : SN_DELTA_LIMIT;
    newdelta = base + delta;
    if (newdelta < limit) {
        SN_SET_DELTA(sn, newdelta);
    } else {
        index = sn - cg->main.notes;
        if ((cg->main.noteCount & cg->main.noteMask) == 0) {
            if (!GrowSrcNotes(cx, cg))
                return NULL;

Here is the caller graph for this function:

Definition at line 6787 of file jsemit.c.

  { int bin = JS_CeilingLog2(totalCount);
    if (bin >= NBINS)
        bin = NBINS - 1;
    ++hist[bin];
  }
#endif
    return JS_TRUE;
}

JSBool
js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg)
{
    size_t size, incr;
    ptrdiff_t delta;

    size = TRYNOTE_SIZE(cg->treeContext.tryCount);
    if (size <= cg->tryNoteSpace)
        return JS_TRUE;

    /*
     * Allocate trynotes from cx->tempPool.
     * XXX Too much growing and we bloat, as other tempPool allocators block
     * in-place growth, and we never recycle old free space in an arena.
     * YYY But once we consume an entire arena, we'll realloc it, letting the
     * malloc heap recycle old space, while still freeing _en masse_ via the
     * arena pool.
     */
    if (!cg->tryBase) {
        size = JS_ROUNDUP(size, TRYNOTE_SIZE(TRYNOTE_CHUNK));
        JS_ARENA_ALLOCATE_CAST(cg->tryBase, JSTryNote *, &cx->tempPool, size);
        if (!cg->tryBase)
            return JS_FALSE;
        cg->tryNoteSpace = size;
        cg->tryNext = cg->tryBase;
    } else {
        delta = PTRDIFF((char *)cg->tryNext, (char *)cg->tryBase, char);
        incr = size - cg->tryNoteSpace;

Here is the caller graph for this function:

Definition at line 1488 of file jsemit.c.

{
    jsdouble dval;
    jsint ival;
    JSAtom *valueAtom;
    JSAtomListElement *ale;

    /* XXX just do numbers for now */
    if (pn->pn_type == TOK_NUMBER) {
        dval = pn->pn_dval;
        valueAtom = (JSDOUBLE_IS_INT(dval, ival) && INT_FITS_IN_JSVAL(ival))
                    ? js_AtomizeInt(cx, ival, 0)
                    : js_AtomizeDouble(cx, dval, 0);
        if (!valueAtom)
            return JS_FALSE;
        ale = js_IndexAtom(cx, atom, &cg->constList);
        if (!ale)
            return JS_FALSE;
        ALE_SET_VALUE(ale, ATOM_KEY(valueAtom));
    }
    return JS_TRUE;
}

Here is the call graph for this function:

ptrdiff_t js_Emit1 ( JSContext cx,
JSCodeGenerator cg,
JSOp  op 
)

Definition at line 175 of file jsemit.c.

{
    ptrdiff_t offset = EmitCheck(cx, cg, op, 1);

    if (offset >= 0) {
        *CG_NEXT(cg)++ = (jsbytecode)op;
        UpdateDepth(cx, cg, offset);
    }
    return offset;
}

Here is the call graph for this function:

Here is the caller graph for this function:

ptrdiff_t js_Emit2 ( JSContext cx,
JSCodeGenerator cg,
JSOp  op,
jsbytecode  op1 
)

Definition at line 187 of file jsemit.c.

{
    ptrdiff_t offset = EmitCheck(cx, cg, op, 2);

    if (offset >= 0) {
        jsbytecode *next = CG_NEXT(cg);
        next[0] = (jsbytecode)op;
        next[1] = op1;
        CG_NEXT(cg) = next + 2;
        UpdateDepth(cx, cg, offset);
    }
    return offset;
}

Here is the call graph for this function:

ptrdiff_t js_Emit3 ( JSContext cx,
JSCodeGenerator cg,
JSOp  op,
jsbytecode  op1,
jsbytecode  op2 
)

Definition at line 202 of file jsemit.c.

{
    ptrdiff_t offset = EmitCheck(cx, cg, op, 3);

    if (offset >= 0) {
        jsbytecode *next = CG_NEXT(cg);
        next[0] = (jsbytecode)op;
        next[1] = op1;
        next[2] = op2;
        CG_NEXT(cg) = next + 3;
        UpdateDepth(cx, cg, offset);
    }
    return offset;
}

Here is the call graph for this function:

Here is the caller graph for this function:

JSBool js_EmitFunctionBody ( JSContext cx,
JSCodeGenerator cg,
JSParseNode body,
JSFunction fun 
)

Definition at line 3140 of file jsemit.c.

{
    JSStackFrame *fp, frame;
    JSObject *funobj;
    JSBool ok;

    fp = cx->fp;
    funobj = fun->object;
    JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj &&
                      fp->scopeChain != funobj));
    memset(&frame, 0, sizeof frame);
    frame.fun = fun;
    frame.varobj = frame.scopeChain = funobj;
    frame.down = fp;
    frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx)
                  ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
                  : JSFRAME_COMPILING;
    cx->fp = &frame;
    ok = js_EmitFunctionBytecode(cx, cg, body);
    cx->fp = fp;
    if (!ok)
        return JS_FALSE;

    if (!js_NewScriptFromCG(cx, cg, fun))
        return JS_FALSE;

    JS_ASSERT(FUN_INTERPRETED(fun));
    return JS_TRUE;
}

Here is the call graph for this function:

Definition at line 3121 of file jsemit.c.

{
    if (!js_AllocTryNotes(cx, cg))
        return JS_FALSE;

    if (cg->treeContext.flags & TCF_FUN_IS_GENERATOR) {
        /* JSOP_GENERATOR must be the first instruction. */
        CG_SWITCH_TO_PROLOG(cg);
        JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg));
        if (js_Emit1(cx, cg, JSOP_GENERATOR) < 0)
            return JS_FALSE;
        CG_SWITCH_TO_MAIN(cg);
    }

    return js_EmitTree(cx, cg, body) &&
           js_Emit1(cx, cg, JSOP_STOP) >= 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

ptrdiff_t js_EmitN ( JSContext cx,
JSCodeGenerator cg,
JSOp  op,
size_t  extra 
)

Definition at line 219 of file jsemit.c.

{
    ptrdiff_t length = 1 + (ptrdiff_t)extra;
    ptrdiff_t offset = EmitCheck(cx, cg, op, length);

    if (offset >= 0) {
        jsbytecode *next = CG_NEXT(cg);
        *next = (jsbytecode)op;
        memset(next + 1, 0, BYTECODE_SIZE(extra));
        CG_NEXT(cg) = next + length;
        UpdateDepth(cx, cg, offset);
    }
    return offset;
}

Here is the call graph for this function:

Here is the caller graph for this function:

JSBool js_EmitTree ( JSContext cx,
JSCodeGenerator cg,
JSParseNode pn 
)

Definition at line 3896 of file jsemit.c.

                           {
        if (offset == target && SN_IS_GETTABLE(sn))
            return JS_TRUE;
        offset += SN_DELTA(sn);
    }
    return JS_FALSE;
}
#endif

JSBool
js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
{
    JSBool ok, useful, wantval;
    JSStmtInfo *stmt, stmtInfo;
    ptrdiff_t top, off, tmp, beq, jmp;
    JSParseNode *pn2, *pn3;
    JSAtom *atom;
    JSAtomListElement *ale;
    jsatomid atomIndex;
    ptrdiff_t noteIndex;
    JSSrcNoteType noteType;
    jsbytecode *pc;
    JSOp op;
    JSTokenType type;
    uint32 argc;
    int stackDummy;

    if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
        return JS_FALSE;
    }

    ok = JS_TRUE;
    cg->emitLevel++;
    pn->pn_offset = top = CG_OFFSET(cg);

    /* Emit notes to tell the current bytecode's source line number. */
    UPDATE_LINE_NUMBER_NOTES(cx, cg, pn);

    switch (pn->pn_type) {
      case TOK_FUNCTION:
      {
        void *cg2mark;
        JSCodeGenerator *cg2;
        JSFunction *fun;

#if JS_HAS_XML_SUPPORT
        if (pn->pn_arity == PN_NULLARY) {
            if (js_Emit1(cx, cg, JSOP_GETFUNNS) < 0)
                return JS_FALSE;
            break;
        }
#endif

        /* Generate code for the function's body. */
        cg2mark = JS_ARENA_MARK(cg->codePool);
        JS_ARENA_ALLOCATE_TYPE(cg2, JSCodeGenerator, cg->codePool);
        if (!cg2) {
            JS_ReportOutOfMemory(cx);
            return JS_FALSE;
        }
        if (!js_InitCodeGenerator(cx, cg2, cg->codePool, cg->notePool,
                                  cg->filename, pn->pn_pos.begin.lineno,
                                  cg->principals)) {
            return JS_FALSE;
        }
        cg2->treeContext.flags = (uint16) (pn->pn_flags | TCF_IN_FUNCTION);
        cg2->treeContext.tryCount = pn->pn_tryCount;
        cg2->parent = cg;
        fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(pn->pn_funAtom));
        if (!js_EmitFunctionBody(cx, cg2, pn->pn_body, fun))
            return JS_FALSE;

        /*
         * We need an activation object if an inner peeks out, or if such
         * inner-peeking caused one of our inners to become heavyweight.
         */
        if (cg2->treeContext.flags &
            (TCF_FUN_USES_NONLOCALS | TCF_FUN_HEAVYWEIGHT)) {
            cg->treeContext.flags |= TCF_FUN_HEAVYWEIGHT;
        }
        js_FinishCodeGenerator(cx, cg2);
        JS_ARENA_RELEASE(cg->codePool, cg2mark);

        /* Make the function object a literal in the outer script's pool. */
        ale = js_IndexAtom(cx, pn->pn_funAtom, &cg->atomList);
        if (!ale)
            return JS_FALSE;
        atomIndex = ALE_INDEX(ale);

        /* Emit a bytecode pointing to the closure object in its immediate. */
        if (pn->pn_op != JSOP_NOP) {
            EMIT_ATOM_INDEX_OP(pn->pn_op, atomIndex);
            break;
        }

        /* Top-level named functions need a nop for decompilation. */
        noteIndex = js_NewSrcNote2(cx, cg, SRC_FUNCDEF, (ptrdiff_t)atomIndex);
        if (noteIndex < 0 ||
            js_Emit1(cx, cg, JSOP_NOP) < 0) {
            return JS_FALSE;
        }

        /*
         * Top-levels also need a prolog op to predefine their names in the
         * variable object, or if local, to fill their stack slots.
         */
        CG_SWITCH_TO_PROLOG(cg);

        if (cg->treeContext.flags & TCF_IN_FUNCTION) {
            JSObject *obj, *pobj;
            JSProperty *prop;
            JSScopeProperty *sprop;
            uintN slot;

            obj = OBJ_GET_PARENT(cx, fun->object);
            if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(fun->atom),
                                         &pobj, &prop)) {
                return JS_FALSE;
            }

            JS_ASSERT(prop && pobj == obj);
            sprop = (JSScopeProperty *) prop;
            JS_ASSERT(sprop->getter == js_GetLocalVariable);
            slot = sprop->shortid;
            OBJ_DROP_PROPERTY(cx, pobj, prop);

            /*
             * If this local function is declared in a body block induced by
             * let declarations, reparent fun->object to the compiler-created
             * body block object so that JSOP_DEFLOCALFUN can clone that block
             * into the runtime scope chain.
             */
            stmt = cg->treeContext.topStmt;
            if (stmt && stmt->type == STMT_BLOCK &&
                stmt->down && stmt->down->type == STMT_BLOCK &&
                (stmt->down->flags & SIF_SCOPE)) {
                obj = ATOM_TO_OBJECT(stmt->down->atom);
                JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass);
                OBJ_SET_PARENT(cx, fun->object, obj);
            }

            if (atomIndex >= JS_BIT(16)) {
                /*
                 * Lots of literals in the outer function, so we have to emit
                 * [JSOP_LITOPX, atomIndex, JSOP_DEFLOCALFUN, var slot].
                 */
                off = js_EmitN(cx, cg, JSOP_LITOPX, 3);
                if (off < 0)
                    return JS_FALSE;
                pc = CG_CODE(cg, off);
                SET_LITERAL_INDEX(pc, atomIndex);
                EMIT_UINT16_IMM_OP(JSOP_DEFLOCALFUN, slot);
            } else {
                /* Emit [JSOP_DEFLOCALFUN, var slot, atomIndex]. */
                off = js_EmitN(cx, cg, JSOP_DEFLOCALFUN,
                               VARNO_LEN + ATOM_INDEX_LEN);
                if (off < 0)
                    return JS_FALSE;
                pc = CG_CODE(cg, off);
                SET_VARNO(pc, slot);
                pc += VARNO_LEN;
                SET_ATOM_INDEX(pc, atomIndex);
            }
        } else {
            JS_ASSERT(!cg->treeContext.topStmt);
            EMIT_ATOM_INDEX_OP(JSOP_DEFFUN, atomIndex);
        }

        CG_SWITCH_TO_MAIN(cg);
        break;
      }

#if JS_HAS_EXPORT_IMPORT
      case TOK_EXPORT:
        pn2 = pn->pn_head;
        if (pn2->pn_type == TOK_STAR) {
            /*
             * 'export *' must have no other elements in the list (what would
             * be the point?).
             */
            if (js_Emit1(cx, cg, JSOP_EXPORTALL) < 0)
                return JS_FALSE;
        } else {
            /*
             * If not 'export *', the list consists of NAME nodes identifying
             * properties of the variables object to flag as exported.
             */
            do {
                ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);
                if (!ale)
                    return JS_FALSE;
                EMIT_ATOM_INDEX_OP(JSOP_EXPORTNAME, ALE_INDEX(ale));
            } while ((pn2 = pn2->pn_next) != NULL);
        }
        break;

      case TOK_IMPORT:
        for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
            /*
             * Each subtree on an import list is rooted by a DOT or LB node.
             * A DOT may have a null pn_atom member, in which case pn_op must
             * be JSOP_IMPORTALL -- see EmitPropOp above.
             */
            if (!js_EmitTree(cx, cg, pn2))
                return JS_FALSE;
        }
        break;
#endif /* JS_HAS_EXPORT_IMPORT */

      case TOK_IF:
        /* Initialize so we can detect else-if chains and avoid recursion. */
        stmtInfo.type = STMT_IF;
        beq = jmp = -1;
        noteIndex = -1;

      if_again:
        /* Emit code for the condition before pushing stmtInfo. */
        if (!js_EmitTree(cx, cg, pn->pn_kid1))
            return JS_FALSE;
        top = CG_OFFSET(cg);
        if (stmtInfo.type == STMT_IF) {
            js_PushStatement(&cg->treeContext, &stmtInfo, STMT_IF, top);
        } else {
            /*
             * We came here from the goto further below that detects else-if
             * chains, so we must mutate stmtInfo back into a STMT_IF record.
             * Also (see below for why) we need a note offset for SRC_IF_ELSE
             * to help the decompiler.  Actually, we need two offsets, one for
             * decompiling any else clause and the second for decompiling an
             * else-if chain without bracing, overindenting, or incorrectly
             * scoping let declarations.
             */
            JS_ASSERT(stmtInfo.type == STMT_ELSE);
            stmtInfo.type = STMT_IF;
            stmtInfo.update = top;
            if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq))
                return JS_FALSE;
            if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 1, top - jmp))
                return JS_FALSE;
        }

        /* Emit an annotated branch-if-false around the then part. */
        pn3 = pn->pn_kid3;
        noteIndex = js_NewSrcNote(cx, cg, pn3 ? SRC_IF_ELSE : SRC_IF);
        if (noteIndex < 0)
            return JS_FALSE;
        beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
        if (beq < 0)
            return JS_FALSE;

        /* Emit code for the then and optional else parts. */
        if (!js_EmitTree(cx, cg, pn->pn_kid2))
            return JS_FALSE;
        if (pn3) {
            /* Modify stmtInfo so we know we're in the else part. */
            stmtInfo.type = STMT_ELSE;

            /*
             * Emit a JSOP_BACKPATCH op to jump from the end of our then part
             * around the else part.  The js_PopStatementCG call at the bottom
             * of this switch case will fix up the backpatch chain linked from
             * stmtInfo.breaks.
             */
            jmp = EmitGoto(cx, cg, &stmtInfo, &stmtInfo.breaks, NULL, SRC_NULL);
            if (jmp < 0)
                return JS_FALSE;

            /* Ensure the branch-if-false comes here, then emit the else. */
            CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
            if (pn3->pn_type == TOK_IF) {
                pn = pn3;
                goto if_again;
            }

            if (!js_EmitTree(cx, cg, pn3))
                return JS_FALSE;

            /*
             * Annotate SRC_IF_ELSE with the offset from branch to jump, for
             * the decompiler's benefit.  We can't just "back up" from the pc
             * of the else clause, because we don't know whether an extended
             * jump was required to leap from the end of the then clause over
             * the else clause.
             */
            if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq))
                return JS_FALSE;
        } else {
            /* No else part, fixup the branch-if-false to come here. */
            CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
        }
        ok = js_PopStatementCG(cx, cg);
        break;

      case TOK_SWITCH:
        /* Out of line to avoid bloating js_EmitTree's stack frame size. */
        ok = EmitSwitch(cx, cg, pn, &stmtInfo);
        break;

      case TOK_WHILE:
        js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WHILE_LOOP, top);
        if (!js_EmitTree(cx, cg, pn->pn_left))
            return JS_FALSE;
        noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE);
        if (noteIndex < 0)
            return JS_FALSE;
        beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
        if (beq < 0)
            return JS_FALSE;
        if (!js_EmitTree(cx, cg, pn->pn_right))
            return JS_FALSE;
        jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg));
        if (jmp < 0)
            return JS_FALSE;
        CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
        if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq))
            return JS_FALSE;
        ok = js_PopStatementCG(cx, cg);
        break;

      case TOK_DO:
        /* Emit an annotated nop so we know to decompile a 'do' keyword. */
        if (js_NewSrcNote(cx, cg, SRC_WHILE) < 0 ||
            js_Emit1(cx, cg, JSOP_NOP) < 0) {
            return JS_FALSE;
        }

        /* Compile the loop body. */
        top = CG_OFFSET(cg);
        js_PushStatement(&cg->treeContext, &stmtInfo, STMT_DO_LOOP, top);
        if (!js_EmitTree(cx, cg, pn->pn_left))
            return JS_FALSE;

        /* Set loop and enclosing label update offsets, for continue. */
        stmt = &stmtInfo;
        do {
            stmt->update = CG_OFFSET(cg);
        } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL);

        /* Compile the loop condition, now that continues know where to go. */
        if (!js_EmitTree(cx, cg, pn->pn_right))
            return JS_FALSE;

        /*
         * No source note needed, because JSOP_IFNE is used only for do-while.
         * If we ever use JSOP_IFNE for other purposes, we can still avoid yet
         * another note here, by storing (jmp - top) in the SRC_WHILE note's
         * offset, and fetching that delta in order to decompile recursively.
         */
        if (EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg)) < 0)
            return JS_FALSE;
        ok = js_PopStatementCG(cx, cg);
        break;

      case TOK_FOR:
        beq = 0;                /* suppress gcc warnings */
        pn2 = pn->pn_left;
        js_PushStatement(&cg->treeContext, &stmtInfo, STMT_FOR_LOOP, top);

        if (pn2->pn_type == TOK_IN) {
            JSBool emitIFEQ;

            /* Set stmtInfo type for later testing. */
            stmtInfo.type = STMT_FOR_IN_LOOP;
            noteIndex = -1;

            /*
             * If the left part is 'var x', emit code to define x if necessary
             * using a prolog opcode, but do not emit a pop.  If the left part
             * is 'var x = i', emit prolog code to define x if necessary; then
             * emit code to evaluate i, assign the result to x, and pop the
             * result off the stack.
             *
             * All the logic to do this is implemented in the outer switch's
             * TOK_VAR case, conditioned on pn_extra flags set by the parser.
             *
             * In the 'for (var x = i in o) ...' case, the js_EmitTree(...pn3)
             * called here will generate the proper note for the assignment
             * op that sets x = i, hoisting the initialized var declaration
             * out of the loop: 'var x = i; for (x in o) ...'.
             *
             * In the 'for (var x in o) ...' case, nothing but the prolog op
             * (if needed) should be generated here, we must emit the note
             * just before the JSOP_FOR* opcode in the switch on pn3->pn_type
             * a bit below, so nothing is hoisted: 'for (var x in o) ...'.
             *
             * A 'for (let x = i in o)' loop must not be hoisted, since in
             * this form the let variable is scoped by the loop body (but not
             * the head).  The initializer expression i must be evaluated for
             * any side effects.  So we hoist only i in the let case.
             */
            pn3 = pn2->pn_left;
            type = pn3->pn_type;
            cg->treeContext.flags |= TCF_IN_FOR_INIT;
            if (TOKEN_TYPE_IS_DECL(type) && !js_EmitTree(cx, cg, pn3))
                return JS_FALSE;
            cg->treeContext.flags &= ~TCF_IN_FOR_INIT;

            /* Compile the object expression to the right of 'in'. */
            if (!js_EmitTree(cx, cg, pn2->pn_right))
                return JS_FALSE;

            /*
             * Emit a bytecode to convert top of stack value to the iterator
             * object depending on the loop variant (for-in, for-each-in, or
             * destructuring for-in).
             */
#if JS_HAS_DESTRUCTURING
            JS_ASSERT(pn->pn_op == JSOP_FORIN ||
                      pn->pn_op == JSOP_FOREACHKEYVAL ||
                      pn->pn_op == JSOP_FOREACH);
#else
            JS_ASSERT(pn->pn_op == JSOP_FORIN || pn->pn_op == JSOP_FOREACH);
#endif
            if (js_Emit1(cx, cg, pn->pn_op) < 0)
                return JS_FALSE;

            top = CG_OFFSET(cg);
            SET_STATEMENT_TOP(&stmtInfo, top);

            /*
             * Compile a JSOP_FOR* bytecode based on the left hand side.
             *
             * Initialize op to JSOP_SETNAME in case of |for ([a, b] in o)...|
             * or similar, to signify assignment, rather than declaration, to
             * the decompiler.  EmitDestructuringOps takes a prolog bytecode
             * parameter and emits the appropriate source note, defaulting to
             * assignment, so JSOP_SETNAME is not critical here; many similar
             * ops could be used -- just not JSOP_NOP (which means 'let').
             */
            emitIFEQ = JS_TRUE;
            op = JSOP_SETNAME;
            switch (type) {
#if JS_HAS_BLOCK_SCOPE
              case TOK_LET:
#endif
              case TOK_VAR:
                JS_ASSERT(pn3->pn_arity == PN_LIST && pn3->pn_count == 1);
                pn3 = pn3->pn_head;
#if JS_HAS_DESTRUCTURING
                if (pn3->pn_type == TOK_ASSIGN) {
                    pn3 = pn3->pn_left;
                    JS_ASSERT(pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC);
                }
                if (pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC) {
                    op = pn2->pn_left->pn_op;
                    goto destructuring_for;
                }
#else
                JS_ASSERT(pn3->pn_type == TOK_NAME);
#endif
                /*
                 * Always annotate JSOP_FORLOCAL if given input of the form
                 * 'for (let x in * o)' -- the decompiler must not hoist the
                 * 'let x' out of the loop head, or x will be bound in the
                 * wrong scope.  Likewise, but in this case only for the sake
                 * of higher decompilation fidelity only, do not hoist 'var x'
                 * when given 'for (var x in o)'.  But 'for (var x = i in o)'
                 * requires hoisting in order to preserve the initializer i.
                 * The decompiler can only handle so much!
                 */
                if ((
#if JS_HAS_BLOCK_SCOPE
                     type == TOK_LET ||
#endif
                     !pn3->pn_expr) &&
                    js_NewSrcNote2(cx, cg, SRC_DECL,
                                   type == TOK_VAR
                                   ? SRC_DECL_VAR
                                   : SRC_DECL_LET) < 0) {
                    return JS_FALSE;
                }
                /* FALL THROUGH */
              case TOK_NAME:
                if (pn3->pn_slot >= 0) {
                    op = pn3->pn_op;
                    switch (op) {
                      case JSOP_GETARG:   /* FALL THROUGH */
                      case JSOP_SETARG:   op = JSOP_FORARG; break;
                      case JSOP_GETVAR:   /* FALL THROUGH */
                      case JSOP_SETVAR:   op = JSOP_FORVAR; break;
                      case JSOP_GETGVAR:  /* FALL THROUGH */
                      case JSOP_SETGVAR:  op = JSOP_FORNAME; break;
                      case JSOP_GETLOCAL: /* FALL THROUGH */
                      case JSOP_SETLOCAL: op = JSOP_FORLOCAL; break;
                      default:            JS_ASSERT(0);
                    }
                } else {
                    pn3->pn_op = JSOP_FORNAME;
                    if (!BindNameToSlot(cx, &cg->treeContext, pn3, JS_FALSE))
                        return JS_FALSE;
                    op = pn3->pn_op;
                }
                if (pn3->pn_slot >= 0) {
                    if (pn3->pn_attrs & JSPROP_READONLY) {
                        JS_ASSERT(op == JSOP_FORVAR);
                        op = JSOP_FORCONST;
                    }
                    atomIndex = (jsatomid) pn3->pn_slot;
                    EMIT_UINT16_IMM_OP(op, atomIndex);
                } else {
                    if (!EmitAtomOp(cx, pn3, op, cg))
                        return JS_FALSE;
                }
                break;

              case TOK_DOT:
                useful = JS_FALSE;
                if (!CheckSideEffects(cx, &cg->treeContext, pn3->pn_expr,
                                      &useful)) {
                    return JS_FALSE;
                }
                if (!useful) {
                    if (!EmitPropOp(cx, pn3, JSOP_FORPROP, cg))
                        return JS_FALSE;
                    break;
                }
                /* FALL THROUGH */

#if JS_HAS_DESTRUCTURING
              case TOK_RB:
              case TOK_RC:
              destructuring_for:
#endif
#if JS_HAS_XML_SUPPORT
              case TOK_UNARYOP:
#endif
#if JS_HAS_LVALUE_RETURN
              case TOK_LP:
#endif
              case TOK_LB:
                /*
                 * We separate the first/next bytecode from the enumerator
                 * variable binding to avoid any side-effects in the index
                 * expression (e.g., for (x[i++] in {}) should not bind x[i]
                 * or increment i at all).
                 */
                emitIFEQ = JS_FALSE;
                if (!js_Emit1(cx, cg, JSOP_FORELEM))
                    return JS_FALSE;

                /*
                 * Emit a SRC_WHILE note with offset telling the distance to
                 * the loop-closing jump (we can't reckon from the branch at
                 * the top of the loop, because the loop-closing jump might
                 * need to be an extended jump, independent of whether the
                 * branch is short or long).
                 */
                noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE);
                if (noteIndex < 0)
                    return JS_FALSE;
                beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
                if (beq < 0)
                    return JS_FALSE;

#if JS_HAS_DESTRUCTURING
                if (pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC) {
                    if (!EmitDestructuringOps(cx, cg, op, pn3))
                        return JS_FALSE;
                    if (js_Emit1(cx, cg, JSOP_POP) < 0)
                        return JS_FALSE;
                    break;
                }
#endif
#if JS_HAS_LVALUE_RETURN
                if (pn3->pn_type == TOK_LP) {
                    JS_ASSERT(pn3->pn_op == JSOP_SETCALL);
                    if (!js_EmitTree(cx, cg, pn3))
                        return JS_FALSE;
                    if (!js_Emit1(cx, cg, JSOP_ENUMELEM))
                        return JS_FALSE;
                    break;
                }
#endif
#if JS_HAS_XML_SUPPORT
                if (pn3->pn_type == TOK_UNARYOP) {
                    JS_ASSERT(pn3->pn_op == JSOP_BINDXMLNAME);
                    if (!js_EmitTree(cx, cg, pn3))
                        return JS_FALSE;
                    if (!js_Emit1(cx, cg, JSOP_ENUMELEM))
                        return JS_FALSE;
                    break;
                }
#endif

                /* Now that we're safely past the IFEQ, commit side effects. */
                if (!EmitElemOp(cx, pn3, JSOP_ENUMELEM, cg))
                    return JS_FALSE;
                break;

              default:
                JS_ASSERT(0);
            }

            if (emitIFEQ) {
                /* Annotate so the decompiler can find the loop-closing jump. */
                noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE);
                if (noteIndex < 0)
                    return JS_FALSE;

                /* Pop and test the loop condition generated by JSOP_FOR*. */
                beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
                if (beq < 0)
                    return JS_FALSE;
            }
        } else {
            op = JSOP_POP;
            if (!pn2->pn_kid1) {
                /* No initializer: emit an annotated nop for the decompiler. */
                op = JSOP_NOP;
            } else {
                cg->treeContext.flags |= TCF_IN_FOR_INIT;
#if JS_HAS_DESTRUCTURING
                pn3 = pn2->pn_kid1;
                if (pn3->pn_type == TOK_ASSIGN &&
                    !MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) {
                    return JS_FALSE;
                }
#endif
                if (op == JSOP_POP) {
                    if (!js_EmitTree(cx, cg, pn3))
                        return JS_FALSE;
                    if (TOKEN_TYPE_IS_DECL(pn3->pn_type)) {
                        /*
                         * Check whether a destructuring-initialized var decl
                         * was optimized to a group assignment.  If so, we do
                         * not need to emit a pop below, so switch to a nop,
                         * just for the decompiler.
                         */
                        JS_ASSERT(pn3->pn_arity == PN_LIST);
                        if (pn3->pn_extra & PNX_GROUPINIT)
                            op = JSOP_NOP;
                    }
                }
                cg->treeContext.flags &= ~TCF_IN_FOR_INIT;
            }
            noteIndex = js_NewSrcNote(cx, cg, SRC_FOR);
            if (noteIndex < 0 ||
                js_Emit1(cx, cg, op) < 0) {
                return JS_FALSE;
            }

            top = CG_OFFSET(cg);
            SET_STATEMENT_TOP(&stmtInfo, top);
            if (!pn2->pn_kid2) {
                /* No loop condition: flag this fact in the source notes. */
                if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, 0))
                    return JS_FALSE;
            } else {
                if (!js_EmitTree(cx, cg, pn2->pn_kid2))
                    return JS_FALSE;
                if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0,
                                         CG_OFFSET(cg) - top)) {
                    return JS_FALSE;
                }
                beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
                if (beq < 0)
                    return JS_FALSE;
            }

            /* Set pn3 (used below) here to avoid spurious gcc warnings. */
            pn3 = pn2->pn_kid3;
        }

        /* Emit code for the loop body. */
        if (!js_EmitTree(cx, cg, pn->pn_right))
            return JS_FALSE;

        if (pn2->pn_type != TOK_IN) {
            /* Set the second note offset so we can find the update part. */
            JS_ASSERT(noteIndex != -1);
            if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1,
                                     CG_OFFSET(cg) - top)) {
                return JS_FALSE;
            }

            if (pn3) {
                /* Set loop and enclosing "update" offsets, for continue. */
                stmt = &stmtInfo;
                do {
                    stmt->update = CG_OFFSET(cg);
                } while ((stmt = stmt->down) != NULL &&
                         stmt->type == STMT_LABEL);

                op = JSOP_POP;
#if JS_HAS_DESTRUCTURING
                if (pn3->pn_type == TOK_ASSIGN &&
                    !MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) {
                    return JS_FALSE;
                }
#endif
                if (op == JSOP_POP) {
                    if (!js_EmitTree(cx, cg, pn3))
                        return JS_FALSE;
                    if (js_Emit1(cx, cg, op) < 0)
                        return JS_FALSE;
                }

                /* Restore the absolute line number for source note readers. */
                off = (ptrdiff_t) pn->pn_pos.end.lineno;
                if (CG_CURRENT_LINE(cg) != (uintN) off) {
                    if (js_NewSrcNote2(cx, cg, SRC_SETLINE, off) < 0)
                        return JS_FALSE;
                    CG_CURRENT_LINE(cg) = (uintN) off;
                }
            }

            /* The third note offset helps us find the loop-closing jump. */
            if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 2,
                                     CG_OFFSET(cg) - top)) {
                return JS_FALSE;
            }
        }

        /* Emit the loop-closing jump and fixup all jump offsets. */
        jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg));
        if (jmp < 0)
            return JS_FALSE;
        if (beq > 0)
            CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
        if (pn2->pn_type == TOK_IN) {
            /* Set the SRC_WHILE note offset so we can find the closing jump. */
            JS_ASSERT(noteIndex != -1);
            if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, jmp - beq))
                return JS_FALSE;
        }

        /* Now fixup all breaks and continues (before for/in's JSOP_ENDITER). */
        if (!js_PopStatementCG(cx, cg))
            return JS_FALSE;

        if (pn2->pn_type == TOK_IN) {
            if (js_Emit1(cx, cg, JSOP_ENDITER) < 0)
                return JS_FALSE;
        }
        break;

      case TOK_BREAK:
        stmt = cg->treeContext.topStmt;
        atom = pn->pn_atom;
        if (atom) {
            ale = js_IndexAtom(cx, atom, &cg->atomList);
            if (!ale)
                return JS_FALSE;
            while (stmt->type != STMT_LABEL || stmt->atom != atom)
                stmt = stmt->down;
            noteType = SRC_BREAK2LABEL;
        } else {
            ale = NULL;
            while (!STMT_IS_LOOP(stmt) && stmt->type != STMT_SWITCH)
                stmt = stmt->down;
            noteType = SRC_NULL;
        }

        if (EmitGoto(cx, cg, stmt, &stmt->breaks, ale, noteType) < 0)
            return JS_FALSE;
        break;

      case TOK_CONTINUE:
        stmt = cg->treeContext.topStmt;
        atom = pn->pn_atom;
        if (atom) {
            /* Find the loop statement enclosed by the matching label. */
            JSStmtInfo *loop = NULL;
            ale = js_IndexAtom(cx, atom, &cg->atomList);
            if (!ale)
                return JS_FALSE;
            while (stmt->type != STMT_LABEL || stmt->atom != atom) {
                if (STMT_IS_LOOP(stmt))
                    loop = stmt;
                stmt = stmt->down;
            }
            stmt = loop;
            noteType = SRC_CONT2LABEL;
        } else {
            ale = NULL;
            while (!STMT_IS_LOOP(stmt))
                stmt = stmt->down;
            noteType = SRC_CONTINUE;
        }

        if (EmitGoto(cx, cg, stmt, &stmt->continues, ale, noteType) < 0)
            return JS_FALSE;
        break;

      case TOK_WITH:
        if (!js_EmitTree(cx, cg, pn->pn_left))
            return JS_FALSE;
        js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WITH, CG_OFFSET(cg));
        if (js_Emit1(cx, cg, JSOP_ENTERWITH) < 0)
            return JS_FALSE;
        if (!js_EmitTree(cx, cg, pn->pn_right))
            return JS_FALSE;
        if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0)
            return JS_FALSE;
        ok = js_PopStatementCG(cx, cg);
        break;

      case TOK_TRY:
      {
        ptrdiff_t start, end, catchJump, catchStart, finallyCatch;
        intN depth;
        JSParseNode *lastCatch;

        catchJump = catchStart = finallyCatch = -1;

        /*
         * Push stmtInfo to track jumps-over-catches and gosubs-to-finally
         * for later fixup.
         *
         * When a finally block is 'active' (STMT_FINALLY on the treeContext),
         * non-local jumps (including jumps-over-catches) result in a GOSUB
         * being written into the bytecode stream and fixed-up later (c.f.
         * EmitBackPatchOp and BackPatch).
         */
        js_PushStatement(&cg->treeContext, &stmtInfo,
                         pn->pn_kid3 ? STMT_FINALLY : STMT_TRY,
                         CG_OFFSET(cg));

        /*
         * About JSOP_SETSP: an exception can be thrown while the stack is in
         * an unbalanced state, and this imbalance causes problems with things
         * like function invocation later on.
         *
         * To fix this, we compute the 'balanced' stack depth upon try entry,
         * and then restore the stack to this depth when we hit the first catch
         * or finally block.  We can't just zero the stack, because things like
         * for/in and with that are active upon entry to the block keep state
         * variables on the stack.
         */
        depth = cg->stackDepth;

        /* Mark try location for decompilation, then emit try block. */
        if (js_Emit1(cx, cg, JSOP_TRY) < 0)
            return JS_FALSE;
        start = CG_OFFSET(cg);
        if (!js_EmitTree(cx, cg, pn->pn_kid1))
            return JS_FALSE;

        /* GOSUB to finally, if present. */
        if (pn->pn_kid3) {
            if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
                return JS_FALSE;
            jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &GOSUBS(stmtInfo));
            if (jmp < 0)
                return JS_FALSE;

            /* JSOP_RETSUB pops the return pc-index, balancing the stack. */
            cg->stackDepth = depth;
        }

        /* Emit (hidden) jump over catch and/or finally. */
        if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
            return JS_FALSE;
        jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &catchJump);
        if (jmp < 0)
            return JS_FALSE;

        end = CG_OFFSET(cg);

        /* If this try has a catch block, emit it. */
        pn2 = pn->pn_kid2;
        lastCatch = NULL;
        if (pn2) {
            jsint count = 0;    /* previous catch block's population */

            catchStart = end;

            /*
             * The emitted code for a catch block looks like:
             *
             * [throwing]                          only if 2nd+ catch block
             * [leaveblock]                        only if 2nd+ catch block
             * enterblock                          with SRC_CATCH
             * exception
             * [dup]                               only if catchguard
             * setlocalpop <slot>                  or destructuring code
             * [< catchguard code >]               if there's a catchguard
             * [ifeq <offset to next catch block>]         " "
             * [pop]                               only if catchguard
             * < catch block contents >
             * leaveblock
             * goto <end of catch blocks>          non-local; finally applies
             *
             * If there's no catch block without a catchguard, the last
             * <offset to next catch block> points to rethrow code.  This
             * code will [gosub] to the finally code if appropriate, and is
             * also used for the catch-all trynote for capturing exceptions
             * thrown from catch{} blocks.
             */
            for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
                ptrdiff_t guardJump, catchNote;

                guardJump = GUARDJUMP(stmtInfo);
                if (guardJump == -1) {
                    /* Set stack to original depth (see SETSP comment above). */
                    EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth);
                    cg->stackDepth = depth;
                } else {
                    /* Fix up and clean up previous catch block. */
                    CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, guardJump);

                    /*
                     * Account for the pushed exception object that we still
                     * have after the jumping from the previous guard.
                     */
                    JS_ASSERT(cg->stackDepth == depth);
                    cg->stackDepth = depth + 1;

                    /*
                     * Move exception back to cx->exception to prepare for
                     * the next catch. We hide [throwing] from the decompiler
                     * since it compensates for the hidden JSOP_DUP at the
                     * start of the previous guarded catch.
                     */
                    if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
                        js_Emit1(cx, cg, JSOP_THROWING) < 0) {
                        return JS_FALSE;
                    }

                    /*
                     * Emit an unbalanced [leaveblock] for the previous catch,
                     * whose block object count is saved below.
                     */
                    if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
                        return JS_FALSE;
                    JS_ASSERT(count >= 0);
                    EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count);
                }

                /*
                 * Annotate the JSOP_ENTERBLOCK that's about to be generated
                 * by the call to js_EmitTree immediately below.  Save this
                 * source note's index in stmtInfo for use by the TOK_CATCH:
                 * case, where the length of the catch guard is set as the
                 * note's offset.
                 */
                catchNote = js_NewSrcNote2(cx, cg, SRC_CATCH, 0);
                if (catchNote < 0)
                    return JS_FALSE;
                CATCHNOTE(stmtInfo) = catchNote;

                /*
                 * Emit the lexical scope and catch body.  Save the catch's
                 * block object population via count, for use when targeting
                 * guardJump at the next catch (the guard mismatch case).
                 */
                JS_ASSERT(pn3->pn_type == TOK_LEXICALSCOPE);
                count = OBJ_BLOCK_COUNT(cx, ATOM_TO_OBJECT(pn3->pn_atom));
                if (!js_EmitTree(cx, cg, pn3))
                    return JS_FALSE;

                /* gosub <finally>, if required */
                if (pn->pn_kid3) {
                    jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH,
                                          &GOSUBS(stmtInfo));
                    if (jmp < 0)
                        return JS_FALSE;
                    JS_ASSERT(cg->stackDepth == depth);
                }

                /*
                 * Jump over the remaining catch blocks.  This will get fixed
                 * up to jump to after catch/finally.
                 */
                if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
                    return JS_FALSE;
                jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &catchJump);
                if (jmp < 0)
                    return JS_FALSE;

                /*
                 * Save a pointer to the last catch node to handle try-finally
                 * and try-catch(guard)-finally special cases.
                 */
                lastCatch = pn3->pn_expr;
            }
        }

        /*
         * Last catch guard jumps to the rethrow code sequence if none of the
         * guards match. Target guardJump at the beginning of the rethrow
         * sequence, just in case a guard expression throws and leaves the
         * stack unbalanced.
         */
        if (lastCatch && lastCatch->pn_kid2) {
            CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, GUARDJUMP(stmtInfo));

            /* Sync the stack to take into account pushed exception. */
            JS_ASSERT(cg->stackDepth == depth);
            cg->stackDepth = depth + 1;

            /*
             * Rethrow the exception, delegating executing of finally if any
             * to the exception handler.
             */
            if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
                js_Emit1(cx, cg, JSOP_THROW) < 0) {
                return JS_FALSE;
            }
        }

        JS_ASSERT(cg->stackDepth == depth);

        /* Emit finally handler if any. */
        if (pn->pn_kid3) {
            /*
             * We emit [setsp][gosub] to call try-finally when an exception is
             * thrown from try or try-catch blocks. The [gosub] and [retsub]
             * opcodes will take care of stacking and rethrowing any exception
             * pending across the finally.
             */
            finallyCatch = CG_OFFSET(cg);
            EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth);

            jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH,
                                  &GOSUBS(stmtInfo));
            if (jmp < 0)
                return JS_FALSE;

            JS_ASSERT(cg->stackDepth == depth);
            JS_ASSERT((uintN)depth <= cg->maxStackDepth);

            /*
             * Fix up the gosubs that might have been emitted before non-local
             * jumps to the finally code.
             */
            if (!BackPatch(cx, cg, GOSUBS(stmtInfo), CG_NEXT(cg), JSOP_GOSUB))
                return JS_FALSE;

            /*
             * The stack budget must be balanced at this point.  All [gosub]
             * calls emitted before this point will push two stack slots, one
             * for the pending exception (or JSVAL_HOLE if there is no pending
             * exception) and one for the [retsub] pc-index.
             */
            JS_ASSERT(cg->stackDepth == depth);
            cg->stackDepth += 2;
            if ((uintN)cg->stackDepth > cg->maxStackDepth)
                cg->maxStackDepth = cg->stackDepth;

            /* Now indicate that we're emitting a subroutine body. */
            stmtInfo.type = STMT_SUBROUTINE;
            if (!UpdateLineNumberNotes(cx, cg, pn->pn_kid3))
                return JS_FALSE;
            if (js_Emit1(cx, cg, JSOP_FINALLY) < 0 ||
                !js_EmitTree(cx, cg, pn->pn_kid3) ||
                js_Emit1(cx, cg, JSOP_RETSUB) < 0) {
                return JS_FALSE;
            }

            /* Restore stack depth budget to its balanced state. */
            JS_ASSERT(cg->stackDepth == depth + 2);
            cg->stackDepth = depth;
        }
        if (!js_PopStatementCG(cx, cg))
            return JS_FALSE;

        if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 ||
            js_Emit1(cx, cg, JSOP_NOP) < 0) {
            return JS_FALSE;
        }

        /* Fix up the end-of-try/catch jumps to come here. */
        if (!BackPatch(cx, cg, catchJump, CG_NEXT(cg), JSOP_GOTO))
            return JS_FALSE;

        /*
         * Add the try note last, to let post-order give us the right ordering
         * (first to last for a given nesting level, inner to outer by level).
         */
        if (pn->pn_kid2) {
            JS_ASSERT(end != -1 && catchStart != -1);
            if (!js_NewTryNote(cx, cg, start, end, catchStart))
                return JS_FALSE;
        }

        /*
         * If we've got a finally, mark try+catch region with additional
         * trynote to catch exceptions (re)thrown from a catch block or
         * for the try{}finally{} case.
         */
        if (pn->pn_kid3) {
            JS_ASSERT(finallyCatch != -1);
            if (!js_NewTryNote(cx, cg, start, finallyCatch, finallyCatch))
                return JS_FALSE;
        }
        break;
      }

      case TOK_CATCH:
      {
        ptrdiff_t catchStart, guardJump;

        /*
         * Morph STMT_BLOCK to STMT_CATCH, note the block entry code offset,
         * and save the block object atom.
         */
        stmt = cg->treeContext.topStmt;
        JS_ASSERT(stmt->type == STMT_BLOCK && (stmt->flags & SIF_SCOPE));
        stmt->type = STMT_CATCH;
        catchStart = stmt->update;
        atom = stmt->atom;

        /* Go up one statement info record to the TRY or FINALLY record. */
        stmt = stmt->down;
        JS_ASSERT(stmt->type == STMT_TRY || stmt->type == STMT_FINALLY);

        /* Pick up the pending exception and bind it to the catch variable. */
        if (js_Emit1(cx, cg, JSOP_EXCEPTION) < 0)
            return JS_FALSE;

        /*
         * Dup the exception object if there is a guard for rethrowing to use
         * it later when rethrowing or in other catches.
         */
        if (pn->pn_kid2) {
            if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
                js_Emit1(cx, cg, JSOP_DUP) < 0) {
                return JS_FALSE;
            }
        }

        pn2 = pn->pn_kid1;
        switch (pn2->pn_type) {
#if JS_HAS_DESTRUCTURING
          case TOK_RB:
          case TOK_RC:
            if (!EmitDestructuringOps(cx, cg, JSOP_NOP, pn2))
                return JS_FALSE;
            if (js_Emit1(cx, cg, JSOP_POP) < 0)
                return JS_FALSE;
            break;
#endif

          case TOK_NAME:
            /* Inline BindNameToSlot, adding block depth to pn2->pn_slot. */
            pn2->pn_slot += OBJ_BLOCK_DEPTH(cx, ATOM_TO_OBJECT(atom));
            EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, pn2->pn_slot);
            break;

          default:
            JS_ASSERT(0);
        }

        /* Emit the guard expression, if there is one. */
        if (pn->pn_kid2) {
            if (!js_EmitTree(cx, cg, pn->pn_kid2))
                return JS_FALSE;
            if (!js_SetSrcNoteOffset(cx, cg, CATCHNOTE(*stmt), 0,
                                     CG_OFFSET(cg) - catchStart)) {
                return JS_FALSE;
            }
            /* ifeq <next block> */
            guardJump = EmitJump(cx, cg, JSOP_IFEQ, 0);
            if (guardJump < 0)
                return JS_FALSE;
            GUARDJUMP(*stmt) = guardJump;

            /* Pop duplicated exception object as we no longer need it. */
            if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
                js_Emit1(cx, cg, JSOP_POP) < 0) {
                return JS_FALSE;
            }
        }

        /* Emit the catch body. */
        if (!js_EmitTree(cx, cg, pn->pn_kid3))
            return JS_FALSE;

        /*
         * Annotate the JSOP_LEAVEBLOCK that will be emitted as we unwind via
         * our TOK_LEXICALSCOPE parent, so the decompiler knows to pop.
         */
        off = cg->stackDepth;
        if (js_NewSrcNote2(cx, cg, SRC_CATCH, off) < 0)
            return JS_FALSE;
        break;
      }

      case TOK_VAR:
        if (!EmitVariables(cx, cg, pn, JS_FALSE, &noteIndex))
            return JS_FALSE;
        break;

      case TOK_RETURN:
        /* Push a return value */
        pn2 = pn->pn_kid;
        if (pn2) {
            if (!js_EmitTree(cx, cg, pn2))
                return JS_FALSE;
        } else {
            if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
                return JS_FALSE;
        }

        /*
         * EmitNonLocalJumpFixup mutates op to JSOP_RETRVAL after emitting a
         * JSOP_SETRVAL if there are open try blocks having finally clauses.
         * We can't simply transfer control flow to our caller in that case,
         * because we must gosub to those clauses from inner to outer, with
         * the correct stack pointer (i.e., after popping any with, for/in,
         * etc., slots nested inside the finally's try).
         */
        op = JSOP_RETURN;
        if (!EmitNonLocalJumpFixup(cx, cg, NULL, &op))
            return JS_FALSE;
        if (js_Emit1(cx, cg, op) < 0)
            return JS_FALSE;
        break;

#if JS_HAS_GENERATORS
      case TOK_YIELD:
        if (pn->pn_kid) {
            if (!js_EmitTree(cx, cg, pn->pn_kid))
                return JS_FALSE;
        } else {
            if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
                return JS_FALSE;
        }
        if (js_Emit1(cx, cg, JSOP_YIELD) < 0)
            return JS_FALSE;
        break;
#endif

      case TOK_LC:
#if JS_HAS_XML_SUPPORT
        if (pn->pn_arity == PN_UNARY) {
            if (!js_EmitTree(cx, cg, pn->pn_kid))
                return JS_FALSE;
            if (js_Emit1(cx, cg, pn->pn_op) < 0)
                return JS_FALSE;
            break;
        }
#endif

        JS_ASSERT(pn->pn_arity == PN_LIST);

        noteIndex = -1;
        tmp = CG_OFFSET(cg);
        if (pn->pn_extra & PNX_NEEDBRACES) {
            noteIndex = js_NewSrcNote2(cx, cg, SRC_BRACE, 0);
            if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0)
                return JS_FALSE;
        }

        js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BLOCK, top);
        for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
            if (!js_EmitTree(cx, cg, pn2))
                return JS_FALSE;
        }

        if (noteIndex >= 0 &&
            !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0,
                                 CG_OFFSET(cg) - tmp)) {
            return JS_FALSE;
        }

        ok = js_PopStatementCG(cx, cg);
        break;

      case TOK_BODY:
        JS_ASSERT(pn->pn_arity == PN_LIST);
        js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BODY, top);
        for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
            if (!js_EmitTree(cx, cg, pn2))
                return JS_FALSE;
        }
        ok = js_PopStatementCG(cx, cg);
        break;

      case TOK_SEMI:
        pn2 = pn->pn_kid;
        if (pn2) {
            /*
             * Top-level or called-from-a-native JS_Execute/EvaluateScript,
             * debugger, and eval frames may need the value of the ultimate
             * expression statement as the script's result, despite the fact
             * that it appears useless to the compiler.
             */
            useful = wantval = !cx->fp->fun ||
                               !FUN_INTERPRETED(cx->fp->fun) ||
                               (cx->fp->flags & JSFRAME_SPECIAL);
            if (!useful) {
                if (!CheckSideEffects(cx, &cg->treeContext, pn2, &useful))
                    return JS_FALSE;
            }

            /*
             * Don't eliminate apparently useless expressions if they are
             * labeled expression statements.  The tc->topStmt->update test
             * catches the case where we are nesting in js_EmitTree for a
             * labeled compound statement.
             */
            if (!useful &&
                (!cg->treeContext.topStmt ||
                 cg->treeContext.topStmt->type != STMT_LABEL ||
                 cg->treeContext.topStmt->update < CG_OFFSET(cg))) {
                CG_CURRENT_LINE(cg) = pn2->pn_pos.begin.lineno;
                if (!js_ReportCompileErrorNumber(cx, cg,
                                                 JSREPORT_CG |
                                                 JSREPORT_WARNING |
                                                 JSREPORT_STRICT,
                                                 JSMSG_USELESS_EXPR)) {
                    return JS_FALSE;
                }
            } else {
                op = wantval ? JSOP_POPV : JSOP_POP;
#if JS_HAS_DESTRUCTURING
                if (!wantval &&
                    pn2->pn_type == TOK_ASSIGN &&
                    !MaybeEmitGroupAssignment(cx, cg, op, pn2, &op)) {
                    return JS_FALSE;
                }
#endif
                if (op != JSOP_NOP) {
                    if (!js_EmitTree(cx, cg, pn2))
                        return JS_FALSE;
                    if (js_Emit1(cx, cg, op) < 0)
                        return JS_FALSE;
                }
            }
        }
        break;

      case TOK_COLON:
        /* Emit an annotated nop so we know to decompile a label. */
        atom = pn->pn_atom;
        ale = js_IndexAtom(cx, atom, &cg->atomList);
        if (!ale)
            return JS_FALSE;
        pn2 = pn->pn_expr;
        noteType = (pn2->pn_type == TOK_LC ||
                    (pn2->pn_type == TOK_LEXICALSCOPE &&
                     pn2->pn_expr->pn_type == TOK_LC))
                   ? SRC_LABELBRACE
                   : SRC_LABEL;
        noteIndex = js_NewSrcNote2(cx, cg, noteType,
                                   (ptrdiff_t) ALE_INDEX(ale));
        if (noteIndex < 0 ||
            js_Emit1(cx, cg, JSOP_NOP) < 0) {
            return JS_FALSE;
        }

        /* Emit code for the labeled statement. */
        js_PushStatement(&cg->treeContext, &stmtInfo, STMT_LABEL,
                         CG_OFFSET(cg));
        stmtInfo.atom = atom;
        if (!js_EmitTree(cx, cg, pn2))
            return JS_FALSE;
        if (!js_PopStatementCG(cx, cg))
            return JS_FALSE;

        /* If the statement was compound, emit a note for the end brace. */
        if (noteType == SRC_LABELBRACE) {
            if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 ||
                js_Emit1(cx, cg, JSOP_NOP) < 0) {
                return JS_FALSE;
            }
        }
        break;

      case TOK_COMMA:
        /*
         * Emit SRC_PCDELTA notes on each JSOP_POP between comma operands.
         * These notes help the decompiler bracket the bytecodes generated
         * from each sub-expression that follows a comma.
         */
        off = noteIndex = -1;
        for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) {
            if (!js_EmitTree(cx, cg, pn2))
                return JS_FALSE;
            tmp = CG_OFFSET(cg);
            if (noteIndex >= 0) {
                if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off))
                    return JS_FALSE;
            }
            if (!pn2->pn_next)
                break;
            off = tmp;
            noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0);
            if (noteIndex < 0 ||
                js_Emit1(cx, cg, JSOP_POP) < 0) {
                return JS_FALSE;
            }
        }
        break;

      case TOK_ASSIGN:
        /*
         * Check left operand type and generate specialized code for it.
         * Specialize to avoid ECMA "reference type" values on the operand
         * stack, which impose pervasive runtime "GetValue" costs.
         */
        pn2 = pn->pn_left;
        JS_ASSERT(pn2->pn_type != TOK_RP);
        atomIndex = (jsatomid) -1;
        switch (pn2->pn_type) {
          case TOK_NAME:
            if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE))
                return JS_FALSE;
            if (pn2->pn_slot >= 0) {
                atomIndex = (jsatomid) pn2->pn_slot;
            } else {
                ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);
                if (!ale)
                    return JS_FALSE;
                atomIndex = ALE_INDEX(ale);
                EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex);
            }
            break;
          case TOK_DOT:
            if (!js_EmitTree(cx, cg, pn2->pn_expr))
                return JS_FALSE;
            ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);
            if (!ale)
                return JS_FALSE;
            atomIndex = ALE_INDEX(ale);
            break;
          case TOK_LB:
            JS_ASSERT(pn2->pn_arity == PN_BINARY);
            if (!js_EmitTree(cx, cg, pn2->pn_left))
                return JS_FALSE;
            if (!js_EmitTree(cx, cg, pn2->pn_right))
                return JS_FALSE;
            break;
#if JS_HAS_DESTRUCTURING
          case TOK_RB:
          case TOK_RC:
            break;
#endif
#if JS_HAS_LVALUE_RETURN
          case TOK_LP:
            if (!js_EmitTree(cx, cg, pn2))
                return JS_FALSE;
            break;
#endif
#if JS_HAS_XML_SUPPORT
          case TOK_UNARYOP:
            JS_ASSERT(pn2->pn_op == JSOP_SETXMLNAME);
            if (!js_EmitTree(cx, cg, pn2->pn_kid))
                return JS_FALSE;
            if (js_Emit1(cx, cg, JSOP_BINDXMLNAME) < 0)
                return JS_FALSE;
            break;
#endif
          default:
            JS_ASSERT(0);
        }

        op = pn->pn_op;
#if JS_HAS_GETTER_SETTER
        if (op == JSOP_GETTER || op == JSOP_SETTER) {
            /* We'll emit these prefix bytecodes after emitting the r.h.s. */
            if (atomIndex != (jsatomid) -1 && atomIndex >= JS_BIT(16)) {
                ReportStatementTooLarge(cx, cg);
                return JS_FALSE;
            }
            if (pn2->pn_type == TOK_NAME && pn2->pn_op != JSOP_SETNAME) {
                /*
                 * x getter = y where x is a local or let variable is not
                 * supported.
                 */
                js_ReportCompileErrorNumber(cx,
                                            pn2, JSREPORT_PN | JSREPORT_ERROR,
                                            JSMSG_BAD_GETTER_OR_SETTER,
                                            (op == JSOP_GETTER)
                                            ? js_getter_str
                                            : js_setter_str);
                return JS_FALSE;
            }
        } else
#endif
        /* If += or similar, dup the left operand and get its value. */
        if (op != JSOP_NOP) {
            switch (pn2->pn_type) {
              case TOK_NAME:
                if (pn2->pn_op != JSOP_SETNAME) {
                    EMIT_UINT16_IMM_OP((pn2->pn_op == JSOP_SETGVAR)
                                       ? JSOP_GETGVAR
                                       : (pn2->pn_op == JSOP_SETARG)
                                       ? JSOP_GETARG
                                       : (pn2->pn_op == JSOP_SETLOCAL)
                                       ? JSOP_GETLOCAL
                                       : JSOP_GETVAR,
                                       atomIndex);
                    break;
                }
                /* FALL THROUGH */
              case TOK_DOT:
                if (js_Emit1(cx, cg, JSOP_DUP) < 0)
                    return JS_FALSE;
                EMIT_ATOM_INDEX_OP((pn2->pn_type == TOK_NAME)
                                   ? JSOP_GETXPROP
                                   : JSOP_GETPROP,
                                   atomIndex);
                break;
              case TOK_LB:
#if JS_HAS_LVALUE_RETURN
              case TOK_LP:
#endif
#if JS_HAS_XML_SUPPORT
              case TOK_UNARYOP:
#endif
                if (js_Emit1(cx, cg, JSOP_DUP2) < 0)
                    return JS_FALSE;
                if (js_Emit1(cx, cg, JSOP_GETELEM) < 0)
                    return JS_FALSE;
                break;
              default:;
            }
        }

        /* Now emit the right operand (it may affect the namespace). */
        if (!js_EmitTree(cx, cg, pn->pn_right))
            return JS_FALSE;

        /* If += etc., emit the binary operator with a decompiler note. */
        if (op != JSOP_NOP) {
            /*
             * Take care to avoid SRC_ASSIGNOP if the left-hand side is a
             * const declared in a function (i.e., with non-negative pn_slot
             * and JSPROP_READONLY in pn_attrs), as in this case (just a bit
             * further below) we will avoid emitting the assignment op.
             */
            if (pn2->pn_type != TOK_NAME ||
                pn2->pn_slot < 0 ||
                !(pn2->pn_attrs & JSPROP_READONLY)) {
                if (js_NewSrcNote(cx, cg, SRC_ASSIGNOP) < 0)
                    return JS_FALSE;
            }
            if (js_Emit1(cx, cg, op) < 0)
                return JS_FALSE;
        }

        /* Left parts such as a.b.c and a[b].c need a decompiler note. */
        if (pn2->pn_type != TOK_NAME &&
#if JS_HAS_DESTRUCTURING
            pn2->pn_type != TOK_RB &&
            pn2->pn_type != TOK_RC &&
#endif
            js_NewSrcNote2(cx, cg, SrcNoteForPropOp(pn2, pn2->pn_op),
                           CG_OFFSET(cg) - top) < 0) {
            return JS_FALSE;
        }

        /* Finally, emit the specialized assignment bytecode. */
        switch (pn2->pn_type) {
          case TOK_NAME:
            if (pn2->pn_slot >= 0) {
                if (!(pn2->pn_attrs & JSPROP_READONLY))
                    EMIT_UINT16_IMM_OP(pn2->pn_op, atomIndex);
                break;
            }
            /* FALL THROUGH */
          case TOK_DOT:
            EMIT_ATOM_INDEX_OP(pn2->pn_op, atomIndex);
            break;
          case TOK_LB:
#if JS_HAS_LVALUE_RETURN
          case TOK_LP:
#endif
            if (js_Emit1(cx, cg, JSOP_SETELEM) < 0)
                return JS_FALSE;
            break;
#if JS_HAS_DESTRUCTURING
          case TOK_RB:
          case TOK_RC:
            if (!EmitDestructuringOps(cx, cg, JSOP_SETNAME, pn2))
                return JS_FALSE;
            break;
#endif
#if JS_HAS_XML_SUPPORT
          case TOK_UNARYOP:
            if (js_Emit1(cx, cg, JSOP_SETXMLNAME) < 0)
                return JS_FALSE;
            break;
#endif
          default:
            JS_ASSERT(0);
        }
        break;

      case TOK_HOOK:
        /* Emit the condition, then branch if false to the else part. */
        if (!js_EmitTree(cx, cg, pn->pn_kid1))
            return JS_FALSE;
        noteIndex = js_NewSrcNote(cx, cg, SRC_COND);
        if (noteIndex < 0)
            return JS_FALSE;
        beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
        if (beq < 0 || !js_EmitTree(cx, cg, pn->pn_kid2))
            return JS_FALSE;

        /* Jump around else, fixup the branch, emit else, fixup jump. */
        jmp = EmitJump(cx, cg, JSOP_GOTO, 0);
        if (jmp < 0)
            return JS_FALSE;
        CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);

        /*
         * Because each branch pushes a single value, but our stack budgeting
         * analysis ignores branches, we now have to adjust cg->stackDepth to
         * ignore the value pushed by the first branch.  Execution will follow
         * only one path, so we must decrement cg->stackDepth.
         *
         * Failing to do this will foil code, such as the try/catch/finally
         * exception handling code generator, that samples cg->stackDepth for
         * use at runtime (JSOP_SETSP), or in let expression and block code
         * generation, which must use the stack depth to compute local stack
         * indexes correctly.
         */
        JS_ASSERT(cg->stackDepth > 0);
        cg->stackDepth--;
        if (!js_EmitTree(cx, cg, pn->pn_kid3))
            return JS_FALSE;
        CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
        if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq))
            return JS_FALSE;
        break;

      case TOK_OR:
      case TOK_AND:
        /*
         * JSOP_OR converts the operand on the stack to boolean, and if true,
         * leaves the original operand value on the stack and jumps; otherwise
         * it pops and falls into the next bytecode, which evaluates the right
         * operand.  The jump goes around the right operand evaluation.
         *
         * JSOP_AND converts the operand on the stack to boolean, and if false,
         * leaves the original operand value on the stack and jumps; otherwise
         * it pops and falls into the right operand's bytecode.
         *
         * Avoid tail recursion for long ||...|| expressions and long &&...&&
         * expressions or long mixtures of ||'s and &&'s that can easily blow
         * the stack, by forward-linking and then backpatching all the JSOP_OR
         * and JSOP_AND bytecodes' immediate jump-offset operands.
         */
        pn3 = pn;
        if (!js_EmitTree(cx, cg, pn->pn_left))
            return JS_FALSE;
        top = EmitJump(cx, cg, JSOP_BACKPATCH_POP, 0);
        if (top < 0)
            return JS_FALSE;
        jmp = top;
        pn2 = pn->pn_right;
        while (pn2->pn_type == TOK_OR || pn2->pn_type == TOK_AND) {
            pn = pn2;
            if (!js_EmitTree(cx, cg, pn->pn_left))
                return JS_FALSE;
            off = EmitJump(cx, cg, JSOP_BACKPATCH_POP, 0);
            if (off < 0)
                return JS_FALSE;
            if (!SetBackPatchDelta(cx, cg, CG_CODE(cg, jmp), off - jmp))
                return JS_FALSE;
            jmp = off;
            pn2 = pn->pn_right;
        }
        if (!js_EmitTree(cx, cg, pn2))
            return JS_FALSE;
        off = CG_OFFSET(cg);
        do {
            pc = CG_CODE(cg, top);
            tmp = GetJumpOffset(cg, pc);
            CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, off - top);
            *pc = pn3->pn_op;
            top += tmp;
        } while ((pn3 = pn3->pn_right) != pn2);
        break;

      case TOK_BITOR:
      case TOK_BITXOR:
      case TOK_BITAND:
      case TOK_EQOP:
      case TOK_RELOP:
      case TOK_IN:
      case TOK_INSTANCEOF:
      case TOK_SHOP:
      case TOK_PLUS:
      case TOK_MINUS:
      case TOK_STAR:
      case TOK_DIVOP:
        if (pn->pn_arity == PN_LIST) {
            /* Left-associative operator chain: avoid too much recursion. */
            pn2 = pn->pn_head;
            if (!js_EmitTree(cx, cg, pn2))
                return JS_FALSE;
            op = pn->pn_op;
            while ((pn2 = pn2->pn_next) != NULL) {
                if (!js_EmitTree(cx, cg, pn2))
                    return JS_FALSE;
                if (js_Emit1(cx, cg, op) < 0)
                    return JS_FALSE;
            }
        } else {
#if JS_HAS_XML_SUPPORT
            uintN oldflags;

      case TOK_DBLCOLON:
            if (pn->pn_arity == PN_NAME) {
                if (!js_EmitTree(cx, cg, pn->pn_expr))
                    return JS_FALSE;
                if (!EmitAtomOp(cx, pn, pn->pn_op, cg))
                    return JS_FALSE;
                break;
            }

            /*
             * Binary :: has a right operand that brackets arbitrary code,
             * possibly including a let (a = b) ... expression.  We must clear
             * TCF_IN_FOR_INIT to avoid mis-compiling such beasts.
             */
            oldflags = cg->treeContext.flags;
            cg->treeContext.flags &= ~TCF_IN_FOR_INIT;
#endif

            /* Binary operators that evaluate both operands unconditionally. */
            if (!js_EmitTree(cx, cg, pn->pn_left))
                return JS_FALSE;
            if (!js_EmitTree(cx, cg, pn->pn_right))
                return JS_FALSE;
#if JS_HAS_XML_SUPPORT
            cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT;
#endif
            if (js_Emit1(cx, cg, pn->pn_op) < 0)
                return JS_FALSE;
        }
        break;

      case TOK_THROW:
#if JS_HAS_XML_SUPPORT
      case TOK_AT:
      case TOK_DEFAULT:
        JS_ASSERT(pn->pn_arity == PN_UNARY);
        /* FALL THROUGH */
#endif
      case TOK_UNARYOP:
      {
        uintN oldflags;

        /* Unary op, including unary +/-. */
        pn2 = pn->pn_kid;
        op = pn->pn_op;
        if (op == JSOP_TYPEOF) {
            for (pn3 = pn2; pn3->pn_type == TOK_RP; pn3 = pn3->pn_kid)
                continue;
            if (pn3->pn_type != TOK_NAME)
                op = JSOP_TYPEOFEXPR;
        }
        oldflags = cg->treeContext.flags;
        cg->treeContext.flags &= ~TCF_IN_FOR_INIT;
        if (!js_EmitTree(cx, cg, pn2))
            return JS_FALSE;
        cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT;
#if JS_HAS_XML_SUPPORT
        if (op == JSOP_XMLNAME &&
            js_NewSrcNote2(cx, cg, SRC_PCBASE,
                           CG_OFFSET(cg) - pn2->pn_offset) < 0) {
            return JS_FALSE;
        }
#endif
        if (js_Emit1(cx, cg, op) < 0)
            return JS_FALSE;
        break;
      }

      case TOK_INC:
      case TOK_DEC:
      {
        intN depth;

        /* Emit lvalue-specialized code for ++/-- operators. */
        pn2 = pn->pn_kid;
        JS_ASSERT(pn2->pn_type != TOK_RP);
        op = pn->pn_op;
        depth = cg->stackDepth;
        switch (pn2->pn_type) {
          case TOK_NAME:
            pn2->pn_op = op;
            if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE))
                return JS_FALSE;
            op = pn2->pn_op;
            if (pn2->pn_slot >= 0) {
                if (pn2->pn_attrs & JSPROP_READONLY) {
                    /* Incrementing a declared const: just get its value. */
                    op = ((js_CodeSpec[op].format & JOF_TYPEMASK) == JOF_CONST)
                         ? JSOP_GETGVAR
                         : JSOP_GETVAR;
                }
                atomIndex = (jsatomid) pn2->pn_slot;
                EMIT_UINT16_IMM_OP(op, atomIndex);
            } else {
                if (!EmitAtomOp(cx, pn2, op, cg))
                    return JS_FALSE;
            }
            break;
          case TOK_DOT:
            if (!EmitPropOp(cx, pn2, op, cg))
                return JS_FALSE;
            ++depth;
            break;
          case TOK_LB:
            if (!EmitElemOp(cx, pn2, op, cg))
                return JS_FALSE;
            depth += 2;
            break;
#if JS_HAS_LVALUE_RETURN
          case TOK_LP:
            if (!js_EmitTree(cx, cg, pn2))
                return JS_FALSE;
            depth = cg->stackDepth;
            if (js_NewSrcNote2(cx, cg, SRC_PCBASE,
                               CG_OFFSET(cg) - pn2->pn_offset) < 0) {
                return JS_FALSE;
            }
            if (js_Emit1(cx, cg, op) < 0)
                return JS_FALSE;
            break;
#endif
#if JS_HAS_XML_SUPPORT
          case TOK_UNARYOP:
            JS_ASSERT(pn2->pn_op == JSOP_SETXMLNAME);
            if (!js_EmitTree(cx, cg, pn2->pn_kid))
                return JS_FALSE;
            if (js_Emit1(cx, cg, JSOP_BINDXMLNAME) < 0)
                return JS_FALSE;
            depth = cg->stackDepth;
            if (js_Emit1(cx, cg, op) < 0)
                return JS_FALSE;
            break;
#endif
          default:
            JS_ASSERT(0);
        }

        /*
         * Allocate another stack slot for GC protection in case the initial
         * value being incremented or decremented is not a number, but
         * converts to a jsdouble. In the TOK_NAME cases, op has 0 operand
         * uses and 1 definition, so we don't need an extra stack slot -- we
         * can use the one allocated for the def.
         */
        if (pn2->pn_type != TOK_NAME &&
            (js_CodeSpec[op].format & (JOF_INC | JOF_DEC)) &&
            (uintN)depth == cg->maxStackDepth) {
            ++cg->maxStackDepth;
        }
        break;
      }

      case TOK_DELETE:
        /*
         * Under ECMA 3, deleting a non-reference returns true -- but alas we
         * must evaluate the operand if it appears it might have side effects.
         */
        pn2 = pn->pn_kid;
        switch (pn2->pn_type) {
          case TOK_NAME:
            pn2->pn_op = JSOP_DELNAME;
            if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE))
                return JS_FALSE;
            op = pn2->pn_op;
            if (op == JSOP_FALSE) {
                if (js_Emit1(cx, cg, op) < 0)
                    return JS_FALSE;
            } else {
                if (!EmitAtomOp(cx, pn2, op, cg))
                    return JS_FALSE;
            }
            break;
          case TOK_DOT:
            if (!EmitPropOp(cx, pn2, JSOP_DELPROP, cg))
                return JS_FALSE;
            break;
#if JS_HAS_XML_SUPPORT
          case TOK_DBLDOT:
            if (!EmitElemOp(cx, pn2, JSOP_DELDESC, cg))
                return JS_FALSE;
            break;
#endif
#if JS_HAS_LVALUE_RETURN
          case TOK_LP:
            if (pn2->pn_op != JSOP_SETCALL) {
                JS_ASSERT(pn2->pn_op == JSOP_CALL || pn2->pn_op == JSOP_EVAL);
                pn2->pn_op = JSOP_SETCALL;
            }
            top = CG_OFFSET(cg);
            if (!js_EmitTree(cx, cg, pn2))
                return JS_FALSE;
            if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0)
                return JS_FALSE;
            if (js_Emit1(cx, cg, JSOP_DELELEM) < 0)
                return JS_FALSE;
            break;
#endif
          case TOK_LB:
            if (!EmitElemOp(cx, pn2, JSOP_DELELEM, cg))
                return JS_FALSE;
            break;
          default:
            /*
             * If useless, just emit JSOP_TRUE; otherwise convert delete foo()
             * to foo(), true (a comma expression, requiring SRC_PCDELTA).
             */
            useful = JS_FALSE;
            if (!CheckSideEffects(cx, &cg->treeContext, pn2, &useful))
                return JS_FALSE;
            if (!useful) {
                off = noteIndex = -1;
            } else {
                if (!js_EmitTree(cx, cg, pn2))
                    return JS_FALSE;
                off = CG_OFFSET(cg);
                noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0);
                if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_POP) < 0)
                    return JS_FALSE;
            }
            if (js_Emit1(cx, cg, JSOP_TRUE) < 0)
                return JS_FALSE;
            if (noteIndex >= 0) {
                tmp = CG_OFFSET(cg);
                if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off))
                    return JS_FALSE;
            }
        }
        break;

#if JS_HAS_XML_SUPPORT
      case TOK_FILTER:
        if (!js_EmitTree(cx, cg, pn->pn_left))
            return JS_FALSE;
        jmp = js_Emit3(cx, cg, JSOP_FILTER, 0, 0);
        if (jmp < 0)
            return JS_FALSE;
        if (!js_EmitTree(cx, cg, pn->pn_right))
            return JS_FALSE;
        if (js_Emit1(cx, cg, JSOP_ENDFILTER) < 0)
            return JS_FALSE;
        CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
        break;
#endif

      case TOK_DOT:
        /*
         * Pop a stack operand, convert it to object, get a property named by
         * this bytecode's immediate-indexed atom operand, and push its value
         * (not a reference to it).  This bytecode sets the virtual machine's
         * "obj" register to the left operand's ToObject conversion result,
         * for use by JSOP_PUSHOBJ.
         */
        ok = EmitPropOp(cx, pn, pn->pn_op, cg);
        break;

      case TOK_LB:
#if JS_HAS_XML_SUPPORT
      case TOK_DBLDOT:
#endif
        /*
         * Pop two operands, convert the left one to object and the right one
         * to property name (atom or tagged int), get the named property, and
         * push its value.  Set the "obj" register to the result of ToObject
         * on the left operand.
         */
        ok = EmitElemOp(cx, pn, pn->pn_op, cg);
        break;

      case TOK_NEW:
      case TOK_LP:
      {
        uintN oldflags;

        /*
         * Emit function call or operator new (constructor call) code.
         * First, emit code for the left operand to evaluate the callable or
         * constructable object expression.
         *
         * For E4X, if this expression is a dotted member reference, select
         * JSOP_GETMETHOD instead of JSOP_GETPROP.  ECMA-357 separates XML
         * method lookup from the normal property id lookup done for native
         * objects.
         */
        pn2 = pn->pn_head;
#if JS_HAS_XML_SUPPORT
        if (pn2->pn_type == TOK_DOT && pn2->pn_op != JSOP_GETMETHOD) {
            JS_ASSERT(pn2->pn_op == JSOP_GETPROP);
            pn2->pn_op = JSOP_GETMETHOD;
            pn2->pn_attrs |= JSPROP_IMPLICIT_FUNCTION_NAMESPACE;
        }
#endif
        if (!js_EmitTree(cx, cg, pn2))
            return JS_FALSE;

        /*
         * Push the virtual machine's "obj" register, which was set by a
         * name, property, or element get (or set) bytecode.
         */
        if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0)
            return JS_FALSE;

        /* Remember start of callable-object bytecode for decompilation hint. */
        off = top;

        /*
         * Emit code for each argument in order, then emit the JSOP_*CALL or
         * JSOP_NEW bytecode with a two-byte immediate telling how many args
         * were pushed on the operand stack.
         */
        oldflags = cg->treeContext.flags;
        cg->treeContext.flags &= ~TCF_IN_FOR_INIT;
        for (pn2 = pn2->pn_next; pn2; pn2 = pn2->pn_next) {
            if (!js_EmitTree(cx, cg, pn2))
                return JS_FALSE;
        }
        cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT;
        if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - off) < 0)
            return JS_FALSE;

        argc = pn->pn_count - 1;
        if (js_Emit3(cx, cg, pn->pn_op, ARGC_HI(argc), ARGC_LO(argc)) < 0)
            return JS_FALSE;
        break;
      }

      case TOK_LEXICALSCOPE:
      {
        JSObject *obj;
        jsint count;

        atom = pn->pn_atom;
        obj = ATOM_TO_OBJECT(atom);
        js_PushBlockScope(&cg->treeContext, &stmtInfo, atom, CG_OFFSET(cg));

        OBJ_SET_BLOCK_DEPTH(cx, obj, cg->stackDepth);
        count = OBJ_BLOCK_COUNT(cx, obj);
        cg->stackDepth += count;
        if ((uintN)cg->stackDepth > cg->maxStackDepth)
            cg->maxStackDepth = cg->stackDepth;

        /*
         * If this lexical scope is not for a catch block, let block or let
         * expression, or any kind of for loop (where the scope starts in the
         * head after the first part if for (;;), else in the body if for-in);
         * and if our container is top-level but not a function body, or else
         * a block statement; then emit a SRC_BRACE note.  All other container
         * statements get braces by default from the decompiler.
         */
        noteIndex = -1;
        type = pn->pn_expr->pn_type;
        if (type != TOK_CATCH && type != TOK_LET && type != TOK_FOR &&
            (!(stmt = stmtInfo.down)
             ? !(cg->treeContext.flags & TCF_IN_FUNCTION)
             : stmt->type == STMT_BLOCK)) {
#if defined DEBUG_brendan || defined DEBUG_mrbkap
            /* There must be no source note already output for the next op. */
            JS_ASSERT(CG_NOTE_COUNT(cg) == 0 ||
                      CG_LAST_NOTE_OFFSET(cg) != CG_OFFSET(cg) ||
                      !GettableNoteForNextOp(cg));
#endif
            noteIndex = js_NewSrcNote2(cx, cg, SRC_BRACE, 0);
            if (noteIndex < 0)
                return JS_FALSE;
        }

        ale = js_IndexAtom(cx, atom, &cg->atomList);
        if (!ale)
            return JS_FALSE;
        JS_ASSERT(CG_OFFSET(cg) == top);
        EMIT_ATOM_INDEX_OP(JSOP_ENTERBLOCK, ALE_INDEX(ale));

        if (!js_EmitTree(cx, cg, pn->pn_expr))
            return JS_FALSE;

        op = pn->pn_op;
        if (op == JSOP_LEAVEBLOCKEXPR) {
            if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0)
                return JS_FALSE;
        } else {
            if (noteIndex >= 0 &&
                !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0,
                                     CG_OFFSET(cg) - top)) {
                return JS_FALSE;
            }
        }

        /* Emit the JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR opcode. */
        EMIT_UINT16_IMM_OP(op, count);
        cg->stackDepth -= count;

        ok = js_PopStatementCG(cx, cg);
        break;
      }

#if JS_HAS_BLOCK_SCOPE
      case TOK_LET:
        /* Let statements have their variable declarations on the left. */
        if (pn->pn_arity == PN_BINARY) {
            pn2 = pn->pn_right;
            pn = pn->pn_left;
        } else {
            pn2 = NULL;
        }

        /* Non-null pn2 means that pn is the variable list from a let head. */
        JS_ASSERT(pn->pn_arity == PN_LIST);
        if (!EmitVariables(cx, cg, pn, pn2 != NULL, &noteIndex))
            return JS_FALSE;

        /* Thus non-null pn2 is the body of the let block or expression. */
        tmp = CG_OFFSET(cg);
        if (pn2 && !js_EmitTree(cx, cg, pn2))
            return JS_FALSE;

        if (noteIndex >= 0 &&
            !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0,
                                 CG_OFFSET(cg) - tmp)) {
            return JS_FALSE;
        }
        break;
#endif /* JS_HAS_BLOCK_SCOPE */

#if JS_HAS_GENERATORS
       case TOK_ARRAYPUSH:
        /*
         * The array object's stack index is in cg->arrayCompSlot.  See below
         * under the array initialiser code generator for array comprehension
         * special casing.
         */
        if (!js_EmitTree(cx, cg, pn->pn_kid))
            return JS_FALSE;
        EMIT_UINT16_IMM_OP(pn->pn_op, cg->arrayCompSlot);
        break;
#endif

      case TOK_RB:
#if JS_HAS_GENERATORS
      case TOK_ARRAYCOMP:
#endif
        /*
         * Emit code for [a, b, c] of the form:
         *   t = new Array; t[0] = a; t[1] = b; t[2] = c; t;
         * but use a stack slot for t and avoid dup'ing and popping it via
         * the JSOP_NEWINIT and JSOP_INITELEM bytecodes.
         */
        ale = js_IndexAtom(cx, CLASS_ATOM(cx, Array), &cg->atomList);
        if (!ale)
            return JS_FALSE;
        EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale));
        if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0)
            return JS_FALSE;
        if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0)
            return JS_FALSE;

        pn2 = pn->pn_head;
#if JS_HAS_SHARP_VARS
        if (pn2 && pn2->pn_type == TOK_DEFSHARP) {
            EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num);
            pn2 = pn2->pn_next;
        }
#endif

#if JS_HAS_GENERATORS
        if (pn->pn_type == TOK_ARRAYCOMP) {
            uintN saveSlot;

            /*
             * Pass the new array's stack index to the TOK_ARRAYPUSH case by
             * storing it in pn->pn_extra, then simply traverse the TOK_FOR
             * node and its kids under pn2 to generate this comprehension.
             */
            JS_ASSERT(cg->stackDepth > 0);
            saveSlot = cg->arrayCompSlot;
            cg->arrayCompSlot = (uint32) (cg->stackDepth - 1);
            if (!js_EmitTree(cx, cg, pn2))
                return JS_FALSE;
            cg->arrayCompSlot = saveSlot;

            /* Emit the usual op needed for decompilation. */
            if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0)
                return JS_FALSE;
            break;
        }
#endif /* JS_HAS_GENERATORS */

        for (atomIndex = 0; pn2; atomIndex++, pn2 = pn2->pn_next) {
            if (!EmitNumberOp(cx, atomIndex, cg))
                return JS_FALSE;

            /* FIXME 260106: holes in a sparse initializer are void-filled. */
            if (pn2->pn_type == TOK_COMMA) {
                if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
                    return JS_FALSE;
            } else {
                if (!js_EmitTree(cx, cg, pn2))
                    return JS_FALSE;
            }

            if (js_Emit1(cx, cg, JSOP_INITELEM) < 0)
                return JS_FALSE;
        }

        if (pn->pn_extra & PNX_ENDCOMMA) {
            /* Emit a source note so we know to decompile an extra comma. */
            if (js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0)
                return JS_FALSE;
        }

        /* Emit an op for sharp array cleanup and decompilation. */
        if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0)
            return JS_FALSE;
        break;

      case TOK_RC:
        /*
         * Emit code for {p:a, '%q':b, 2:c} of the form:
         *   t = new Object; t.p = a; t['%q'] = b; t[2] = c; t;
         * but use a stack slot for t and avoid dup'ing and popping it via
         * the JSOP_NEWINIT and JSOP_INITELEM bytecodes.
         */
        ale = js_IndexAtom(cx, CLASS_ATOM(cx, Object), &cg->atomList);
        if (!ale)
            return JS_FALSE;
        EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale));

        if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0)
            return JS_FALSE;
        if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0)
            return JS_FALSE;

        pn2 = pn->pn_head;
#if JS_HAS_SHARP_VARS
        if (pn2 && pn2->pn_type == TOK_DEFSHARP) {
            EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num);
            pn2 = pn2->pn_next;
        }
#endif

        for (; pn2; pn2 = pn2->pn_next) {
            /* Emit an index for t[2], else map an atom for t.p or t['%q']. */
            pn3 = pn2->pn_left;
            switch (pn3->pn_type) {
              case TOK_NUMBER:
                if (!EmitNumberOp(cx, pn3->pn_dval, cg))
                    return JS_FALSE;
                break;
              case TOK_NAME:
              case TOK_STRING:
                ale = js_IndexAtom(cx, pn3->pn_atom, &cg->atomList);
                if (!ale)
                    return JS_FALSE;
                break;
              default:
                JS_ASSERT(0);
            }

            /* Emit code for the property initializer. */
            if (!js_EmitTree(cx, cg, pn2->pn_right))
                return JS_FALSE;

#if JS_HAS_GETTER_SETTER
            op = pn2->pn_op;
            if (op == JSOP_GETTER || op == JSOP_SETTER) {
                if (pn3->pn_type != TOK_NUMBER &&
                    ALE_INDEX(ale) >= JS_BIT(16)) {
                    ReportStatementTooLarge(cx, cg);
                    return JS_FALSE;
                }
                if (js_Emit1(cx, cg, op) < 0)
                    return JS_FALSE;
            }
#endif
            /* Annotate JSOP_INITELEM so we decompile 2:c and not just c. */
            if (pn3->pn_type == TOK_NUMBER) {
                if (js_NewSrcNote(cx, cg, SRC_INITPROP) < 0)
                    return JS_FALSE;
                if (js_Emit1(cx, cg, JSOP_INITELEM) < 0)
                    return JS_FALSE;
            } else {
                EMIT_ATOM_INDEX_OP(JSOP_INITPROP, ALE_INDEX(ale));
            }
        }

        /* Emit an op for sharpArray cleanup and decompilation. */
        if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0)
            return JS_FALSE;
        break;

#if JS_HAS_SHARP_VARS
      case TOK_DEFSHARP:
        if (!js_EmitTree(cx, cg, pn->pn_kid))
            return JS_FALSE;
        EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid) pn->pn_num);
        break;

      case TOK_USESHARP:
        EMIT_UINT16_IMM_OP(JSOP_USESHARP, (jsatomid) pn->pn_num);
        break;
#endif /* JS_HAS_SHARP_VARS */

      case TOK_RP:
      {
        uintN oldflags;

        /*
         * The node for (e) has e as its kid, enabling users who want to nest
         * assignment expressions in conditions to avoid the error correction
         * done by Condition (from x = y to x == y) by double-parenthesizing.
         */
        oldflags = cg->treeContext.flags;
        cg->treeContext.flags &= ~TCF_IN_FOR_INIT;
        if (!js_EmitTree(cx, cg, pn->pn_kid))
            return JS_FALSE;
        cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT;
        if (js_Emit1(cx, cg, JSOP_GROUP) < 0)
            return JS_FALSE;
        break;
      }

      case TOK_NAME:
        if (!BindNameToSlot(cx, &cg->treeContext, pn, JS_FALSE))
            return JS_FALSE;
        op = pn->pn_op;
        if (op == JSOP_ARGUMENTS) {
            if (js_Emit1(cx, cg, op) < 0)
                return JS_FALSE;
            break;
        }
        if (pn->pn_slot >= 0) {
            atomIndex = (jsatomid) pn->pn_slot;
            EMIT_UINT16_IMM_OP(op, atomIndex);
            break;
        }
        /* FALL THROUGH */

#if JS_HAS_XML_SUPPORT
      case TOK_XMLATTR:
      case TOK_XMLSPACE:
      case TOK_XMLTEXT:
      case TOK_XMLCDATA:
      case TOK_XMLCOMMENT:
#endif
      case TOK_STRING:
      case TOK_OBJECT:
        /*
         * The scanner and parser associate JSOP_NAME with TOK_NAME, although
         * other bytecodes may result instead (JSOP_BINDNAME/JSOP_SETNAME,
         * JSOP_FORNAME, etc.).  Among JSOP_*NAME* variants, only JSOP_NAME
         * may generate the first operand of a call or new expression, so only
         * it sets the "obj" virtual machine register to the object along the
         * scope chain in which the name was found.
         *
         * Token types for STRING and OBJECT have corresponding bytecode ops
         * in pn_op and emit the same format as NAME, so they share this code.
         */
        ok = EmitAtomOp(cx, pn, pn->pn_op, cg);
        break;

      case TOK_NUMBER:
        ok = EmitNumberOp(cx, pn->pn_dval, cg);
        break;

#if JS_HAS_XML_SUPPORT
      case TOK_ANYNAME:
#endif
      case TOK_PRIMARY:
        if (js_Emit1(cx, cg, pn->pn_op) < 0)
            return JS_FALSE;
        break;

#if JS_HAS_DEBUGGER_KEYWORD
      case TOK_DEBUGGER:
        if (js_Emit1(cx, cg, JSOP_DEBUGGER) < 0)
            return JS_FALSE;
        break;
#endif /* JS_HAS_DEBUGGER_KEYWORD */

#if JS_HAS_XML_SUPPORT
      case TOK_XMLELEM:
      case TOK_XMLLIST:
        if (pn->pn_op == JSOP_XMLOBJECT) {
            ok = EmitAtomOp(cx, pn, pn->pn_op, cg);
            break;
        }

        JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0);
        switch (pn->pn_head ? pn->pn_head->pn_type : TOK_XMLLIST) {
          case TOK_XMLETAGO:
            JS_ASSERT(0);
            /* FALL THROUGH */
          case TOK_XMLPTAGC:
          case TOK_XMLSTAGO:
            break;
          default:
            if (js_Emit1(cx, cg, JSOP_STARTXML) < 0)
                return JS_FALSE;
        }

        for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
            if (pn2->pn_type == TOK_LC &&
                js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) {
                return JS_FALSE;
            }
            if (!js_EmitTree(cx, cg, pn2))
                return JS_FALSE;
            if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0)
                return JS_FALSE;
        }

        if (pn->pn_extra & PNX_XMLROOT) {
            if (pn->pn_count == 0) {
                JS_ASSERT(pn->pn_type == TOK_XMLLIST);
                atom = cx->runtime->atomState.emptyAtom;
                ale = js_IndexAtom(cx, atom, &cg->atomList);
                if (!ale)
                    return JS_FALSE;
                EMIT_ATOM_INDEX_OP(JSOP_STRING, ALE_INDEX(ale));
            }
            if (js_Emit1(cx, cg, pn->pn_op) < 0)
                return JS_FALSE;
        }
#ifdef DEBUG
        else
            JS_ASSERT(pn->pn_count != 0);
#endif
        break;

      case TOK_XMLPTAGC:
        if (pn->pn_op == JSOP_XMLOBJECT) {
            ok = EmitAtomOp(cx, pn, pn->pn_op, cg);
            break;
        }
        /* FALL THROUGH */

      case TOK_XMLSTAGO:
      case TOK_XMLETAGO:
      {
        uint32 i;

        if (js_Emit1(cx, cg, JSOP_STARTXML) < 0)
            return JS_FALSE;

        ale = js_IndexAtom(cx,
                           (pn->pn_type == TOK_XMLETAGO)
                           ? cx->runtime->atomState.etagoAtom
                           : cx->runtime->atomState.stagoAtom,
                           &cg->atomList);
        if (!ale)
            return JS_FALSE;
        EMIT_ATOM_INDEX_OP(JSOP_STRING, ALE_INDEX(ale));

        JS_ASSERT(pn->pn_count != 0);
        pn2 = pn->pn_head;
        if (pn2->pn_type == TOK_LC && js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0)
            return JS_FALSE;
        if (!js_EmitTree(cx, cg, pn2))
            return JS_FALSE;
        if (js_Emit1(cx, cg, JSOP_ADD) < 0)
            return JS_FALSE;

        for (pn2 = pn2->pn_next, i = 0; pn2; pn2 = pn2->pn_next, i++) {
            if (pn2->pn_type == TOK_LC &&
                js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) {
                return JS_FALSE;
            }
            if (!js_EmitTree(cx, cg, pn2))
                return JS_FALSE;
            if ((i & 1) && pn2->pn_type == TOK_LC) {
                if (js_Emit1(cx, cg, JSOP_TOATTRVAL) < 0)
                    return JS_FALSE;
            }
            if (js_Emit1(cx, cg,
                         (i & 1) ? JSOP_ADDATTRVAL : JSOP_ADDATTRNAME) < 0) {
                return JS_FALSE;
            }
        }

        ale = js_IndexAtom(cx,
                           (pn->pn_type == TOK_XMLPTAGC)
                           ? cx->runtime->atomState.ptagcAtom
                           : cx->runtime->atomState.tagcAtom,
                           &cg->atomList);
        if (!ale)
            return JS_FALSE;
        EMIT_ATOM_INDEX_OP(JSOP_STRING, ALE_INDEX(ale));
        if (js_Emit1(cx, cg, JSOP_ADD) < 0)
            return JS_FALSE;

        if ((pn->pn_extra & PNX_XMLROOT) && js_Emit1(cx, cg, pn->pn_op) < 0)
            return JS_FALSE;
        break;
      }

      case TOK_XMLNAME:
        if (pn->pn_arity == PN_LIST) {
            JS_ASSERT(pn->pn_count != 0);
            for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
                if (!js_EmitTree(cx, cg, pn2))
                    return JS_FALSE;
                if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0)
                    return JS_FALSE;
            }
        } else {
            JS_ASSERT(pn->pn_arity == PN_NULLARY);
            ok = EmitAtomOp(cx, pn, pn->pn_op, cg);
        }
        break;

      case TOK_XMLPI:
        ale = js_IndexAtom(cx, pn->pn_atom2, &cg->atomList);
        if (!ale)
            return JS_FALSE;
        if (!EmitAtomIndexOp(cx, JSOP_QNAMEPART, ALE_INDEX(ale), cg))
            return JS_FALSE;
        if (!EmitAtomOp(cx, pn, JSOP_XMLPI, cg))
            return JS_FALSE;
        break;
#endif /* JS_HAS_XML_SUPPORT */

Here is the caller graph for this function:

Definition at line 6724 of file jsemit.c.

{
    uintN prologCount, mainCount, totalCount;
    ptrdiff_t offset, delta;
    jssrcnote *sn;

    JS_ASSERT(cg->current == &cg->main);

    prologCount = cg->prolog.noteCount;
    if (prologCount && cg->prolog.currentLine != cg->firstLine) {
        CG_SWITCH_TO_PROLOG(cg);
        if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)cg->firstLine) < 0)
            return JS_FALSE;
        prologCount = cg->prolog.noteCount;
        CG_SWITCH_TO_MAIN(cg);
    } else {
        /*
         * Either no prolog srcnotes, or no line number change over prolog.
         * We don't need a SRC_SETLINE, but we may need to adjust the offset
         * of the first main note, by adding to its delta and possibly even
         * prepending SRC_XDELTA notes to it to account for prolog bytecodes
         * that came at and after the last annotated bytecode.
         */
        offset = CG_PROLOG_OFFSET(cg) - cg->prolog.lastNoteOffset;
        JS_ASSERT(offset >= 0);
        if (offset > 0 && cg->main.noteCount != 0) {
            /* NB: Use as much of the first main note's delta as we can. */
            sn = cg->main.notes;
            delta = SN_IS_XDELTA(sn)
                    ? SN_XDELTA_MASK - (*sn & SN_XDELTA_MASK)
                    : SN_DELTA_MASK - (*sn & SN_DELTA_MASK);
            if (offset < delta)
                delta = offset;
            for (;;) {
                if (!js_AddToSrcNoteDelta(cx, cg, sn, delta))
                    return JS_FALSE;
                offset -= delta;
                if (offset == 0)
                    break;
                delta = JS_MIN(offset, SN_XDELTA_MASK);
                sn = cg->main.notes;
            }
        }
    }

    mainCount = cg->main.noteCount;
    totalCount = prologCount + mainCount;
    if (prologCount)
        memcpy(notes, cg->prolog.notes, SRCNOTE_SIZE(prologCount));
    memcpy(notes + prologCount, cg->main.notes, SRCNOTE_SIZE(mainCount));
    SN_MAKE_TERMINATOR(&notes[totalCount]);

Here is the caller graph for this function:

Definition at line 6841 of file jsemit.c.

Here is the caller graph for this function:

Definition at line 6414 of file jsemit.c.

                                                 {
    {"null",            0,      0,      0},
    {"if",              0,      0,      0},
    {"if-else",         2,      0,      1},
    {"while",           1,      0,      1},
    {"for",             3,      1,      1},
    {"continue",        0,      0,      0},
    {"decl",            1,      1,      1},
    {"pcdelta",         1,      0,      1},
    {"assignop",        0,      0,      0},
    {"cond",            1,      0,      1},
    {"brace",           1,      0,      1},
    {"hidden",          0,      0,      0},
    {"pcbase",          1,      0,     -1},
    {"label",           1,      0,      0},
    {"labelbrace",      1,      0,      0},
    {"endbrace",        0,      0,      0},

Definition at line 1198 of file jsemit.c.

{
    JSStmtInfo *stmt;

    for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
        if (stmt->type == type)
            return JS_TRUE;
    }
    return JS_FALSE;
}
JSBool js_IsGlobalReference ( JSTreeContext tc,
JSAtom atom,
JSBool loopyp 
)

Definition at line 1210 of file jsemit.c.

{
    JSStmtInfo *stmt;
    JSObject *obj;
    JSScope *scope;

    *loopyp = JS_FALSE;
    for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
        if (stmt->type == STMT_WITH)
            return JS_FALSE;
        if (STMT_IS_LOOP(stmt)) {
            *loopyp = JS_TRUE;
            continue;
        }
        if (stmt->flags & SIF_SCOPE) {
            obj = ATOM_TO_OBJECT(stmt->atom);
            JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass);
            scope = OBJ_SCOPE(obj);
            if (SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom)))
                return JS_FALSE;
        }
    }
    return JS_TRUE;
}

Here is the caller graph for this function:

JSStmtInfo* js_LexicalLookup ( JSTreeContext tc,
JSAtom atom,
jsint slotp,
JSBool  letdecl 
)

Definition at line 1513 of file jsemit.c.

{
    JSStmtInfo *stmt;
    JSObject *obj;
    JSScope *scope;
    JSScopeProperty *sprop;
    jsval v;

    for (stmt = tc->topScopeStmt; stmt; stmt = stmt->downScope) {
        if (stmt->type == STMT_WITH) {
            /* Ignore with statements enclosing a single let declaration. */
            if (letdecl)
                continue;
            break;
        }

        /* Skip "maybe scope" statements that don't contain let bindings. */
        if (!(stmt->flags & SIF_SCOPE))
            continue;

        obj = ATOM_TO_OBJECT(stmt->atom);
        JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass);
        scope = OBJ_SCOPE(obj);
        sprop = SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom));
        if (sprop) {
            JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);

            if (slotp) {
                /*
                 * Use LOCKED_OBJ_GET_SLOT since we know obj is single-
                 * threaded and owned by this compiler activation.
                 */
                v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_BLOCK_DEPTH);
                JS_ASSERT(JSVAL_IS_INT(v) && JSVAL_TO_INT(v) >= 0);
                *slotp = JSVAL_TO_INT(v) + sprop->shortid;
            }
            return stmt;
        }
    }

    if (slotp)
        *slotp = -1;
    return stmt;
}

Here is the caller graph for this function:

JSBool js_LookupCompileTimeConstant ( JSContext cx,
JSCodeGenerator cg,
JSAtom atom,
jsval vp 
)

Definition at line 1559 of file jsemit.c.

{
    JSBool ok;
    JSStackFrame *fp;
    JSStmtInfo *stmt;
    jsint slot;
    JSAtomListElement *ale;
    JSObject *obj, *pobj;
    JSProperty *prop;
    uintN attrs;

    /*
     * fp chases cg down the stack, but only until we reach the outermost cg.
     * This enables propagating consts from top-level into switch cases in a
     * function compiled along with the top-level script.  All stack frames
     * with matching code generators should be flagged with JSFRAME_COMPILING;
     * we check sanity here.
     */
    *vp = JSVAL_VOID;
    ok = JS_TRUE;
    fp = cx->fp;
    do {
        JS_ASSERT(fp->flags & JSFRAME_COMPILING);

        obj = fp->varobj;
        if (obj == fp->scopeChain) {
            /* XXX this will need revising when 'let const' is added. */
            stmt = js_LexicalLookup(&cg->treeContext, atom, &slot, JS_FALSE);
            if (stmt)
                return JS_TRUE;

            ATOM_LIST_SEARCH(ale, &cg->constList, atom);
            if (ale) {
                *vp = ALE_VALUE(ale);
                return JS_TRUE;
            }

            /*
             * Try looking in the variable object for a direct property that
             * is readonly and permanent.  We know such a property can't be
             * shadowed by another property on obj's prototype chain, or a
             * with object or catch variable; nor can prop's value be changed,
             * nor can prop be deleted.
             */
            prop = NULL;
            if (OBJ_GET_CLASS(cx, obj) == &js_FunctionClass) {
                ok = js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom),
                                             &pobj, &prop);
                if (!ok)
                    break;
                if (prop) {
#ifdef DEBUG
                    JSScopeProperty *sprop = (JSScopeProperty *)prop;

                    /*
                     * Any hidden property must be a formal arg or local var,
                     * which will shadow a global const of the same name.
                     */
                    JS_ASSERT(sprop->getter == js_GetArgument ||
                              sprop->getter == js_GetLocalVariable);
#endif
                    OBJ_DROP_PROPERTY(cx, pobj, prop);
                    break;
                }
            }

            ok = OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop);
            if (ok) {
                if (pobj == obj &&
                    (fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO))) {
                    /*
                     * We're compiling code that will be executed immediately,
                     * not re-executed against a different scope chain and/or
                     * variable object.  Therefore we can get constant values
                     * from our variable object here.
                     */
                    ok = OBJ_GET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop,
                                            &attrs);
                    if (ok && !(~attrs & (JSPROP_READONLY | JSPROP_PERMANENT)))
                        ok = OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp);
                }
                if (prop)
                    OBJ_DROP_PROPERTY(cx, pobj, prop);
            }
            if (!ok || prop)
                break;
        }
        fp = fp->down;
    } while ((cg = cg->parent) != NULL);
    return ok;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 6473 of file jsemit.c.

{
    intN index, n;
    jssrcnote *sn;
    ptrdiff_t offset, delta, xdelta;

    /*
     * Claim a note slot in CG_NOTES(cg) by growing it if necessary and then
     * incrementing CG_NOTE_COUNT(cg).
     */
    index = AllocSrcNote(cx, cg);
    if (index < 0)
        return -1;
    sn = &CG_NOTES(cg)[index];

    /*
     * Compute delta from the last annotated bytecode's offset.  If it's too
     * big to fit in sn, allocate one or more xdelta notes and reset sn.
     */
    offset = CG_OFFSET(cg);
    delta = offset - CG_LAST_NOTE_OFFSET(cg);
    CG_LAST_NOTE_OFFSET(cg) = offset;
    if (delta >= SN_DELTA_LIMIT) {
        do {
            xdelta = JS_MIN(delta, SN_XDELTA_MASK);
            SN_MAKE_XDELTA(sn, xdelta);
            delta -= xdelta;
            index = AllocSrcNote(cx, cg);
            if (index < 0)
                return -1;
            sn = &CG_NOTES(cg)[index];
        } while (delta >= SN_DELTA_LIMIT);
    }

    /*
     * Initialize type and delta, then allocate the minimum number of notes

Here is the caller graph for this function:

intN js_NewSrcNote2 ( JSContext cx,
JSCodeGenerator cg,
JSSrcNoteType  type,
ptrdiff_t  offset 
)

Definition at line 6521 of file jsemit.c.

                                                           {
        if (js_NewSrcNote(cx, cg, SRC_NULL) < 0)
            return -1;
    }
    return index;
}

intN
js_NewSrcNote2(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type,
               ptrdiff_t offset)

Here is the caller graph for this function:

intN js_NewSrcNote3 ( JSContext cx,
JSCodeGenerator cg,
JSSrcNoteType  type,
ptrdiff_t  offset1,
ptrdiff_t  offset2 
)

Definition at line 6535 of file jsemit.c.

                    {
        if (!js_SetSrcNoteOffset(cx, cg, index, 0, offset))
            return -1;
    }
    return index;
}

intN
js_NewSrcNote3(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type,
               ptrdiff_t offset1, ptrdiff_t offset2)
{
    intN index;

Here is the caller graph for this function:

JSTryNote* js_NewTryNote ( JSContext cx,
JSCodeGenerator cg,
ptrdiff_t  start,
ptrdiff_t  end,
ptrdiff_t  catchStart 
)

Definition at line 6826 of file jsemit.c.

{

Definition at line 1455 of file jsemit.c.

{
    JSStmtInfo *stmt;
    JSObject *blockObj;

    stmt = tc->topStmt;
    tc->topStmt = stmt->down;
    if (STMT_LINKS_SCOPE(stmt)) {
        tc->topScopeStmt = stmt->downScope;
        if (stmt->flags & SIF_SCOPE) {
            blockObj = ATOM_TO_OBJECT(stmt->atom);
            tc->blockChain = JSVAL_TO_OBJECT(blockObj->slots[JSSLOT_PARENT]);
        }
    }
}

Here is the caller graph for this function:

Definition at line 1472 of file jsemit.c.

{
    JSStmtInfo *stmt;

    stmt = cg->treeContext.topStmt;
    if (!STMT_IS_TRYING(stmt) &&
        (!BackPatch(cx, cg, stmt->breaks, CG_NEXT(cg), JSOP_GOTO) ||
         !BackPatch(cx, cg, stmt->continues, CG_CODE(cg, stmt->update),
                    JSOP_GOTO))) {
        return JS_FALSE;
    }
    js_PopStatement(&cg->treeContext);
    return JS_TRUE;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void js_PushBlockScope ( JSTreeContext tc,
JSStmtInfo stmt,
JSAtom blockAtom,
ptrdiff_t  top 
)

Definition at line 1254 of file jsemit.c.

{
    JSObject *blockObj;

    js_PushStatement(tc, stmt, STMT_BLOCK, top);
    stmt->flags |= SIF_SCOPE;
    blockObj = ATOM_TO_OBJECT(blockAtom);
    blockObj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(tc->blockChain);
    stmt->downScope = tc->topScopeStmt;
    tc->topScopeStmt = stmt;
    tc->blockChain = blockObj;
    stmt->atom = blockAtom;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void js_PushStatement ( JSTreeContext tc,
JSStmtInfo stmt,
JSStmtType  type,
ptrdiff_t  top 
)

Definition at line 1236 of file jsemit.c.

{
    stmt->type = type;
    stmt->flags = 0;
    SET_STATEMENT_TOP(stmt, top);
    stmt->atom = NULL;
    stmt->down = tc->topStmt;
    tc->topStmt = stmt;
    if (STMT_LINKS_SCOPE(stmt)) {
        stmt->downScope = tc->topScopeStmt;
        tc->topScopeStmt = stmt;
    } else {
        stmt->downScope = NULL;
    }
}

Here is the caller graph for this function:

Definition at line 1181 of file jsemit.c.

{
    if (!cg->spanDeps) {
        if (JUMP_OFFSET_MIN <= off && off <= JUMP_OFFSET_MAX) {
            SET_JUMP_OFFSET(pc, off);
            return JS_TRUE;
        }

        if (!BuildSpanDepTable(cx, cg))
            return JS_FALSE;
    }

    return SetSpanDepTarget(cx, cg, GetSpanDep(cg, pc), off);
}

Here is the call graph for this function:

Here is the caller graph for this function:

JSBool js_SetSrcNoteOffset ( JSContext cx,
JSCodeGenerator cg,
uintN  index,
uintN  which,
ptrdiff_t  offset 
)

Definition at line 6636 of file jsemit.c.

                                    {
        return (ptrdiff_t)(((uint32)(sn[0] & SN_3BYTE_OFFSET_MASK) << 16)
                           | (sn[1] << 8)
                           | sn[2]);
    }
    return (ptrdiff_t)*sn;
}

JSBool
js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index,
                    uintN which, ptrdiff_t offset)
{
    jssrcnote *sn;
    ptrdiff_t diff;

    if ((jsuword)offset >= (jsuword)((ptrdiff_t)SN_3BYTE_OFFSET_FLAG << 16)) {
        ReportStatementTooLarge(cx, cg);
        return JS_FALSE;
    }

    /* Find the offset numbered which (i.e., skip exactly which offsets). */
    sn = &CG_NOTES(cg)[index];
    JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
    JS_ASSERT(which < js_SrcNoteSpec[SN_TYPE(sn)].arity);
    for (sn++; which; sn++, which--) {
        if (*sn & SN_3BYTE_OFFSET_FLAG)
            sn += 2;
    }

    /* See if the new offset requires three bytes. */
    if (offset > (ptrdiff_t)SN_3BYTE_OFFSET_MASK) {
        /* Maybe this offset was already set to a three-byte value. */
        if (!(*sn & SN_3BYTE_OFFSET_FLAG)) {
            /* Losing, need to insert another two bytes for this offset. */
            index = PTRDIFF(sn, CG_NOTES(cg), jssrcnote);

            /*
             * Simultaneously test to see if the source note array must grow to
             * accomodate either the first or second byte of additional storage
             * required by this 3-byte offset.
             */
            if (((CG_NOTE_COUNT(cg) + 1) & CG_NOTE_MASK(cg)) <= 1) {
                if (!GrowSrcNotes(cx, cg))
                    return JS_FALSE;
                sn = CG_NOTES(cg) + index;
            }
            CG_NOTE_COUNT(cg) += 2;

            diff = CG_NOTE_COUNT(cg) - (index + 3);

Here is the caller graph for this function:


Variable Documentation

Definition at line 335 of file jsemit.h.

Definition at line 335 of file jsemit.h.

Definition at line 335 of file jsemit.h.

Definition at line 335 of file jsemit.h.

Definition at line 335 of file jsemit.h.

Definition at line 335 of file jsemit.h.

Definition at line 670 of file jsemit.h.