📄 jsemit.c
字号:
/* 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;}JSBooljs_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, jsval *vp){ 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;}/* * Allocate an index invariant for all activations of the code being compiled * in cg, that can be used to store and fetch a reference to a cloned RegExp * object that shares the same JSRegExp private data created for the object * literal in pn->pn_atom. We need clones to hold lastIndex and other direct * properties that should not be shared among threads sharing a precompiled * function or script. * * If the code being compiled is function code, allocate a reserved slot in * the cloned function object that shares its precompiled script with other * cloned function objects and with the compiler-created clone-parent. There * are fun->nregexps such reserved slots in each function object cloned from * fun->object. NB: during compilation, funobj slots must never be allocated, * because js_AllocSlot could hand out one of the slots that should be given * to a regexp clone. * * If the code being compiled is global code, reserve the fp->vars slot at * ALE_INDEX(ale), by ensuring that cg->treeContext.numGlobalVars is at least * one more than this index. For global code, fp->vars is parallel to the * global script->atomMap.vector array, but possibly shorter for the common * case (where var declarations and regexp literals cluster toward the front * of the script or function body). * * Global variable name literals in script->atomMap have fast-global slot * numbers (stored as int-tagged jsvals) in the corresponding fp->vars array * element. The atomIndex for a regexp object literal thus also addresses an * fp->vars element that is not used by any optimized global variable, so we * use that GC-scanned element to keep the regexp object clone alive, as well * as to lazily create and find it at run-time for the JSOP_REGEXP bytecode. * * In no case can cx->fp->varobj be a Call object here, because that implies * we are compiling eval code, in which case (cx->fp->flags & JSFRAME_EVAL) * is true, and js_GetToken will have already selected JSOP_OBJECT instead of * JSOP_REGEXP, to avoid all this RegExp object cloning business. * * Why clone regexp objects? ECMA specifies that when a regular expression * literal is scanned, a RegExp object is created. In the spec, compilation * and execution happen indivisibly, but in this implementation and many of * its embeddings, code is precompiled early and re-executed in multiple * threads, or using multiple global objects, or both, for efficiency. * * In such cases, naively following ECMA leads to wrongful sharing of RegExp * objects, which makes for collisions on the lastIndex property (especially * for global regexps) and on any ad-hoc properties. Also, __proto__ and * __parent__ refer to the pre-compilation prototype and global objects, a * pigeon-hole problem for instanceof tests. */static JSBoolIndexRegExpClone(JSContext *cx, JSParseNode *pn, JSAtomListElement *ale, JSCodeGenerator *cg){ JSObject *varobj, *reobj; JSClass *clasp; JSFunction *fun; JSRegExp *re; uint16 *countPtr; uintN cloneIndex; JS_ASSERT(!(cx->fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO))); varobj = cx->fp->varobj; clasp = OBJ_GET_CLASS(cx, varobj); if (clasp == &js_FunctionClass) { fun = (JSFunction *) JS_GetPrivate(cx, varobj); countPtr = &fun->u.i.nregexps; cloneIndex = *countPtr; } else { JS_ASSERT(clasp != &js_CallClass); countPtr = &cg->treeContext.numGlobalVars; cloneIndex = ALE_INDEX(ale); } if ((cloneIndex + 1) >> 16) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEED_DIET, js_script_str); return JS_FALSE; } if (cloneIndex >= *countPtr) *countPtr = cloneIndex + 1; reobj = ATOM_TO_OBJECT(pn->pn_atom); JS_ASSERT(OBJ_GET_CLASS(cx, reobj) == &js_RegExpClass); re = (JSRegExp *) JS_GetPrivate(cx, reobj); re->cloneIndex = cloneIndex; return JS_TRUE;}/* * Emit a bytecode and its 2-byte constant (atom) index immediate operand. * If the atomIndex requires more than 2 bytes, emit a prefix op whose 24-bit * immediate operand indexes the atom in script->atomMap. * * If op has JOF_NAME mode, emit JSOP_FINDNAME to find and push the object in * the scope chain in which the literal name was found, followed by the name * as a string. This enables us to use the JOF_ELEM counterpart to op. * * Otherwise, if op has JOF_PROP mode, emit JSOP_LITERAL before op, to push * the atom's value key. For JOF_PROP ops, the object being operated on has * already been pushed, and JSOP_LITERAL will push the id, leaving the stack * in the proper state for a JOF_ELEM counterpart. * * Otherwise, emit JSOP_LITOPX to push the atom index, then perform a special * dispatch on op, but getting op's atom index from the stack instead of from * an unsigned 16-bit immediate operand. */static JSBoolEmitAtomIndexOp(JSContext *cx, JSOp op, jsatomid atomIndex, JSCodeGenerator *cg){ uint32 mode; JSOp prefixOp; ptrdiff_t off; jsbytecode *pc; if (atomIndex >= JS_BIT(16)) { mode = (js_CodeSpec[op].format & JOF_MODEMASK); if (op != JSOP_SETNAME) { prefixOp = ((mode != JOF_NAME && mode != JOF_PROP) ||#if JS_HAS_XML_SUPPORT op == JSOP_GETMETHOD || op == JSOP_SETMETHOD ||#endif op == JSOP_SETCONST) ? JSOP_LITOPX : (mode == JOF_NAME) ? JSOP_FINDNAME : JSOP_LITERAL; off = js_EmitN(cx, cg, prefixOp, 3); if (off < 0) return JS_FALSE; pc = CG_CODE(cg, off); SET_LITERAL_INDEX(pc, atomIndex); } switch (op) { case JSOP_DECNAME: op = JSOP_DECELEM; break; case JSOP_DECPROP: op = JSOP_DECELEM; break; case JSOP_DELNAME: op = JSOP_DELELEM; break; case JSOP_DELPROP: op = JSOP_DELELEM; break; case JSOP_FORNAME: op = JSOP_FORELEM; break; case JSOP_FORPROP: op = JSOP_FORELEM; break; case JSOP_GETPROP: op = JSOP_GETELEM; break; case JSOP_GETXPROP: op = JSOP_GETXELEM; break; case JSOP_IMPORTPROP: op = JSOP_IMPORTELEM; break; case JSOP_INCNAME: op = JSOP_INCELEM; break; case JSOP_INCPROP: op = JSOP_INCELEM; break; case JSOP_INITPROP: op = JSOP_INITELEM; break; case JSOP_NAME: op = JSOP_GETELEM; break; case JSOP_NAMEDEC: op = JSOP_ELEMDEC; break; case JSOP_NAMEINC: op = JSOP_ELEMINC; break; case JSOP_PROPDEC: op = JSOP_ELEMDEC; break; case JSOP_PROPINC: op = JSOP_ELEMINC; break; case JSOP_BINDNAME: return JS_TRUE; case JSOP_SETNAME: op = JSOP_SETELEM; break; case JSOP_SETPROP: op = JSOP_SETELEM; break;#if JS_HAS_EXPORT_IMPORT case JSOP_EXPORTNAME: ReportStatementTooLarge(cx, cg); return JS_FALSE;#endif default:#if JS_HAS_XML_SUPPORT JS_ASSERT(mode == 0 || op == JSOP_SETCONST || op == JSOP_GETMETHOD || op == JSOP_SETMETHOD);#else JS_ASSERT(mode == 0 || op == JSOP_SETCONST);#endif break; } return js_Emit1(cx, cg, op) >= 0; } EMIT_UINT16_IMM_OP(op, atomIndex); return JS_TRUE;}/* * Slight sugar for EmitAtomIndexOp, again accessing cx and cg from the macro * caller's lexical environment, and embedding a false return on error. * XXXbe hey, who checks for fun->nvars and fun->nargs overflow?! */#define EMIT_ATOM_INDEX_OP(op, atomIndex) \ JS_BEGIN_MACRO \ if (!EmitAtomIndexOp(cx, op, atomIndex, cg)) \ 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; if (op == JSOP_REGEXP && !IndexRegExpClone(cx, pn, ale, cg)) return JS_FALSE; return EmitAtomIndexOp(cx, op, ALE_INDEX(ale), cg);}/* * 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 BindNameToSlot 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. * * NB: if you add more opcodes specialized from JSOP_NAME, etc., don't forget * to update the TOK_FOR (for-in) and TOK_ASSIGN (op=, e.g. +=) special cases * in js_EmitTree. */static JSBoolBindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, JSBool letdecl){ JSAtom *atom; JSStmtInfo *stmt; jsint slot; JSOp op; JSStackFrame *fp; JSObject *obj, *pobj; JSClass *clasp; JSBool optimizeGlobals; JSPropertyOp getter; uintN attrs; JSAtomListElement *ale; JSProperty *prop; JSScopeProperty *sprop; JS_ASSERT(pn->pn_type == TOK_NAME); if (pn->pn_slot >= 0 || pn->pn_op == JSOP_ARGUMENTS) return JS_TRUE; /* QNAME references can never be optimized to use arg/var storage. */ if (pn->pn_op == JSOP_QNAMEPART) return JS_TRUE; /* * We can't optimize if we are compiling a with statement and its body, * or we're in a catch block whose exception variable has the same name
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -