Annotation of sbbs/include/mozilla/js/jsscope.h, revision 1.1

1.1     ! root        1: /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
        !             2:  *
        !             3:  * ***** BEGIN LICENSE BLOCK *****
        !             4:  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
        !             5:  *
        !             6:  * The contents of this file are subject to the Mozilla Public License Version
        !             7:  * 1.1 (the "License"); you may not use this file except in compliance with
        !             8:  * the License. You may obtain a copy of the License at
        !             9:  * http://www.mozilla.org/MPL/
        !            10:  *
        !            11:  * Software distributed under the License is distributed on an "AS IS" basis,
        !            12:  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
        !            13:  * for the specific language governing rights and limitations under the
        !            14:  * License.
        !            15:  *
        !            16:  * The Original Code is Mozilla Communicator client code, released
        !            17:  * March 31, 1998.
        !            18:  *
        !            19:  * The Initial Developer of the Original Code is
        !            20:  * Netscape Communications Corporation.
        !            21:  * Portions created by the Initial Developer are Copyright (C) 1998
        !            22:  * the Initial Developer. All Rights Reserved.
        !            23:  *
        !            24:  * Contributor(s):
        !            25:  *
        !            26:  * Alternatively, the contents of this file may be used under the terms of
        !            27:  * either of the GNU General Public License Version 2 or later (the "GPL"),
        !            28:  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
        !            29:  * in which case the provisions of the GPL or the LGPL are applicable instead
        !            30:  * of those above. If you wish to allow use of your version of this file only
        !            31:  * under the terms of either the GPL or the LGPL, and not to allow others to
        !            32:  * use your version of this file under the terms of the MPL, indicate your
        !            33:  * decision by deleting the provisions above and replace them with the notice
        !            34:  * and other provisions required by the GPL or the LGPL. If you do not delete
        !            35:  * the provisions above, a recipient may use your version of this file under
        !            36:  * the terms of any one of the MPL, the GPL or the LGPL.
        !            37:  *
        !            38:  * ***** END LICENSE BLOCK ***** */
        !            39: 
        !            40: #ifndef jsscope_h___
        !            41: #define jsscope_h___
        !            42: /*
        !            43:  * JS symbol tables.
        !            44:  */
        !            45: #include "jstypes.h"
        !            46: #include "jsobj.h"
        !            47: #include "jsprvtd.h"
        !            48: #include "jspubtd.h"
        !            49: 
        !            50: #ifdef JS_THREADSAFE
        !            51: # include "jslock.h"
        !            52: #endif
        !            53: 
        !            54: /*
        !            55:  * Given P independent, non-unique properties each of size S words mapped by
        !            56:  * all scopes in a runtime, construct a property tree of N nodes each of size
        !            57:  * S+L words (L for tree linkage).  A nominal L value is 2 for leftmost-child
        !            58:  * and right-sibling links.  We hope that the N < P by enough that the space
        !            59:  * overhead of L, and the overhead of scope entries pointing at property tree
        !            60:  * nodes, is worth it.
        !            61:  *
        !            62:  * The tree construction goes as follows.  If any empty scope in the runtime
        !            63:  * has a property X added to it, find or create a node under the tree root
        !            64:  * labeled X, and set scope->lastProp to point at that node.  If any non-empty
        !            65:  * scope whose most recently added property is labeled Y has another property
        !            66:  * labeled Z added, find or create a node for Z under the node that was added
        !            67:  * for Y, and set scope->lastProp to point at that node.
        !            68:  *
        !            69:  * A property is labeled by its members' values: id, getter, setter, slot,
        !            70:  * attributes, tiny or short id, and a field telling for..in order.  Note that
        !            71:  * labels are not unique in the tree, but they are unique among a node's kids
        !            72:  * (barring rare and benign multi-threaded race condition outcomes, see below)
        !            73:  * and along any ancestor line from the tree root to a given leaf node (except
        !            74:  * for the hard case of duplicate formal parameters to a function).
        !            75:  *
        !            76:  * Thus the root of the tree represents all empty scopes, and the first ply
        !            77:  * of the tree represents all scopes containing one property, etc.  Each node
        !            78:  * in the tree can stand for any number of scopes having the same ordered set
        !            79:  * of properties, where that node was the last added to the scope.  (We need
        !            80:  * not store the root of the tree as a node, and do not -- all we need are
        !            81:  * links to its kids.)
        !            82:  *
        !            83:  * Sidebar on for..in loop order: ECMA requires no particular order, but this
        !            84:  * implementation has promised and delivered property definition order, and
        !            85:  * compatibility is king.  We could use an order number per property, which
        !            86:  * would require a sort in js_Enumerate, and an entry order generation number
        !            87:  * per scope.  An order number beats a list, which should be doubly-linked for
        !            88:  * O(1) delete.  An even better scheme is to use a parent link in the property
        !            89:  * tree, so that the ancestor line can be iterated from scope->lastProp when
        !            90:  * filling in a JSIdArray from back to front.  This parent link also helps the
        !            91:  * GC to sweep properties iteratively.
        !            92:  *
        !            93:  * What if a property Y is deleted from a scope?  If Y is the last property in
        !            94:  * the scope, we simply adjust the scope's lastProp member after we remove the
        !            95:  * scope's hash-table entry pointing at that property node.  The parent link
        !            96:  * mentioned in the for..in sidebar above makes this adjustment O(1).  But if
        !            97:  * Y comes between X and Z in the scope, then we might have to "fork" the tree
        !            98:  * at X, leaving X->Y->Z in case other scopes have those properties added in
        !            99:  * that order; and to finish the fork, we'd add a node labeled Z with the path
        !           100:  * X->Z, if it doesn't exist.  This could lead to lots of extra nodes, and to
        !           101:  * O(n^2) growth when deleting lots of properties.
        !           102:  * 
        !           103:  * Rather, for O(1) growth all around, we should share the path X->Y->Z among
        !           104:  * scopes having those three properties added in that order, and among scopes
        !           105:  * having only X->Z where Y was deleted.  All such scopes have a lastProp that
        !           106:  * points to the Z child of Y.  But a scope in which Y was deleted does not
        !           107:  * have a table entry for Y, and when iterating that scope by traversing the
        !           108:  * ancestor line from Z, we will have to test for a table entry for each node,
        !           109:  * skipping nodes that lack entries.
        !           110:  *
        !           111:  * What if we add Y again?  X->Y->Z->Y is wrong and we'll enumerate Y twice.
        !           112:  * Therefore we must fork in such a case, if not earlier.  Because delete is
        !           113:  * "bursty", we should not fork eagerly.  Delaying a fork till we are at risk
        !           114:  * of adding Y after it was deleted already requires a flag in the JSScope, to
        !           115:  * wit, SCOPE_MIDDLE_DELETE.
        !           116:  *
        !           117:  * What about thread safety?  If the property tree operations done by requests
        !           118:  * are find-node and insert-node, then the only hazard is duplicate insertion.
        !           119:  * This is harmless except for minor bloat.  When all requests have ended or
        !           120:  * been suspended, the GC is free to sweep the tree after marking all nodes
        !           121:  * reachable from scopes, performing remove-node operations as needed.  Note
        !           122:  * also that the stable storage of the property nodes during active requests
        !           123:  * permits the property cache (see jsinterp.h) to dereference JSScopeProperty
        !           124:  * weak references safely.
        !           125:  *
        !           126:  * Is the property tree worth it compared to property storage in each table's
        !           127:  * entries?  To decide, we must find the relation <> between the words used
        !           128:  * with a property tree and the words required without a tree.
        !           129:  *
        !           130:  * Model all scopes as one super-scope of capacity T entries (T a power of 2).
        !           131:  * Let alpha be the load factor of this double hash-table.  With the property
        !           132:  * tree, each entry in the table is a word-sized pointer to a node that can be
        !           133:  * shared by many scopes.  But all such pointers are overhead compared to the
        !           134:  * situation without the property tree, where the table stores property nodes
        !           135:  * directly, as entries each of size S words.  With the property tree, we need
        !           136:  * L=2 extra words per node for siblings and kids pointers.  Without the tree,
        !           137:  * (1-alpha)*S*T words are wasted on free or removed sentinel-entries required
        !           138:  * by double hashing.
        !           139:  *
        !           140:  * Therefore,
        !           141:  *
        !           142:  *      (property tree)                 <> (no property tree)
        !           143:  *      N*(S+L) + T                     <> S*T
        !           144:  *      N*(S+L) + T                     <> P*S + (1-alpha)*S*T
        !           145:  *      N*(S+L) + alpha*T + (1-alpha)*T <> P*S + (1-alpha)*S*T
        !           146:  *
        !           147:  * Note that P is alpha*T by definition, so
        !           148:  *
        !           149:  *      N*(S+L) + P + (1-alpha)*T <> P*S + (1-alpha)*S*T
        !           150:  *      N*(S+L)                   <> P*S - P + (1-alpha)*S*T - (1-alpha)*T
        !           151:  *      N*(S+L)                   <> (P + (1-alpha)*T) * (S-1)
        !           152:  *      N*(S+L)                   <> (P + (1-alpha)*P/alpha) * (S-1)
        !           153:  *      N*(S+L)                   <> P * (1/alpha) * (S-1)
        !           154:  *
        !           155:  * Let N = P*beta for a compression ratio beta, beta <= 1:
        !           156:  *
        !           157:  *      P*beta*(S+L) <> P * (1/alpha) * (S-1)
        !           158:  *      beta*(S+L)   <> (S-1)/alpha
        !           159:  *      beta         <> (S-1)/((S+L)*alpha)
        !           160:  *
        !           161:  * For S = 6 (32-bit architectures) and L = 2, the property tree wins iff
        !           162:  *
        !           163:  *      beta < 5/(8*alpha)
        !           164:  *
        !           165:  * We ensure that alpha <= .75, so the property tree wins if beta < .83_.  An
        !           166:  * average beta from recent Mozilla browser startups was around .6.
        !           167:  *
        !           168:  * Can we reduce L?  Observe that the property tree degenerates into a list of
        !           169:  * lists if at most one property Y follows X in all scopes.  In or near such a
        !           170:  * case, we waste a word on the right-sibling link outside of the root ply of
        !           171:  * the tree.  Note also that the root ply tends to be large, so O(n^2) growth
        !           172:  * searching it is likely, indicating the need for hashing (but with increased
        !           173:  * thread safety costs).
        !           174:  *
        !           175:  * If only K out of N nodes in the property tree have more than one child, we
        !           176:  * could eliminate the sibling link and overlay a children list or hash-table
        !           177:  * pointer on the leftmost-child link (which would then be either null or an
        !           178:  * only-child link; the overlay could be tagged in the low bit of the pointer,
        !           179:  * or flagged elsewhere in the property tree node, although such a flag must
        !           180:  * not be considered when comparing node labels during tree search).
        !           181:  *
        !           182:  * For such a system, L = 1 + (K * averageChildrenTableSize) / N instead of 2.
        !           183:  * If K << N, L approaches 1 and the property tree wins if beta < .95.
        !           184:  *
        !           185:  * We observe that fan-out below the root ply of the property tree appears to
        !           186:  * have extremely low degree (see the MeterPropertyTree code that histograms
        !           187:  * child-counts in jsscope.c), so instead of a hash-table we use a linked list
        !           188:  * of child node pointer arrays ("kid chunks").  The details are isolated in
        !           189:  * jsscope.c; others must treat JSScopeProperty.kids as opaque.  We leave it
        !           190:  * strongly typed for debug-ability of the common (null or one-kid) cases.
        !           191:  *
        !           192:  * One final twist (can you stand it?): the mean number of entries per scope
        !           193:  * in Mozilla is < 5, with a large standard deviation (~8).  Instead of always
        !           194:  * allocating scope->table, we leave it null while initializing all the other
        !           195:  * scope members as if it were non-null and minimal-length.  Until a property
        !           196:  * is added that crosses the threshold of 6 or more entries for hashing, or
        !           197:  * until a "middle delete" occurs, we use linear search from scope->lastProp
        !           198:  * to find a given id, and save on the space overhead of a hash table.
        !           199:  */
        !           200: 
        !           201: struct JSScope {
        !           202:     JSObjectMap     map;                /* base class state */
        !           203:     JSObject        *object;            /* object that owns this scope */
        !           204:     uint16          flags;              /* flags, see below */
        !           205:     int16           hashShift;          /* multiplicative hash shift */
        !           206:     uint32          entryCount;         /* number of entries in table */
        !           207:     uint32          removedCount;       /* removed entry sentinels in table */
        !           208:     JSScopeProperty **table;            /* table of ptrs to shared tree nodes */
        !           209:     JSScopeProperty *lastProp;          /* pointer to last property added */
        !           210: #ifdef JS_THREADSAFE
        !           211:     JSContext       *ownercx;           /* creating context, NULL if shared */
        !           212:     JSThinLock      lock;               /* binary semaphore protecting scope */
        !           213:     union {                             /* union lockful and lock-free state: */
        !           214:         jsrefcount  count;              /* lock entry count for reentrancy */
        !           215:         JSScope     *link;              /* next link in rt->scopeSharingTodo */
        !           216:     } u;
        !           217: #ifdef DEBUG
        !           218:     const char      *file[4];           /* file where lock was (re-)taken */
        !           219:     unsigned int    line[4];            /* line where lock was (re-)taken */
        !           220: #endif
        !           221: #endif
        !           222: };
        !           223: 
        !           224: #define OBJ_SCOPE(obj)                  ((JSScope *)(obj)->map)
        !           225: 
        !           226: /* By definition, hashShift = JS_DHASH_BITS - log2(capacity). */
        !           227: #define SCOPE_CAPACITY(scope)           JS_BIT(JS_DHASH_BITS-(scope)->hashShift)
        !           228: 
        !           229: /* Scope flags and some macros to hide them from other files than jsscope.c. */
        !           230: #define SCOPE_MIDDLE_DELETE             0x0001
        !           231: #define SCOPE_SEALED                    0x0002
        !           232: 
        !           233: #define SCOPE_HAD_MIDDLE_DELETE(scope)  ((scope)->flags & SCOPE_MIDDLE_DELETE)
        !           234: #define SCOPE_SET_MIDDLE_DELETE(scope)  ((scope)->flags |= SCOPE_MIDDLE_DELETE)
        !           235: #define SCOPE_CLR_MIDDLE_DELETE(scope)  ((scope)->flags &= ~SCOPE_MIDDLE_DELETE)
        !           236: 
        !           237: #define SCOPE_IS_SEALED(scope)          ((scope)->flags & SCOPE_SEALED)
        !           238: #define SCOPE_SET_SEALED(scope)         ((scope)->flags |= SCOPE_SEALED)
        !           239: #if 0
        !           240: /*
        !           241:  * Don't define this, it can't be done safely because JS_LOCK_OBJ will avoid
        !           242:  * taking the lock if the object owns its scope and the scope is sealed.
        !           243:  */
        !           244: #define SCOPE_CLR_SEALED(scope)         ((scope)->flags &= ~SCOPE_SEALED)
        !           245: #endif
        !           246: 
        !           247: /*
        !           248:  * A little information hiding for scope->lastProp, in case it ever becomes
        !           249:  * a tagged pointer again.
        !           250:  */
        !           251: #define SCOPE_LAST_PROP(scope)          ((scope)->lastProp)
        !           252: #define SCOPE_REMOVE_LAST_PROP(scope)   ((scope)->lastProp =                  \
        !           253:                                          (scope)->lastProp->parent)
        !           254: 
        !           255: struct JSScopeProperty {
        !           256:     jsid            id;                 /* int-tagged jsval/untagged JSAtom* */
        !           257:     JSPropertyOp    getter;             /* getter and setter hooks or objects */
        !           258:     JSPropertyOp    setter;
        !           259:     uint32          slot;               /* index in obj->slots vector */
        !           260:     uint8           attrs;              /* attributes, see jsapi.h JSPROP_* */
        !           261:     uint8           flags;              /* flags, see below for defines */
        !           262:     int16           shortid;            /* tinyid, or local arg/var index */
        !           263:     JSScopeProperty *parent;            /* parent node, reverse for..in order */
        !           264:     JSScopeProperty *kids;              /* null, single child, or a tagged ptr
        !           265:                                            to many-kids data structure */
        !           266: };
        !           267: 
        !           268: /* JSScopeProperty pointer tag bit indicating a collision. */
        !           269: #define SPROP_COLLISION                 ((jsuword)1)
        !           270: #define SPROP_REMOVED                   ((JSScopeProperty *) SPROP_COLLISION)
        !           271: 
        !           272: /* Macros to get and set sprop pointer values and collision flags. */
        !           273: #define SPROP_IS_FREE(sprop)            ((sprop) == NULL)
        !           274: #define SPROP_IS_REMOVED(sprop)         ((sprop) == SPROP_REMOVED)
        !           275: #define SPROP_IS_LIVE(sprop)            ((sprop) > SPROP_REMOVED)
        !           276: #define SPROP_FLAG_COLLISION(spp,sprop) (*(spp) = (JSScopeProperty *)         \
        !           277:                                          ((jsuword)(sprop) | SPROP_COLLISION))
        !           278: #define SPROP_HAD_COLLISION(sprop)      ((jsuword)(sprop) & SPROP_COLLISION)
        !           279: #define SPROP_FETCH(spp)                SPROP_CLEAR_COLLISION(*(spp))
        !           280: 
        !           281: #define SPROP_CLEAR_COLLISION(sprop)                                          \
        !           282:     ((JSScopeProperty *) ((jsuword)(sprop) & ~SPROP_COLLISION))
        !           283: 
        !           284: #define SPROP_STORE_PRESERVING_COLLISION(spp, sprop)                          \
        !           285:     (*(spp) = (JSScopeProperty *) ((jsuword)(sprop)                           \
        !           286:                                    | SPROP_HAD_COLLISION(*(spp))))
        !           287: 
        !           288: /* Bits stored in sprop->flags. */
        !           289: #define SPROP_MARK                      0x01
        !           290: #define SPROP_IS_DUPLICATE              0x02
        !           291: #define SPROP_IS_ALIAS                  0x04
        !           292: #define SPROP_HAS_SHORTID               0x08
        !           293: 
        !           294: /*
        !           295:  * If SPROP_HAS_SHORTID is set in sprop->flags, we use sprop->shortid rather
        !           296:  * than id when calling sprop's getter or setter.
        !           297:  */
        !           298: #define SPROP_USERID(sprop)                                                   \
        !           299:     (((sprop)->flags & SPROP_HAS_SHORTID) ? INT_TO_JSVAL((sprop)->shortid)    \
        !           300:                                           : ID_TO_VALUE((sprop)->id))
        !           301: 
        !           302: #define SPROP_INVALID_SLOT              0xffffffff
        !           303: 
        !           304: #define SPROP_HAS_VALID_SLOT(sprop, scope)                                    \
        !           305:     ((sprop)->slot < (scope)->map.freeslot)
        !           306: 
        !           307: #define SPROP_HAS_STUB_GETTER(sprop)    (!(sprop)->getter)
        !           308: #define SPROP_HAS_STUB_SETTER(sprop)    (!(sprop)->setter)
        !           309: 
        !           310: #define SPROP_CALL_GETTER(cx,sprop,getter,obj,obj2,vp)                        \
        !           311:     (!(getter) ||                                                             \
        !           312:      (getter)(cx, OBJ_THIS_OBJECT(cx,obj), SPROP_USERID(sprop), vp))
        !           313: #define SPROP_CALL_SETTER(cx,sprop,setter,obj,obj2,vp)                        \
        !           314:     (!(setter) ||                                                             \
        !           315:      (setter)(cx, OBJ_THIS_OBJECT(cx,obj), SPROP_USERID(sprop), vp))
        !           316: 
        !           317: #define SPROP_GET(cx,sprop,obj,obj2,vp)                                       \
        !           318:     (((sprop)->attrs & JSPROP_GETTER)                                         \
        !           319:      ? js_InternalGetOrSet(cx, obj, (sprop)->id,                              \
        !           320:                            OBJECT_TO_JSVAL((sprop)->getter), JSACC_READ,      \
        !           321:                            0, 0, vp)                                          \
        !           322:      : SPROP_CALL_GETTER(cx, sprop, (sprop)->getter, obj, obj2, vp))
        !           323: 
        !           324: #define SPROP_SET(cx,sprop,obj,obj2,vp)                                       \
        !           325:     (((sprop)->attrs & JSPROP_SETTER)                                         \
        !           326:      ? js_InternalGetOrSet(cx, obj, (sprop)->id,                              \
        !           327:                            OBJECT_TO_JSVAL((sprop)->setter), JSACC_WRITE,     \
        !           328:                            1, vp, vp)                                         \
        !           329:      : ((sprop)->attrs & JSPROP_GETTER)                                       \
        !           330:      ? (JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,                    \
        !           331:                              JSMSG_GETTER_ONLY, NULL), JS_FALSE)              \
        !           332:      : SPROP_CALL_SETTER(cx, sprop, (sprop)->setter, obj, obj2, vp))
        !           333: 
        !           334: /* Macro for common expression to test for shared permanent attributes. */
        !           335: #define SPROP_IS_SHARED_PERMANENT(sprop)                                      \
        !           336:     ((~(sprop)->attrs & (JSPROP_SHARED | JSPROP_PERMANENT)) == 0)
        !           337: 
        !           338: extern JSScope *
        !           339: js_GetMutableScope(JSContext *cx, JSObject *obj);
        !           340: 
        !           341: extern JSScope *
        !           342: js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp,
        !           343:             JSObject *obj);
        !           344: 
        !           345: extern void
        !           346: js_DestroyScope(JSContext *cx, JSScope *scope);
        !           347: 
        !           348: #define ID_TO_VALUE(id) (((id) & JSVAL_INT) ? id : ATOM_KEY((JSAtom *)(id)))
        !           349: #define HASH_ID(id)     (((id) & JSVAL_INT)                                   \
        !           350:                          ? (jsatomid) JSVAL_TO_INT(id)                        \
        !           351:                          : ((JSAtom *)id)->number)
        !           352: 
        !           353: extern JS_FRIEND_API(JSScopeProperty **)
        !           354: js_SearchScope(JSScope *scope, jsid id, JSBool adding);
        !           355: 
        !           356: #define SCOPE_GET_PROPERTY(scope, id)                                         \
        !           357:     SPROP_FETCH(js_SearchScope(scope, id, JS_FALSE))
        !           358: 
        !           359: #define SCOPE_HAS_PROPERTY(scope, sprop)                                      \
        !           360:     (SCOPE_GET_PROPERTY(scope, (sprop)->id) == (sprop))
        !           361: 
        !           362: extern JSScopeProperty *
        !           363: js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id,
        !           364:                     JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
        !           365:                     uintN attrs, uintN flags, intN shortid);
        !           366: 
        !           367: extern JSScopeProperty *
        !           368: js_ChangeScopePropertyAttrs(JSContext *cx, JSScope *scope,
        !           369:                             JSScopeProperty *sprop, uintN attrs, uintN mask,
        !           370:                             JSPropertyOp getter, JSPropertyOp setter);
        !           371: 
        !           372: extern JSBool
        !           373: js_RemoveScopeProperty(JSContext *cx, JSScope *scope, jsid id);
        !           374: 
        !           375: extern void
        !           376: js_ClearScope(JSContext *cx, JSScope *scope);
        !           377: 
        !           378: #define MARK_SCOPE_PROPERTY(sprop)      ((sprop)->flags |= SPROP_MARK)
        !           379: 
        !           380: extern void
        !           381: js_SweepScopeProperties(JSRuntime *rt);
        !           382: 
        !           383: extern JSBool
        !           384: js_InitPropertyTree(JSRuntime *rt);
        !           385: 
        !           386: extern void
        !           387: js_FinishPropertyTree(JSRuntime *rt);
        !           388: 
        !           389: #endif /* jsscope_h___ */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.