📄 jsinterp.c
字号:
/* Extend the last stack segment, give back the 2 header slots. */ sh->nslots += nslots; a->avail -= 2 * sizeof(jsval); } else { /* * Need a new stack segment, so we must initialize unused slots in the * current frame. See js_GC, just before marking the "operand" jsvals, * where we scan from fp->spbase to fp->sp or through fp->script->depth * (whichever covers fewer slots). */ fp = cx->fp; if (fp && fp->script && fp->spbase) {#ifdef DEBUG jsuword depthdiff = fp->script->depth * sizeof(jsval); JS_ASSERT(JS_UPTRDIFF(fp->sp, fp->spbase) <= depthdiff); JS_ASSERT(JS_UPTRDIFF(*markp, fp->spbase) >= depthdiff);#endif end = fp->spbase + fp->script->depth; for (vp = fp->sp; vp < end; vp++) *vp = JSVAL_VOID; } /* Allocate and push a stack segment header from the 2 extra slots. */ sh = (JSStackHeader *)sp; sh->nslots = nslots; sh->down = cx->stackHeaders; cx->stackHeaders = sh; sp += 2; } return sp;}JS_FRIEND_API(void)js_FreeStack(JSContext *cx, void *mark){ JSStackHeader *sh; jsuword slotdiff; /* Check for zero nslots allocation special case. */ if (!mark) return; /* We can assert because js_FreeStack always balances js_AllocStack. */ sh = cx->stackHeaders; JS_ASSERT(sh); /* If mark is in the current segment, reduce sh->nslots, else pop sh. */ slotdiff = JS_UPTRDIFF(mark, JS_STACK_SEGMENT(sh)) / sizeof(jsval); if (slotdiff < (jsuword)sh->nslots) sh->nslots = slotdiff; else cx->stackHeaders = sh->down; /* Release the stackPool space allocated since mark was set. */ JS_ARENA_RELEASE(&cx->stackPool, mark);}/* * To economize on slots space in functions, the compiler records arguments and * local variables as shared (JSPROP_SHARED) properties with well-known getters * and setters: js_{Get,Set}Argument, js_{Get,Set}LocalVariable. Now, we could * record args and vars in lists or hash tables in function-private data, but * that means more duplication in code, and more data at runtime in the hash * table case due to round-up to powers of two, just to recapitulate the scope * machinery in the function object. * * What's more, for a long time (to the dawn of "Mocha" in 1995), these getters * and setters knew how to search active stack frames in a context to find the * top activation of the function f, in order to satisfy a get or set of f.a, * for argument a, or f.x, for local variable x. You could use f.a instead of * just a in function f(a) { return f.a }, for example, to return the actual * parameter. * * ECMA requires that we give up on this ancient extension, because it is not * compatible with the standard as used by real-world scripts. While Chapter * 16 does allow for additional properties to be defined on native objects by * a conforming implementation, these magic getters and setters cause f.a's * meaning to vary unexpectedly. Real-world scripts set f.A = 42 to define * "class static" (after Java) constants, for example, but if A also names an * arg or var in f, the constant is not available while f is active, and any * non-constant class-static can't be set while f is active. * * So, to label arg and var properties in functions without giving them magic * abilities to affect active frame stack slots, while keeping the properties * shared (slot-less) to save space in the common case (where no assignment * sets a function property with the same name as an arg or var), the setters * for args and vars must handle two special cases here. * * XXX functions tend to have few args and vars, so we risk O(n^2) growth here * XXX ECMA *really* wants args and vars to be stored in function-private data, * not as function object properties. */static JSBoolSetFunctionSlot(JSContext *cx, JSObject *obj, JSPropertyOp setter, jsid id, jsval v){ uintN slot; JSObject *origobj; JSScope *scope; JSScopeProperty *sprop; JSString *str; JSBool ok; slot = (uintN) JSVAL_TO_INT(id); if (OBJ_GET_CLASS(cx, obj) != &js_FunctionClass) { /* * Given a non-function object obj that has a function object in its * prototype chain, where an argument or local variable property named * by (setter, slot) is being set, override the shared property in the * prototype with an unshared property in obj. This situation arises * in real-world JS due to .prototype setting and collisions among a * function's "static property" names and arg or var names, believe it * or not. */ origobj = obj; do { obj = OBJ_GET_PROTO(cx, obj); if (!obj) return JS_TRUE; } while (OBJ_GET_CLASS(cx, obj) != &js_FunctionClass); JS_LOCK_OBJ(cx, obj); scope = OBJ_SCOPE(obj); for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { if (sprop->setter == setter) { JS_ASSERT(!JSVAL_IS_INT(sprop->id) && ATOM_IS_STRING((JSAtom *)sprop->id) && (sprop->flags & SPROP_HAS_SHORTID)); if ((uintN) sprop->shortid == slot) { str = ATOM_TO_STRING((JSAtom *)sprop->id); JS_UNLOCK_SCOPE(cx, scope); return JS_DefineUCProperty(cx, origobj, JSSTRING_CHARS(str), JSSTRING_LENGTH(str), v, NULL, NULL, JSPROP_ENUMERATE); } } } JS_UNLOCK_SCOPE(cx, scope); return JS_TRUE; } /* * Argument and local variable properties of function objects are shared * by default (JSPROP_SHARED), therefore slot-less. But if for function * f(a) {}, f.a = 42 is evaluated, f.a should be 42 after the assignment, * whether or not f is active. So js_SetArgument and js_SetLocalVariable * must be prepared to change an arg or var from shared to unshared status, * allocating a slot in obj to hold v. */ ok = JS_TRUE; JS_LOCK_OBJ(cx, obj); scope = OBJ_SCOPE(obj); for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { if (sprop->setter == setter && (uintN) sprop->shortid == slot) { if (sprop->attrs & JSPROP_SHARED) { sprop = js_ChangeScopePropertyAttrs(cx, scope, sprop, 0, ~JSPROP_SHARED, sprop->getter, setter); if (!sprop) { ok = JS_FALSE; } else { /* See js_SetProperty, near the bottom. */ GC_POKE(cx, pval); LOCKED_OBJ_SET_SLOT(obj, sprop->slot, v); } } break; } } JS_UNLOCK_SCOPE(cx, scope); return ok;}JSBooljs_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp){ return JS_TRUE;}JSBooljs_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp){ return SetFunctionSlot(cx, obj, js_SetArgument, id, *vp);}JSBooljs_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp){ return JS_TRUE;}JSBooljs_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp){ return SetFunctionSlot(cx, obj, js_SetLocalVariable, id, *vp);}/* * Compute the 'this' parameter and store it in frame as frame.thisp. * Activation objects ("Call" objects not created with "new Call()", i.e., * "Call" objects that have private data) may not be referred to by 'this', * as dictated by ECMA. * * N.B.: fp->argv must be set, fp->argv[-1] the nominal 'this' paramter as * a jsval, and fp->argv[-2] must be the callee object reference, usually a * function object. Also, fp->flags must contain JSFRAME_CONSTRUCTING if we * are preparing for a constructor call. */static JSBoolComputeThis(JSContext *cx, JSObject *thisp, JSStackFrame *fp){ JSObject *parent; if (thisp && OBJ_GET_CLASS(cx, thisp) != &js_CallClass) { /* Some objects (e.g., With) delegate 'this' to another object. */ thisp = OBJ_THIS_OBJECT(cx, thisp); if (!thisp) return JS_FALSE; /* Default return value for a constructor is the new object. */ if (fp->flags & JSFRAME_CONSTRUCTING) fp->rval = OBJECT_TO_JSVAL(thisp); } else { /* * ECMA requires "the global object", but in the presence of multiple * top-level objects (windows, frames, or certain layers in the client * object model), we prefer fun's parent. An example that causes this * code to run: * * // in window w1 * function f() { return this } * function g() { return f } * * // in window w2 * var h = w1.g() * alert(h() == w1) * * The alert should display "true". */ JS_ASSERT(!(fp->flags & JSFRAME_CONSTRUCTING)); if (JSVAL_IS_PRIMITIVE(fp->argv[-2]) || !(parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(fp->argv[-2])))) { thisp = cx->globalObject; } else { /* walk up to find the top-level object */ thisp = parent; while ((parent = OBJ_GET_PARENT(cx, thisp)) != NULL) thisp = parent; } } fp->thisp = thisp; fp->argv[-1] = OBJECT_TO_JSVAL(thisp); return JS_TRUE;}extern JSBoolswfdec_script_interpret(void *script, JSContext *cx, jsval *rval);/* * Find a function reference and its 'this' object implicit first parameter * under argc arguments on cx's stack, and call the function. Push missing * required arguments, allocate declared local variables, and pop everything * when done. Then push the return value. */JS_FRIEND_API(JSBool)js_Invoke(JSContext *cx, uintN argc, uintN flags){ void *mark; JSStackFrame *fp, frame; jsval *sp, *newsp, *limit; jsval *vp, v; JSObject *funobj, *parent, *thisp; JSBool ok; JSClass *clasp; JSObjectOps *ops; JSNative native; JSFunction *fun; JSScript *script; void *swf; uintN minargs, nvars; intN nslots, nalloc, surplus; JSInterpreterHook hook; void *hookData; /* Mark the top of stack and load frequently-used registers. */ mark = JS_ARENA_MARK(&cx->stackPool); fp = cx->fp; sp = fp->sp; /* * Set vp to the callee value's stack slot (it's where rval goes). * Once vp is set, control should flow through label out2: to return. * Set frame.rval early so native class and object ops can throw and * return false, causing a goto out2 with ok set to false. Also set * frame.flags to flags so that ComputeThis can test bits in it. */ vp = sp - (2 + argc); v = *vp; frame.rval = JSVAL_VOID; frame.flags = flags; thisp = JSVAL_TO_OBJECT(vp[1]); /* * A callee must be an object reference, unless its |this| parameter * implements the __noSuchMethod__ method, in which case that method will * be called like so: * * thisp.__noSuchMethod__(id, args) * * where id is the name of the method that this invocation attempted to * call by name, and args is an Array containing this invocation's actual * parameters. */ if (JSVAL_IS_PRIMITIVE(v)) {#if JS_HAS_NO_SUCH_METHOD jsbytecode *pc; jsatomid atomIndex; JSAtom *atom; JSObject *argsobj; JSArena *a; if (!fp->script || (flags & JSINVOKE_INTERNAL)) goto bad; /* * We must ComputeThis here to censor Call objects; performance hit, * but at least it's idempotent. * * Normally, we call ComputeThis after all frame members have been * set, and in particular, after any revision of the callee value at * *vp due to clasp->convert (see below). This matters because * ComputeThis may access *vp via fp->argv[-2], to follow the parent * chain to a global object to use as the |this| parameter. * * Obviously, here in the JSVAL_IS_PRIMITIVE(v) case, there can't be * any such defaulting of |this| to callee (v, *vp) ancestor. */ frame.argv = vp + 2; ok = ComputeThis(cx, thisp, &frame); if (!ok) goto out2; thisp = frame.thisp; ok = OBJ_GET_PROPERTY(cx, thisp, (jsid)cx->runtime->atomState.noSuchMethodAtom, &v); if (!ok) goto out2; if (JSVAL_IS_PRIMITIVE(v)) goto bad; pc = (jsbytecode *) vp[-(intN)fp->script->depth]; switch ((JSOp) *pc) { case JSOP_NAME: case JSOP_GETPROP: atomIndex = GET_ATOM_INDEX(pc); atom = js_GetAtom(cx, &fp->script->atomMap, atomIndex); argsobj = js_NewArrayObject(cx, argc, vp + 2); if (!argsobj) { ok = JS_FALSE; goto out2; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -