📄 jsemit.c
字号:
if (!AddSpanDep(cx, cg, pc, pc, off)) return JS_FALSE; } return jmp;}static ptrdiff_tGetJumpOffset(JSCodeGenerator *cg, jsbytecode *pc){ JSSpanDep *sd; JSJumpTarget *jt; ptrdiff_t top; if (!cg->spanDeps) return GET_JUMP_OFFSET(pc); sd = GetSpanDep(cg, pc); jt = sd->target; if (!JT_HAS_TAG(jt)) return JT_TO_BPDELTA(jt); top = sd->top; while (--sd >= cg->spanDeps && sd->top == top) continue; sd++; return JT_CLR_TAG(jt)->offset - sd->offset;}JSBooljs_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, ptrdiff_t off){ 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);}JSBooljs_InWithStatement(JSTreeContext *tc){ JSStmtInfo *stmt; for (stmt = tc->topStmt; stmt; stmt = stmt->down) { if (stmt->type == STMT_WITH) return JS_TRUE; } return JS_FALSE;}JSBooljs_InCatchBlock(JSTreeContext *tc, JSAtom *atom){ JSStmtInfo *stmt; for (stmt = tc->topStmt; stmt; stmt = stmt->down) { if (stmt->type == STMT_CATCH && stmt->label == atom) return JS_TRUE; } return JS_FALSE;}voidjs_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type, ptrdiff_t top){ stmt->type = type; SET_STATEMENT_TOP(stmt, top); stmt->label = NULL; stmt->down = tc->topStmt; tc->topStmt = stmt;}/* * Emit a backpatch op with offset pointing to the previous jump of this type, * so that we can walk back up the chain fixing up the op and jump offset. */#define EMIT_BACKPATCH_OP(cx, cg, last, op, jmp) \ JS_BEGIN_MACRO \ ptrdiff_t offset, delta; \ offset = CG_OFFSET(cg); \ delta = offset - (last); \ last = offset; \ JS_ASSERT(delta > 0); \ jmp = EmitJump((cx), (cg), (op), (delta)); \ JS_END_MACRO/* Emit additional bytecode(s) for non-local jumps. */static JSBoolEmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, JSOp *returnop){ intN depth; JSStmtInfo *stmt; ptrdiff_t jmp; /* * Return from within a try block that has a finally clause must be split * into two ops: JSOP_SETRVAL, to pop the r.v. and store it in fp->rval; * and JSOP_RETRVAL, which makes control flow go back to the caller, who * picks up fp->rval as usual. Otherwise, the stack will be unbalanced * when executing the finally clause. * * We mutate *returnop once only if we find an enclosing try-block (viz, * STMT_FINALLY) to ensure that we emit just one JSOP_SETRVAL before one * or more JSOP_GOSUBs and other fixup opcodes emitted by this function. * Our caller (the TOK_RETURN case of js_EmitTree) then emits *returnop. * The fixup opcodes and gosubs must interleave in the proper order, from * inner statement to outer, so that finally clauses run at the correct * stack depth. */ if (returnop) { JS_ASSERT(*returnop == JSOP_RETURN); for (stmt = cg->treeContext.topStmt; stmt != toStmt; stmt = stmt->down) { if (stmt->type == STMT_FINALLY) { if (js_Emit1(cx, cg, JSOP_SETRVAL) < 0) return JS_FALSE; *returnop = JSOP_RETRVAL; break; } } /* * If there are no try-with-finally blocks open around this return * statement, we can generate a return forthwith and skip generating * any fixup code. */ if (*returnop == JSOP_RETURN) return JS_TRUE; } /* * The non-local jump fixup we emit will unbalance cg->stackDepth, because * the fixup replicates balanced code such as JSOP_LEAVEWITH emitted at the * end of a with statement, so we save cg->stackDepth here and restore it * just before a successful return. */ depth = cg->stackDepth; for (stmt = cg->treeContext.topStmt; stmt != toStmt; stmt = stmt->down) { switch (stmt->type) { case STMT_FINALLY: if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) return JS_FALSE; EMIT_BACKPATCH_OP(cx, cg, stmt->gosub, JSOP_BACKPATCH_PUSH, jmp); if (jmp < 0) return JS_FALSE; break; case STMT_WITH: case STMT_CATCH: /* There's a With object on the stack that we need to pop. */ if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) return JS_FALSE; if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) return JS_FALSE; break; case STMT_FOR_IN_LOOP: /* * The iterator and the object being iterated need to be popped. * JSOP_POP2 isn't decompiled, so it doesn't need to be HIDDEN. */ if (js_Emit1(cx, cg, JSOP_POP2) < 0) return JS_FALSE; break; case STMT_SUBROUTINE: /* There's a retsub pc-offset on the stack that we need to pop. */ if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) return JS_FALSE; if (js_Emit1(cx, cg, JSOP_POP) < 0) return JS_FALSE; break; default:; } } cg->stackDepth = depth; return JS_TRUE;}static ptrdiff_tEmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, ptrdiff_t *last, JSAtomListElement *label, JSSrcNoteType noteType){ intN index; ptrdiff_t jmp; if (!EmitNonLocalJumpFixup(cx, cg, toStmt, NULL)) return -1; if (label) { index = js_NewSrcNote(cx, cg, noteType); if (index < 0) return -1; if (!js_SetSrcNoteOffset(cx, cg, (uintN)index, 0, (ptrdiff_t) ALE_INDEX(label))) { return -1; } } else if (noteType != SRC_NULL) { if (js_NewSrcNote(cx, cg, noteType) < 0) return -1; } EMIT_BACKPATCH_OP(cx, cg, *last, JSOP_BACKPATCH, jmp); return jmp;}static JSBoolBackPatch(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t last, jsbytecode *target, jsbytecode op){ jsbytecode *pc, *stop; ptrdiff_t delta, span; pc = CG_CODE(cg, last); stop = CG_CODE(cg, -1); while (pc != stop) { delta = GetJumpOffset(cg, pc); span = PTRDIFF(target, pc, jsbytecode); CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, span); /* * Set *pc after jump offset in case bpdelta didn't overflow, but span * does (if so, CHECK_AND_SET_JUMP_OFFSET might call BuildSpanDepTable * and need to see the JSOP_BACKPATCH* op at *pc). */ *pc = op; pc -= delta; } return JS_TRUE;}voidjs_PopStatement(JSTreeContext *tc){ tc->topStmt = tc->topStmt->down;}JSBooljs_PopStatementCG(JSContext *cx, JSCodeGenerator *cg){ JSStmtInfo *stmt; stmt = cg->treeContext.topStmt; if (!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;}JSBooljs_DefineCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, JSParseNode *pn){ 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;}JSBooljs_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, jsval *vp){ JSBool ok; JSStackFrame *fp; 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 && !js_InWithStatement(&cg->treeContext) && !js_InCatchBlock(&cg->treeContext, atom)) { 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; ok = OBJ_LOOKUP_PROPERTY(cx, obj, (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, (jsid)atom, prop, &attrs); if (ok && !(~attrs & (JSPROP_READONLY | JSPROP_PERMANENT))) ok = OBJ_GET_PROPERTY(cx, obj, (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;}/* * Emit a bytecode and its 2-byte constant (atom) index immediate operand. * NB: We use cx and cg from our caller's lexical environment, and return * false on error. */#define EMIT_ATOM_INDEX_OP(op, atomIndex) \ JS_BEGIN_MACRO \ if (js_Emit3(cx, cg, op, ATOM_INDEX_HI(atomIndex), \ ATOM_INDEX_LO(atomIndex)) < 0) { \ return JS_FALSE; \ } \ JS_END_MACROstatic JSBoolEmitAtomOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg){ JSAtomListElement *ale; ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList); if (!ale) return JS_FALSE; EMIT_ATOM_INDEX_OP(op, ALE_INDEX(ale)); return JS_TRUE;}/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -