/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef jsscopeinlines_h___ #define jsscopeinlines_h___ #include "jscntxt.h" #include "jsdbgapi.h" #include "jsfun.h" #include "jsobj.h" #include "jsscope.h" #include "jscntxtinlines.h" inline JSEmptyScope * JSScope::createEmptyScope(JSContext *cx, js::Class *clasp) { JS_ASSERT(!isSharedEmpty()); JS_ASSERT(!emptyScope); emptyScope = cx->create(cx, clasp); return emptyScope; } inline JSEmptyScope * JSScope::getEmptyScope(JSContext *cx, js::Class *clasp) { if (emptyScope) { JS_ASSERT(clasp == emptyScope->clasp); return emptyScope->hold(); } return createEmptyScope(cx, clasp); } inline bool JSScope::ensureEmptyScope(JSContext *cx, js::Class *clasp) { if (emptyScope) { JS_ASSERT(clasp == emptyScope->clasp); return true; } if (!createEmptyScope(cx, clasp)) return false; /* We are going to have only single ref to the scope. */ JS_ASSERT(emptyScope->nrefs == 2); emptyScope->nrefs = 1; return true; } inline void JSScope::updateShape(JSContext *cx) { JS_ASSERT(object); js::LeaveTraceIfGlobalObject(cx, object); shape = (hasOwnShape() || !lastProp) ? js_GenerateShape(cx, false) : lastProp->shape; } inline void JSScope::updateFlags(const JSScopeProperty *sprop, bool isDefinitelyAtom) { jsuint index; if (!isDefinitelyAtom && js_IdIsIndex(sprop->id, &index)) setIndexedProperties(); if (sprop->isMethod()) setMethodBarrier(); } inline void JSScope::extend(JSContext *cx, JSScopeProperty *sprop, bool isDefinitelyAtom) { ++entryCount; setLastProperty(sprop); updateShape(cx); updateFlags(sprop, isDefinitelyAtom); } /* * Property read barrier for deferred cloning of compiler-created function * objects optimized as typically non-escaping, ad-hoc methods in obj. */ inline bool JSScope::methodReadBarrier(JSContext *cx, JSScopeProperty *sprop, js::Value *vp) { JS_ASSERT(hasMethodBarrier()); JS_ASSERT(hasProperty(sprop)); JS_ASSERT(sprop->isMethod()); JS_ASSERT(&vp->toObject() == &sprop->methodObject()); JS_ASSERT(object->canHaveMethodBarrier()); JSObject *funobj = &vp->toObject(); JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj); JS_ASSERT(fun == funobj && FUN_NULL_CLOSURE(fun)); funobj = CloneFunctionObject(cx, fun, funobj->getParent()); if (!funobj) return false; funobj->setMethodObj(*object); vp->setObject(*funobj); if (!js_SetPropertyHelper(cx, object, sprop->id, 0, vp)) return false; #ifdef DEBUG if (cx->runtime->functionMeterFilename) { JS_FUNCTION_METER(cx, mreadbarrier); typedef JSRuntime::FunctionCountMap HM; HM &h = cx->runtime->methodReadBarrierCountMap; HM::AddPtr p = h.lookupForAdd(fun); if (!p) { h.add(p, fun, 1); } else { JS_ASSERT(p->key == fun); ++p->value; } } #endif return true; } static JS_ALWAYS_INLINE bool ChangesMethodValue(const js::Value &prev, const js::Value &v) { JSObject *prevObj; return prev.isObject() && (prevObj = &prev.toObject())->isFunction() && (!v.isObject() || &v.toObject() != prevObj); } inline bool JSScope::methodWriteBarrier(JSContext *cx, JSScopeProperty *sprop, const js::Value &v) { if (flags & (BRANDED | METHOD_BARRIER)) { const js::Value &prev = object->lockedGetSlot(sprop->slot); if (ChangesMethodValue(prev, v)) { JS_FUNCTION_METER(cx, mwritebarrier); return methodShapeChange(cx, sprop); } } return true; } inline bool JSScope::methodWriteBarrier(JSContext *cx, uint32 slot, const js::Value &v) { if (flags & (BRANDED | METHOD_BARRIER)) { const js::Value &prev = object->lockedGetSlot(slot); if (ChangesMethodValue(prev, v)) { JS_FUNCTION_METER(cx, mwslotbarrier); return methodShapeChange(cx, slot); } } return true; } inline void JSScope::trace(JSTracer *trc) { JSContext *cx = trc->context; JSScopeProperty *sprop = lastProp; uint8 regenFlag = cx->runtime->gcRegenShapesScopeFlag; if (IS_GC_MARKING_TRACER(trc) && cx->runtime->gcRegenShapes && !hasRegenFlag(regenFlag)) { /* * Either this scope has its own shape, which must be regenerated, or * it must have the same shape as lastProp. */ uint32 newShape; if (sprop) { if (!sprop->hasRegenFlag()) { sprop->shape = js_RegenerateShapeForGC(cx); sprop->setRegenFlag(); } newShape = sprop->shape; } if (!sprop || hasOwnShape()) { newShape = js_RegenerateShapeForGC(cx); JS_ASSERT_IF(sprop, newShape != sprop->shape); } shape = newShape; flags ^= JSScope::SHAPE_REGEN; /* Also regenerate the shapes of this scope's empty scope, if there is one. */ JSScope *empty = emptyScope; if (empty) { JS_ASSERT(!empty->emptyScope); if (!empty->hasRegenFlag(regenFlag)) { uint32 newEmptyShape = js_RegenerateShapeForGC(cx); JS_PROPERTY_TREE(cx).emptyShapeChange(empty->shape, newEmptyShape); empty->shape = newEmptyShape; empty->flags ^= JSScope::SHAPE_REGEN; } } } if (sprop) { JS_ASSERT(hasProperty(sprop)); /* Trace scope's property tree ancestor line. */ do { sprop->trace(trc); } while ((sprop = sprop->parent) != NULL); } } inline JSScopeProperty::JSScopeProperty(jsid id, js::PropertyOp getter, js::PropertyOp setter, uint32 slot, uintN attrs, uintN flags, intN shortid) : id(id), rawGetter(getter), rawSetter(setter), slot(slot), attrs(uint8(attrs)), flags(uint8(flags)), shortid(int16(shortid)) { JS_ASSERT_IF(getter && (attrs & JSPROP_GETTER), getterObj->isCallable()); JS_ASSERT_IF(setter && (attrs & JSPROP_SETTER), setterObj->isCallable()); } inline JSDHashNumber JSScopeProperty::hash() const { JSDHashNumber hash = 0; /* Accumulate from least to most random so the low bits are most random. */ JS_ASSERT_IF(isMethod(), !rawSetter || rawSetter == js_watch_set); if (rawGetter) hash = JS_ROTATE_LEFT32(hash, 4) ^ jsuword(rawGetter); if (rawSetter) hash = JS_ROTATE_LEFT32(hash, 4) ^ jsuword(rawSetter); hash = JS_ROTATE_LEFT32(hash, 4) ^ (flags & PUBLIC_FLAGS); hash = JS_ROTATE_LEFT32(hash, 4) ^ attrs; hash = JS_ROTATE_LEFT32(hash, 4) ^ shortid; hash = JS_ROTATE_LEFT32(hash, 4) ^ slot; hash = JS_ROTATE_LEFT32(hash, 4) ^ JSID_BITS(id); return hash; } inline bool JSScopeProperty::matches(const JSScopeProperty *p) const { JS_ASSERT(!JSID_IS_VOID(id)); JS_ASSERT(!JSID_IS_VOID(p->id)); return id == p->id && matchesParamsAfterId(p->rawGetter, p->rawSetter, p->slot, p->attrs, p->flags, p->shortid); } inline bool JSScopeProperty::matchesParamsAfterId(js::PropertyOp agetter, js::PropertyOp asetter, uint32 aslot, uintN aattrs, uintN aflags, intN ashortid) const { JS_ASSERT(!JSID_IS_VOID(id)); return rawGetter == agetter && rawSetter == asetter && slot == aslot && attrs == aattrs && ((flags ^ aflags) & PUBLIC_FLAGS) == 0 && shortid == ashortid; } inline bool JSScopeProperty::get(JSContext* cx, JSObject* obj, JSObject *pobj, js::Value* vp) { JS_ASSERT(!JSID_IS_VOID(this->id)); JS_ASSERT(!hasDefaultGetter()); if (hasGetterValue()) { JS_ASSERT(!isMethod()); js::Value fval = getterValue(); return js::InternalGetOrSet(cx, obj, id, fval, JSACC_READ, 0, 0, vp); } if (isMethod()) { vp->setObject(methodObject()); JSScope *scope = pobj->scope(); JS_ASSERT(scope->object == pobj); return scope->methodReadBarrier(cx, this, vp); } /* * |with (it) color;| ends up here, as do XML filter-expressions. * Avoid exposing the With object to native getters. */ if (obj->getClass() == &js_WithClass) obj = js_UnwrapWithObject(cx, obj); return js::callJSPropertyOp(cx, getterOp(), obj, SPROP_USERID(this), vp); } inline bool JSScopeProperty::set(JSContext* cx, JSObject* obj, js::Value* vp) { JS_ASSERT_IF(hasDefaultSetter(), hasGetterValue()); if (attrs & JSPROP_SETTER) { js::Value fval = setterValue(); return js::InternalGetOrSet(cx, obj, id, fval, JSACC_WRITE, 1, vp, vp); } if (attrs & JSPROP_GETTER) return js_ReportGetterOnlyAssignment(cx); /* See the comment in JSScopeProperty::get as to why we check for With. */ if (obj->getClass() == &js_WithClass) obj = js_UnwrapWithObject(cx, obj); return js::callJSPropertyOpSetter(cx, setterOp(), obj, SPROP_USERID(this), vp); } #endif /* jsscopeinlines_h___ */