📄 jsemit.c
字号:
* This routine tries to optimize name gets and sets to stack slot loads and * stores, given the variables object and scope chain in cx's top frame, the * compile-time context in tc, and a TOK_NAME node pn. It returns false on * error, true on success. * * The caller can inspect pn->pn_slot for a non-negative slot number to tell * whether optimization occurred, in which case LookupArgOrVar also updated * pn->pn_op. If pn->pn_slot is still -1 on return, pn->pn_op nevertheless * may have been optimized, e.g., from JSOP_NAME to JSOP_ARGUMENTS. Whether * or not pn->pn_op was modified, if this function finds an argument or local * variable name, pn->pn_attrs will contain the property's attributes after a * successful return. */static JSBoolLookupArgOrVar(JSContext *cx, JSTreeContext *tc, JSParseNode *pn){ JSObject *obj, *pobj; JSClass *clasp; JSAtom *atom; JSScopeProperty *sprop; JSOp op; JS_ASSERT(pn->pn_type == TOK_NAME); if (pn->pn_slot >= 0 || pn->pn_op == JSOP_ARGUMENTS) return JS_TRUE; /* * We can't optimize if var and closure (a local function not in a larger * expression and not at top-level within another's body) collide. * XXX suboptimal: keep track of colliding names and deoptimize only those */ if (tc->flags & TCF_FUN_CLOSURE_VS_VAR) return JS_TRUE; /* * We can't optimize if we're not compiling a function body, whether via * eval, or directly when compiling a function statement or expression. */ obj = cx->fp->varobj; clasp = OBJ_GET_CLASS(cx, obj); if (clasp != &js_FunctionClass && clasp != &js_CallClass) return JS_TRUE; /* * We can't optimize if we're in an eval called inside a with statement, * or we're compiling a with statement and its body, or we're in a catch * block whose exception variable has the same name as pn. */ atom = pn->pn_atom; if (cx->fp->scopeChain != obj || js_InWithStatement(tc) || js_InCatchBlock(tc, atom)) { return JS_TRUE; } /* * Ok, we may be able to optimize name to stack slot. Look for an argument * or variable property in the function, or its call object, not found in * any prototype object. Rewrite pn_op and update pn accordingly. NB: We * know that JSOP_DELNAME on an argument or variable must evaluate to * false, due to JSPROP_PERMANENT. */ if (!js_LookupProperty(cx, obj, (jsid)atom, &pobj, (JSProperty **)&sprop)) return JS_FALSE; op = pn->pn_op; if (sprop) { if (pobj == obj) { JSPropertyOp getter = sprop->getter; if (getter == js_GetArgument) { switch (op) { case JSOP_NAME: op = JSOP_GETARG; break; case JSOP_SETNAME: op = JSOP_SETARG; break; case JSOP_INCNAME: op = JSOP_INCARG; break; case JSOP_NAMEINC: op = JSOP_ARGINC; break; case JSOP_DECNAME: op = JSOP_DECARG; break; case JSOP_NAMEDEC: op = JSOP_ARGDEC; break; case JSOP_FORNAME: op = JSOP_FORARG; break; case JSOP_DELNAME: op = JSOP_FALSE; break; default: JS_ASSERT(0); } } else if (getter == js_GetLocalVariable || getter == js_GetCallVariable) { switch (op) { case JSOP_NAME: op = JSOP_GETVAR; break; case JSOP_SETNAME: op = JSOP_SETVAR; break; case JSOP_SETCONST: op = JSOP_SETVAR; break; case JSOP_INCNAME: op = JSOP_INCVAR; break; case JSOP_NAMEINC: op = JSOP_VARINC; break; case JSOP_DECNAME: op = JSOP_DECVAR; break; case JSOP_NAMEDEC: op = JSOP_VARDEC; break; case JSOP_FORNAME: op = JSOP_FORVAR; break; case JSOP_DELNAME: op = JSOP_FALSE; break; default: JS_ASSERT(0); } } if (op != pn->pn_op) { pn->pn_op = op; pn->pn_slot = sprop->shortid; } pn->pn_attrs = sprop->attrs; } OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop); } if (pn->pn_slot < 0) { /* * We couldn't optimize it, so it's not an arg or local var name. Now * we must check for the predefined arguments variable. It may be * overridden by assignment, in which case the function is heavyweight * and the interpreter will look up 'arguments' in the function's call * object. */ if (pn->pn_op == JSOP_NAME && atom == cx->runtime->atomState.argumentsAtom) { pn->pn_op = JSOP_ARGUMENTS; return JS_TRUE; } tc->flags |= TCF_FUN_USES_NONLOCALS; } return JS_TRUE;}/* * If pn contains a useful expression, return true with *answer set to true. * If pn contains a useless expression, return true with *answer set to false. * Return false on error. * * The caller should initialize *answer to false and invoke this function on * an expression statement or similar subtree to decide whether the tree could * produce code that has any side effects. For an expression statement, we * define useless code as code with no side effects, because the main effect, * the value left on the stack after the code executes, will be discarded by a * pop bytecode. */static JSBoolCheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, JSBool *answer){ JSBool ok; JSFunction *fun; JSParseNode *pn2; ok = JS_TRUE; if (!pn || *answer) return ok; switch (pn->pn_arity) { case PN_FUNC: /* * A named function is presumed useful: we can't yet know that it is * not called. The side effects are the creation of a scope object * to parent this function object, and the binding of the function's * name in that scope object. See comments at case JSOP_NAMEDFUNOBJ: * in jsinterp.c. */ fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(pn->pn_funAtom)); if (fun->atom) *answer = JS_TRUE; break; case PN_LIST: if (pn->pn_type == TOK_NEW || pn->pn_type == TOK_LP || pn->pn_type == TOK_LB) { /* * All invocation operations (construct: TOK_NEW, call: TOK_LP) * are presumed to be useful, because they may have side effects * even if their main effect (their return value) is discarded. * * TOK_LB binary trees of 3 or more nodes are flattened into lists * to avoid too much recursion. All such lists must be presumed * to be useful because each index operation could invoke a getter * (the JSOP_ARGUMENTS special case below, in the PN_BINARY case, * does not apply here: arguments[i][j] might invoke a getter). */ *answer = JS_TRUE; } else { for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) ok &= CheckSideEffects(cx, tc, pn2, answer); } break; case PN_TERNARY: ok = CheckSideEffects(cx, tc, pn->pn_kid1, answer) && CheckSideEffects(cx, tc, pn->pn_kid2, answer) && CheckSideEffects(cx, tc, pn->pn_kid3, answer); break; case PN_BINARY: if (pn->pn_type == TOK_ASSIGN) { /* * Assignment is presumed to be useful, even if the next operation * is another assignment overwriting this one's ostensible effect, * because the left operand may be a property with a setter that * has side effects. */ *answer = JS_TRUE; } else { if (pn->pn_type == TOK_LB) { pn2 = pn->pn_left; if (pn2->pn_type == TOK_NAME && !LookupArgOrVar(cx, tc, pn2)) return JS_FALSE; if (pn2->pn_op != JSOP_ARGUMENTS) { /* * Any indexed property reference could call a getter with * side effects, except for arguments[i] where arguments is * unambiguous. */ *answer = JS_TRUE; } } ok = CheckSideEffects(cx, tc, pn->pn_left, answer) && CheckSideEffects(cx, tc, pn->pn_right, answer); } break; case PN_UNARY: if (pn->pn_type == TOK_INC || pn->pn_type == TOK_DEC || pn->pn_type == TOK_DELETE || pn->pn_type == TOK_THROW || pn->pn_type == TOK_DEFSHARP) { /* All these operations have effects that we must commit. */ *answer = JS_TRUE; } else { ok = CheckSideEffects(cx, tc, pn->pn_kid, answer); } break; case PN_NAME: if (pn->pn_type == TOK_NAME) { if (!LookupArgOrVar(cx, tc, pn)) return JS_FALSE; if (pn->pn_slot < 0 && pn->pn_op != JSOP_ARGUMENTS) { /* * Not an argument or local variable use, so this expression * could invoke a getter that has side effects. */ *answer = JS_TRUE; } } pn2 = pn->pn_expr; if (pn->pn_type == TOK_DOT && pn2->pn_type == TOK_NAME) { if (!LookupArgOrVar(cx, tc, pn2)) return JS_FALSE; if (!(pn2->pn_op == JSOP_ARGUMENTS && pn->pn_atom == cx->runtime->atomState.lengthAtom)) { /* * Any dotted property reference could call a getter, except * for arguments.length where arguments is unambiguous. */ *answer = JS_TRUE; } } ok = CheckSideEffects(cx, tc, pn2, answer); break; case PN_NULLARY: if (pn->pn_type == TOK_DEBUGGER) *answer = JS_TRUE; break; } return ok;}static JSBoolEmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg){ JSParseNode *pn2, *pndot, *pnup, *pndown; ptrdiff_t top; JSAtomListElement *ale; pn2 = pn->pn_expr; if (op == JSOP_GETPROP && pn->pn_type == TOK_DOT && pn2->pn_type == TOK_NAME) { /* Try to optimize arguments.length into JSOP_ARGCNT. */ if (!LookupArgOrVar(cx, &cg->treeContext, pn2)) return JS_FALSE; if (pn2->pn_op == JSOP_ARGUMENTS && pn->pn_atom == cx->runtime->atomState.lengthAtom) { return js_Emit1(cx, cg, JSOP_ARGCNT) >= 0; } } /* * If the object operand is also a dotted property reference, reverse the * list linked via pn_expr temporarily so we can iterate over it from the * bottom up (reversing again as we go), to avoid excessive recursion. */ if (pn2->pn_type == TOK_DOT) { pndot = pn2; pnup = NULL; top = CG_OFFSET(cg); for (;;) { /* Reverse pndot->pn_expr to point up, not down. */ pndot->pn_offset = top; pndown = pndot->pn_expr; pndot->pn_expr = pnup; if (pndown->pn_type != TOK_DOT) break; pnup = pndot; pndot = pndown; } /* pndown is a primary expression, not a dotted property reference. */ if (!js_EmitTree(cx, cg, pndown)) return JS_FALSE; do { /* Walk back up the list, emitting annotated name ops. */ if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - pndown->pn_offset) < 0) { return JS_FALSE; } ale = js_IndexAtom(cx, pndot->pn_atom, &cg->atomList); if (!ale) return JS_FALSE; EMIT_ATOM_INDEX_OP(pndot->pn_op, ALE_INDEX(ale)); /* Reverse the pn_expr link again. */ pnup = pndot->pn_expr; pndot->pn_expr = pndown; pndown = pndot; } while ((pndot = pnup) != NULL); } else { if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; } if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - pn2->pn_offset) < 0) return JS_FALSE; if (!pn->pn_atom) { JS_ASSERT(op == JSOP_IMPORTALL); if (js_Emit1(cx, cg, op) < 0) return JS_FALSE; } else { 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;}static JSBoolEmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg){ ptrdiff_t top; JSParseNode *left, *right, *next; jsint slot; top = CG_OFFSET(cg); if (pn->pn_arity == PN_LIST) { /* Left-associative operator chain to avoid too much recursion. */ JS_ASSERT(pn->pn_op == JSOP_GETELEM); JS_ASSERT(pn->pn_count >= 3); left = pn->pn_head; right = PN_LAST(pn); next = left->pn_next; JS_ASSERT(next != right); /* * Try to optimize arguments[0][j]... into JSOP_ARGSUB<0> followed by * one or more index expression and JSOP_GETELEM op pairs. */ if (left->pn_type == TOK_NAME && next->pn_type == TOK_NUMBER) { if (!LookupArgOrVar(cx, &cg->treeContext, left)) return JS_FALSE; if (left->pn_op == JSOP_ARGUMENTS && JSDOUBLE_IS_INT(next->pn_dval, slot) && (jsuint)slot < ATOM_INDEX_LIMIT) { left->pn_offset = next->pn_offset = top; EMIT_ATOM_INDEX_OP(JSOP_ARGSUB, (jsatomid)slot);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -