Milán Major

netscape-revival

1 branch
Code

mocha/include/mo_emit.h

#ifndef _mo_emit_h_
#define _mo_emit_h_
/*
** Mocha bytecode generation.
**
** Brendan Eich, 6/21/95
*/
#include <stddef.h>
#include "prmacros.h"
#include "mo_bcode.h"
#include "mo_prvtd.h"
#include "mo_pubtd.h"

NSPR_BEGIN_EXTERN_C

typedef struct LoopInfo LoopInfo;

struct LoopInfo {
    ptrdiff_t           top;            /* offset of loop top from cg base */
    ptrdiff_t           update;         /* loop update offset (top if none) */
    ptrdiff_t           breaks;         /* offset of last break in loop */
    ptrdiff_t           continues;      /* offset of last continue in loop */
    int                 withDepth;      /* with statement depth counter */
    LoopInfo            *down;          /* info for enclosing loop */
};

#define SET_LOOPINFO_TOP(li, top) \
    ((li)->top = (li)->update = (li)->breaks = (li)->continues = (top))

struct CodeGenerator {
    struct PRArenaPool  *pool;          /* pool in which to allocate code */
    MochaCode           *base;          /* base of Mocha bytecode vector */
    MochaCode           *limit;         /* one byte beyond end of bytecode */
    MochaCode           *ptr;           /* pointer to next free bytecode */
    MochaAtom           *atomList;      /* literals indexed for mapping */
    MochaAtomNumber     atomCount;      /* count of indexed literals */
    MochaOp             lastOpcode;     /* last bytecode emited */
    LoopInfo            *loopInfo;      /* LoopInfo stack for break/continue */
    uint32              depthTypeSet;   /* bitset of statement depth types */
    int                 withDepth;      /* with/for-in statement depth count */
    int                 stackDepth;     /* current stack depth in basic block */
    int                 maxStackDepth;  /* maximum stack depth so far */
    SourceNote          *notes;         /* source notes, see below */
    unsigned            noteCount;      /* number of source notes so far */
    ptrdiff_t           lastOffset;     /* pc offset of last source note */
};

#define CG_CODE(cg,offset)      ((cg)->base + (offset))
#define CG_OFFSET(cg)           ((cg)->ptr - (cg)->base)
#define CG_RESET(cg)            ((cg)->ptr = (cg)->base,                      \
				 (cg)->atomList = 0, (cg)->atomCount = 0,     \
                                 (cg)->lastOpcode = 0, (cg)->loopInfo = 0,    \
                                 (cg)->depthTypeSet = 0, (cg)->withDepth = 0, \
                                 (cg)->stackDepth = (cg)->maxStackDepth = 0,  \
                                 CG_RESET_NOTES(cg))
#define CG_RESET_NOTES(cg)      ((cg)->notes = 0, (cg)->noteCount = 0,        \
                                 (cg)->lastOffset = 0)

/*
** Initialize cg to allocate from arena pool ap.  Return true on success.
** Report an exception and return false if the initial code segment can't
** be allocated.
*/
extern MochaBoolean
mocha_InitCodeGenerator(MochaContext *mc, CodeGenerator *cg,
                        struct PRArenaPool *ap);

/*
** Emit one bytecode.
*/
extern ptrdiff_t
mocha_Emit1(MochaContext *mc, CodeGenerator *cg, MochaOp op);

/*
** Emit two bytecodes, an opcode (op) with a byte of immediate operand (op1).
*/
extern ptrdiff_t
mocha_Emit2(MochaContext *mc, CodeGenerator *cg, MochaOp op, MochaCode op1);

/*
** Emit three bytecodes, an opcode with two bytes of immediate operands.
*/
extern ptrdiff_t
mocha_Emit3(MochaContext *mc, CodeGenerator *cg, MochaOp op, MochaCode op1,
            MochaCode op2);

/*
** Update cg's stack depth budget for the opcode located at offset in cg.
*/
extern void
mocha_UpdateDepth(MochaContext *mc, CodeGenerator *cg, ptrdiff_t offset);

/*
** Support for return from within nested with and for-in statements.
** Return MOCHA_FALSE if too much nesting, MOCHA_TRUE otherwise.
*/
extern MochaBoolean
mocha_IsWithStatementDepth(CodeGenerator *cg, int depth);

extern MochaBoolean
mocha_SetStatementDepthType(MochaContext *mc, CodeGenerator *cg,
                            MochaBoolean isWith);

/*
** Copy code from code generator from starting at fromOffset and running to
** CG_OFFSET(from) into code generator to, starting at toOffset.
**
** NB: this function does not move source notes; the caller must do that.
*/
extern ptrdiff_t
mocha_MoveCode(MochaContext *mc, CodeGenerator *from, ptrdiff_t fromOffset,
               CodeGenerator *to, ptrdiff_t toOffset);

/*
** Push the stack-allocated struct at li onto the cg->loopInfo stack.
*/
extern void
mocha_PushLoopInfo(CodeGenerator *cg, LoopInfo *li, ptrdiff_t top);

/*
** Emit a break instruction, recording it for backpatching.
*/
extern ptrdiff_t
mocha_EmitBreak(MochaContext *mc, CodeGenerator *cg);

/*
** Emit a continue instruction, recording it for backpatching.
*/
extern ptrdiff_t
mocha_EmitContinue(MochaContext *mc, CodeGenerator *cg);

/*
** Pop cg->loopInfo.  If the top LoopInfo struct is not stack-allocated, it
** is up to the caller to free it.
*/
extern void
mocha_PopLoopInfo(CodeGenerator *cg);

/*
** Source notes generated along with bytecode for decompiling and debugging.
** A source note is a uint16 with 4 bits of type and 12 of offset.
**
** NB: order matters -- note types > SRC_SETLINE are "gettable", but the line
**     number types and null are not.
*/
typedef enum SourceNoteType {
    SRC_NULL     = 0,           /* if delta == 0, terminate SourceNote list */
    SRC_NEWLINE  = 1,           /* bytecode follows a source newline */
    SRC_SETLINE  = 2,           /* a file-absolute source line number note */
    SRC_IF       = 3,           /* MOP_IFEQ bytecode is from an if-then */
    SRC_IF_ELSE  = 4,           /* MOP_IFEQ bytecode is from an if-then-else */
    SRC_WHILE    = 5,           /* MOP_IFEQ is from a while loop */
    SRC_FOR      = 6,           /* MOP_NOP or MOP_POP in for loop head */
    SRC_CONTINUE = 7,           /* MOP_GOTO is a continue, not a break */
    SRC_VAR      = 8,           /* MOP_NAME of a local variable declaration */
    SRC_COMMA    = 9,           /* MOP_POP representing a comma operator */
    SRC_ASSIGNOP = 10,          /* += or another assign-op follows */
    SRC_COND     = 11,          /* MOP_IFEQ is from conditional (?:) operator */
    SRC_PAREN    = 12,          /* MOP_NOP generated to mark user parentheses */
    SRC_HIDDEN   = 13           /* MOP_LEAVE for break/cont/return in a with */
} SourceNoteType;

#define SN_TYPE_BITS            4
#define SN_DELTA_BITS           12
#define SN_TYPE_MASK            (PR_BITMASK(SN_TYPE_BITS) << SN_DELTA_BITS)
#define SN_DELTA_MASK           (PR_BITMASK(SN_DELTA_BITS))

#define SN_TYPE(sn)             (*(sn) >> SN_DELTA_BITS)
#define SN_SET_TYPE(sn,_type)   (*(sn) = (SourceNote)                         \
                                         (((_type) << SN_DELTA_BITS)          \
                                          | (*(sn) & SN_DELTA_MASK)))
#define SN_DELTA(sn)            (*(sn) & SN_DELTA_MASK)
#define SN_SET_DELTA(sn,_delta) (*(sn) = (SourceNote)                         \
                                         ((*(sn) & SN_TYPE_MASK)              \
                                          | ((_delta) & SN_DELTA_MASK)))
#define SN_OFFSET(sn)           (*(sn))
#define SN_SET_OFFSET(sn,_off)  (*(sn) = (SourceNote)(_off))

#define SN_DELTA_MAX            (1 << SN_DELTA_BITS)

/* A for loop has 3 extra notes for cond, next, and tail offsets. */
#define SN_LENGTH(sn)           ((SN_TYPE(sn) == SRC_SETLINE) ? 2 :           \
                                    ((SN_TYPE(sn) == SRC_FOR) ? 4 : 1))
#define SN_NEXT(sn)             ((sn) + SN_LENGTH(sn))

/* A source note array is terminated by an all-zero element. */
#define SN_MAKE_TERMINATOR(sn)  (*(sn) = 0)
#define SN_IS_TERMINATOR(sn)    (*(sn) == 0)

/*
** Append a new source note of the given type (and therefore size) to cg's
** notes dynamic array, updating cg->noteCount.  Return the new note's index
** within the array pointed at by cg->notes.  Return -1 if out of memory.
*/
extern int
mocha_NewSourceNote(MochaContext *mc, CodeGenerator *cg, SourceNoteType type);

/*
** Copy source notes from one code generator to another.  The code annotated
** by the copied notes must be moved *after* the notes are moved.
*/
extern MochaBoolean
mocha_MoveSourceNotes(MochaContext *mc, CodeGenerator *from, CodeGenerator *to);

/*
** Finish taking source notes in mc's tempPool by copying them to new stable
** store allocated from codePool.
*/
extern SourceNote *
mocha_FinishTakingSourceNotes(MochaContext *mc, CodeGenerator *cg);

/*
** Get a non-line-type source note for pc.  Return 0 if pc has no such note.
*/
extern SourceNote *
mocha_GetSourceNote(MochaScript *script, MochaCode *pc);

/*
** Return the number of the line from which pc's bytecode was generated.
*/
extern unsigned
mocha_PCtoLineNumber(MochaScript *script, MochaCode *pc);

/*
** Return a structure pointing to a safe copy of code generated in cg, to the
** filename string, and to source notes for debugging/decompilation.  Return
** null on allocation failure, which this function reports.
*/
extern MochaScript *
mocha_NewScript(MochaContext *mc, CodeGenerator *cg, const char *filename,
                unsigned lineno);

/*
** Destroy all dynamically allocated storage held by script.
*/
extern void
mocha_DestroyScript(MochaContext *mc, MochaScript *script);

NSPR_END_EXTERN_C

#endif /* _mo_emit_h_ */