📄 jsobj.c
字号:
JSPrincipals *principals; JSScript *script; JSBool ok;#if JS_HAS_EVAL_THIS_SCOPE JSObject *callerScopeChain = NULL, *callerVarObj = NULL; JSObject *setCallerScopeChain = NULL; JSBool setCallerVarObj = JS_FALSE;#endif fp = cx->fp; caller = JS_GetScriptedCaller(cx, fp); JS_ASSERT(!caller || caller->pc); indirectCall = (caller && *caller->pc != JSOP_EVAL); if (indirectCall && !JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT, js_GetErrorMessage, NULL, JSMSG_BAD_INDIRECT_CALL, js_eval_str)) { return JS_FALSE; } if (!JSVAL_IS_STRING(argv[0])) { *rval = argv[0]; return JS_TRUE; } /* * If the caller is a lightweight function and doesn't have a variables * object, then we need to provide one for the compiler to stick any * declared (var) variables into. */ if (caller && !caller->varobj && !js_GetCallObject(cx, caller, NULL)) return JS_FALSE;#if JS_HAS_SCRIPT_OBJECT /* * Script.prototype.compile/exec and Object.prototype.eval all take an * optional trailing argument that overrides the scope object. */ scopeobj = NULL; if (argc >= 2) { if (!js_ValueToObject(cx, argv[1], &scopeobj)) return JS_FALSE; argv[1] = OBJECT_TO_JSVAL(scopeobj); } if (!scopeobj)#endif {#if JS_HAS_EVAL_THIS_SCOPE /* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */ if (indirectCall) { callerScopeChain = js_GetScopeChain(cx, caller); if (!callerScopeChain) return JS_FALSE; OBJ_TO_INNER_OBJECT(cx, obj); if (!obj) return JS_FALSE; if (obj != callerScopeChain) { if (!js_CheckPrincipalsAccess(cx, obj, caller->script->principals, cx->runtime->atomState.evalAtom)) { return JS_FALSE; } scopeobj = js_NewWithObject(cx, obj, callerScopeChain, -1); if (!scopeobj) return JS_FALSE; /* Set fp->scopeChain too, for the compiler. */ caller->scopeChain = fp->scopeChain = scopeobj; /* Remember scopeobj so we can null its private when done. */ setCallerScopeChain = scopeobj; } callerVarObj = caller->varobj; if (obj != callerVarObj) { /* Set fp->varobj too, for the compiler. */ caller->varobj = fp->varobj = obj; setCallerVarObj = JS_TRUE; } } /* From here on, control must exit through label out with ok set. */#endif /* Compile using caller's current scope object. */ if (caller) { scopeobj = js_GetScopeChain(cx, caller); if (!scopeobj) { ok = JS_FALSE; goto out; } } } /* Ensure we compile this eval with the right object in the scope chain. */ scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_eval_str); if (!scopeobj) return JS_FALSE; str = JSVAL_TO_STRING(argv[0]); if (caller) { principals = JS_EvalFramePrincipals(cx, fp, caller); if (principals == caller->script->principals) { file = caller->script->filename; line = js_PCToLineNumber(cx, caller->script, caller->pc); } else { file = principals->codebase; line = 0; } } else { file = NULL; line = 0; principals = NULL; } /* * Set JSFRAME_EVAL on fp and any frames (e.g., fun_call if eval.call was * invoked) between fp and its scripted caller, to help the compiler easily * find the same caller whose scope and var obj we've set. * * XXX this nonsense could, and perhaps should, go away with a better way * to pass params to the compiler than via the top-most frame. */ do { fp->flags |= JSFRAME_EVAL; } while ((fp = fp->down) != caller); script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals, JSSTRING_CHARS(str), JSSTRING_LENGTH(str), file, line); if (!script) { ok = JS_FALSE; goto out; }#if JS_HAS_SCRIPT_OBJECT if (argc < 2)#endif { /* Execute using caller's new scope object (might be a Call object). */ if (caller) scopeobj = caller->scopeChain; } /* * Belt-and-braces: check that the lesser of eval's principals and the * caller's principals has access to scopeobj. */ ok = js_CheckPrincipalsAccess(cx, scopeobj, principals, cx->runtime->atomState.evalAtom); if (ok) ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); JS_DestroyScript(cx, script);out:#if JS_HAS_EVAL_THIS_SCOPE /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */ if (setCallerScopeChain) { caller->scopeChain = callerScopeChain; JS_ASSERT(OBJ_GET_CLASS(cx, setCallerScopeChain) == &js_WithClass); JS_SetPrivate(cx, setCallerScopeChain, NULL); } if (setCallerVarObj) caller->varobj = callerVarObj;#endif return ok;}#if JS_HAS_OBJ_WATCHPOINTstatic JSBoolobj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp, void *closure){ JSObject *callable; JSRuntime *rt; JSStackFrame *caller; JSPrincipals *subject, *watcher; JSResolvingKey key; JSResolvingEntry *entry; uint32 generation; jsval argv[3]; JSBool ok; callable = (JSObject *) closure; rt = cx->runtime; if (rt->findObjectPrincipals) { /* Skip over any obj_watch_* frames between us and the real subject. */ caller = JS_GetScriptedCaller(cx, cx->fp); if (caller) { /* * Only call the watch handler if the watcher is allowed to watch * the currently executing script. */ watcher = rt->findObjectPrincipals(cx, callable); subject = JS_StackFramePrincipals(cx, caller); if (watcher && subject && !watcher->subsume(watcher, subject)) { /* Silently don't call the watch handler. */ return JS_TRUE; } } } /* Avoid recursion on (obj, id) already being watched on cx. */ key.obj = obj; key.id = id; if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry)) return JS_FALSE; if (!entry) return JS_TRUE; generation = cx->resolvingTable->generation; argv[0] = id; argv[1] = old; argv[2] = *nvp; ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(callable), 3, argv, nvp); js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation); return ok;}static JSBoolobj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ JSObject *callable; jsval userid, value; jsid propid; uintN attrs; callable = js_ValueToCallableObject(cx, &argv[1], 0); if (!callable) return JS_FALSE; /* Compute the unique int/atom symbol id needed by js_LookupProperty. */ userid = argv[0]; if (!JS_ValueToId(cx, userid, &propid)) return JS_FALSE; if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &value, &attrs)) return JS_FALSE; if (attrs & JSPROP_READONLY) return JS_TRUE; return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, callable);}static JSBoolobj_unwatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ return JS_ClearWatchPoint(cx, obj, argv[0], NULL, NULL);}#endif /* JS_HAS_OBJ_WATCHPOINT *//* * Prototype and property query methods, to complement the 'in' and * 'instanceof' operators. *//* Proposed ECMA 15.2.4.5. */static JSBoolobj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ return js_HasOwnPropertyHelper(cx, obj, obj->map->ops->lookupProperty, argc, argv, rval);}JSBooljs_HasOwnPropertyHelper(JSContext *cx, JSObject *obj, JSLookupPropOp lookup, uintN argc, jsval *argv, jsval *rval){ jsid id; JSObject *obj2; JSProperty *prop; JSScopeProperty *sprop; if (!JS_ValueToId(cx, argv[0], &id)) return JS_FALSE; if (!lookup(cx, obj, id, &obj2, &prop)) return JS_FALSE; if (!prop) { *rval = JSVAL_FALSE; } else if (obj2 == obj) { *rval = JSVAL_TRUE; } else { JSClass *clasp; JSExtendedClass *xclasp; clasp = OBJ_GET_CLASS(cx, obj); xclasp = (clasp->flags & JSCLASS_IS_EXTENDED) ? (JSExtendedClass *)clasp : NULL; if (xclasp && xclasp->outerObject && xclasp->outerObject(cx, obj2) == obj) { *rval = JSVAL_TRUE; } else if (OBJ_IS_NATIVE(obj2) && OBJ_GET_CLASS(cx, obj2) == clasp) { /* * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a * delegated property makes that property appear to be direct in * all delegating instances of the same native class. This hack * avoids bloating every function instance with its own 'length' * (AKA 'arity') property. But it must not extend across class * boundaries, to avoid making hasOwnProperty lie (bug 320854). * * It's not really a hack, of course: a permanent property can't * be deleted, and JSPROP_SHARED means "don't allocate a slot in * any instance, prototype or delegating". Without a slot, and * without the ability to remove and recreate (with differences) * the property, there is no way to tell whether it is directly * owned, or indirectly delegated. */ sprop = (JSScopeProperty *)prop; *rval = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop)); } else { *rval = JSVAL_FALSE; } } if (prop) OBJ_DROP_PROPERTY(cx, obj2, prop); return JS_TRUE;}/* Proposed ECMA 15.2.4.6. */static JSBoolobj_isPrototypeOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ JSBool b; if (!js_IsDelegate(cx, obj, *argv, &b)) return JS_FALSE; *rval = BOOLEAN_TO_JSVAL(b); return JS_TRUE;}/* Proposed ECMA 15.2.4.7. */static JSBoolobj_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ jsid id; uintN attrs; JSObject *obj2; JSProperty *prop; JSBool ok; if (!JS_ValueToId(cx, argv[0], &id)) return JS_FALSE; if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) return JS_FALSE; if (!prop) { *rval = JSVAL_FALSE; return JS_TRUE; } /* * XXX ECMA spec error compatible: return false unless hasOwnProperty. * The ECMA spec really should be fixed so propertyIsEnumerable and the * for..in loop agree on whether prototype properties are enumerable, * obviously by fixing this method (not by breaking the for..in loop!). * * We check here for shared permanent prototype properties, which should * be treated as if they are local to obj. They are an implementation * technique used to satisfy ECMA requirements; users should not be able * to distinguish a shared permanent proto-property from a local one. */ if (obj2 != obj && !(OBJ_IS_NATIVE(obj2) && SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) { OBJ_DROP_PROPERTY(cx, obj2, prop); *rval = JSVAL_FALSE; return JS_TRUE; } ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs); OBJ_DROP_PROPERTY(cx, obj2, prop); if (ok) *rval = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0); return ok;}#if JS_HAS_GETTER_SETTERstatic JSBoolobj_defineGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ jsval fval, junk; jsid id; uintN attrs; fval = argv[1]; if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GETTER_OR_SETTER, js_getter_str); return JS_FALSE; } if (!JS_ValueToId(cx, argv[0], &id)) return JS_FALSE; if (!js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL)) return JS_FALSE; /* * Getters and setters are just like watchpoints from an access * control point of view. */ if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -