home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / js / src / jsscope.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  14.9 KB  |  576 lines

  1. /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. /*
  20.  * JS symbol tables.
  21.  */
  22. #include <string.h>
  23. #include "prtypes.h"
  24. #include "prlog.h"
  25. #include "jsapi.h"
  26. #include "jsatom.h"
  27. #include "jscntxt.h"
  28. #include "jsinterp.h"
  29. #include "jslock.h"
  30. #include "jsnum.h"
  31. #include "jsscope.h"
  32. #include "jsstr.h"
  33.  
  34. PR_STATIC_CALLBACK(PRHashNumber)
  35. js_hash_id(const void *key)
  36. {
  37.     jsval v;
  38.     const JSAtom *atom;
  39.  
  40.     v = (jsval)key;
  41.     if (JSVAL_IS_INT(v))
  42.     return JSVAL_TO_INT(v);
  43.     atom = key;
  44.     return atom->number;
  45. }
  46.  
  47. PR_STATIC_CALLBACK(void *)
  48. js_alloc_scope_space(void *priv, size_t size)
  49. {
  50.     return JS_malloc(priv, size);
  51. }
  52.  
  53. PR_STATIC_CALLBACK(void)
  54. js_free_scope_space(void *priv, void *item)
  55. {
  56.     JS_free(priv, item);
  57. }
  58.  
  59. PR_STATIC_CALLBACK(PRHashEntry *)
  60. js_alloc_symbol(void *priv, const void *key)
  61. {
  62.     JSContext *cx;
  63.     JSSymbol *sym;
  64.     JSAtom *atom;
  65.  
  66.     cx = priv;
  67.     PR_ASSERT(JS_IS_LOCKED(cx));
  68.     sym = JS_malloc(cx, sizeof(JSSymbol));
  69.     if (!sym)
  70.     return NULL;
  71.     sym->entry.key = key;
  72.     if (!JSVAL_IS_INT((jsval)key)) {
  73.     atom = (JSAtom *)key;
  74.     js_HoldAtom(cx, atom);
  75.     }
  76.     return &sym->entry;
  77. }
  78.  
  79. PR_STATIC_CALLBACK(void)
  80. js_free_symbol(void *priv, PRHashEntry *he, uintN flag)
  81. {
  82.     JSContext *cx;
  83.     JSSymbol *sym, **sp;
  84.     JSProperty *prop;
  85.  
  86.     cx = priv;
  87.     PR_ASSERT(JS_IS_LOCKED(cx));
  88.     sym = (JSSymbol *)he;
  89.     prop = sym->entry.value;
  90.     if (prop) {
  91.     sym->entry.value = NULL;
  92.     prop = js_DropProperty(cx, prop);
  93.     if (prop) {
  94.         for (sp = &prop->symbols; *sp; sp = &(*sp)->next) {
  95.         if (*sp == sym) {
  96.             *sp = sym->next;
  97.             if (!*sp)
  98.             break;
  99.         }
  100.         }
  101.         sym->next = NULL;
  102.     }
  103.     }
  104.  
  105.     if (flag == HT_FREE_ENTRY) {
  106.     if (!JSVAL_IS_INT(sym_id(sym)))
  107.         JS_LOCK_VOID(cx, js_DropAtom(cx, sym_atom(sym)));
  108.     JS_free(cx, he);
  109.     }
  110. }
  111.  
  112. static PRHashAllocOps hash_scope_alloc_ops = {
  113.     js_alloc_scope_space, js_free_scope_space,
  114.     js_alloc_symbol, js_free_symbol
  115. };
  116.  
  117. /************************************************************************/
  118.  
  119. PR_STATIC_CALLBACK(JSSymbol *)
  120. js_hash_scope_lookup(JSContext *cx, JSScope *scope, jsval id, PRHashNumber hash)
  121. {
  122.     PRHashTable *table = scope->data;
  123.     PRHashEntry **hep;
  124.     JSSymbol *sym;
  125.  
  126.     hep = PR_HashTableRawLookup(table, hash, (const void *)id);
  127.     sym = (JSSymbol *) *hep;
  128.     return sym;
  129. }
  130.  
  131. #define SCOPE_ADD(CLASS_SPECIFIC_CODE)                                        \
  132.     PR_BEGIN_MACRO                                                            \
  133.     if (sym) {                                                            \
  134.         if (sym->entry.value == prop)                                     \
  135.         return sym;                                                   \
  136.         if (sym->entry.value)                                             \
  137.         js_free_symbol(cx, &sym->entry, HT_FREE_VALUE);               \
  138.     } else {                                                              \
  139.         CLASS_SPECIFIC_CODE                                               \
  140.         sym->scope = scope;                                               \
  141.         sym->next = NULL;                                                 \
  142.     }                                                                     \
  143.     if (prop) {                                                           \
  144.         sym->entry.value = js_HoldProperty(cx, prop);                     \
  145.         for (sp = &prop->symbols; *sp; sp = &(*sp)->next)                 \
  146.         ;                                                             \
  147.         *sp = sym;                                                        \
  148.     } else {                                                              \
  149.         sym->entry.value = NULL;                                          \
  150.     }                                                                     \
  151.     PR_END_MACRO
  152.  
  153. PR_STATIC_CALLBACK(JSSymbol *)
  154. js_hash_scope_add(JSContext *cx, JSScope *scope, jsval id, JSProperty *prop)
  155. {
  156.     PRHashTable *table = scope->data;
  157.     const void *key;
  158.     PRHashNumber keyHash;
  159.     PRHashEntry **hep;
  160.     JSSymbol *sym, **sp;
  161.  
  162.     PR_ASSERT(JS_IS_LOCKED(cx));
  163.     table->allocPriv = cx;
  164.     key = (const void *)id;
  165.     keyHash = js_hash_id(key);
  166.     hep = PR_HashTableRawLookup(table, keyHash, key);
  167.     sym = (JSSymbol *) *hep;
  168.     SCOPE_ADD(
  169.     sym = (JSSymbol *) PR_HashTableRawAdd(table, hep, keyHash, key, NULL);
  170.     if (!sym)
  171.         return NULL;
  172.     );
  173.     return sym;
  174. }
  175.  
  176. PR_STATIC_CALLBACK(JSBool)
  177. js_hash_scope_remove(JSContext *cx, JSScope *scope, jsval id)
  178. {
  179.     PRHashTable *table = scope->data;
  180.  
  181.     PR_ASSERT(JS_IS_LOCKED(cx));
  182.     table->allocPriv = cx;
  183.     return PR_HashTableRemove(table, (const void *)id);
  184. }
  185.  
  186. /* Forward declaration for use by js_hash_scope_clear(). */
  187. extern JSScopeOps js_list_scope_ops;
  188.  
  189. PR_STATIC_CALLBACK(void)
  190. js_hash_scope_clear(JSContext *cx, JSScope *scope)
  191. {
  192.     PRHashTable *table = scope->data;
  193.  
  194.     PR_ASSERT(JS_IS_LOCKED(cx));
  195.     table->allocPriv = cx;
  196.     PR_HashTableDestroy(table);
  197.     scope->ops = &js_list_scope_ops;
  198.     scope->data = NULL;
  199. }
  200.  
  201. JSScopeOps js_hash_scope_ops = {
  202.     js_hash_scope_lookup,
  203.     js_hash_scope_add,
  204.     js_hash_scope_remove,
  205.     js_hash_scope_clear
  206. };
  207.  
  208. /************************************************************************/
  209.  
  210. PR_STATIC_CALLBACK(JSSymbol *)
  211. js_list_scope_lookup(JSContext *cx, JSScope *scope, jsval id, PRHashNumber hash)
  212. {
  213.     JSSymbol *sym, **sp;
  214.  
  215.     for (sp = (JSSymbol **)&scope->data; (sym = *sp) != 0;
  216.      sp = (JSSymbol **)&sym->entry.next) {
  217.     if (sym_id(sym) == id) {
  218.         /* Move sym to the front for shorter searches. */
  219.         *sp = (JSSymbol *)sym->entry.next;
  220.         sym->entry.next = scope->data;
  221.         scope->data = sym;
  222.         return sym;
  223.     }
  224.     }
  225.     return NULL;
  226. }
  227.  
  228. #define HASH_THRESHOLD    5
  229.  
  230. PR_STATIC_CALLBACK(JSSymbol *)
  231. js_list_scope_add(JSContext *cx, JSScope *scope, jsval id, JSProperty *prop)
  232. {
  233.     JSSymbol *list = scope->data;
  234.     uint32 nsyms;
  235.     JSSymbol *sym, *next, **sp;
  236.     PRHashTable *table;
  237.     PRHashEntry **hep;
  238.  
  239.     PR_ASSERT(JS_IS_LOCKED(cx));
  240.     nsyms = 0;
  241.     for (sym = list; sym; sym = (JSSymbol *)sym->entry.next) {
  242.     if (sym_id(sym) == id)
  243.         break;
  244.     nsyms++;
  245.     }
  246.  
  247.     if (nsyms >= HASH_THRESHOLD) {
  248.     table = PR_NewHashTable(nsyms, js_hash_id,
  249.                     PR_CompareValues, PR_CompareValues,
  250.                     &hash_scope_alloc_ops, cx);
  251.     if (table) {
  252.         for (sym = list; sym; sym = next) {
  253.         /* Save next for loop update, before it changes in lookup. */
  254.         next = (JSSymbol *)sym->entry.next;
  255.  
  256.         /* Now compute missing keyHash fields. */
  257.         sym->entry.keyHash = js_hash_id(sym->entry.key);
  258.         sym->entry.next = NULL;
  259.         hep = PR_HashTableRawLookup(table,
  260.                         sym->entry.keyHash,
  261.                         sym->entry.key);
  262.         *hep = &sym->entry;
  263.         }
  264.         table->nentries = nsyms;
  265.         scope->ops = &js_hash_scope_ops;
  266.         scope->data = table;
  267.         return scope->ops->add(cx, scope, id, prop);
  268.     }
  269.     }
  270.  
  271.     SCOPE_ADD(
  272.     sym = (JSSymbol *)js_alloc_symbol(cx, (const void *)id);
  273.     if (!sym)
  274.         return NULL;
  275.     /* Don't set keyHash until we know we need it, above. */
  276.     sym->entry.next = scope->data;
  277.     scope->data = sym;
  278.     );
  279.     return sym;
  280. }
  281.  
  282. PR_STATIC_CALLBACK(JSBool)
  283. js_list_scope_remove(JSContext *cx, JSScope *scope, jsval id)
  284. {
  285.     JSSymbol *sym, **sp;
  286.  
  287.     PR_ASSERT(JS_IS_LOCKED(cx));
  288.     for (sp = (JSSymbol **)&scope->data; (sym = *sp) != 0;
  289.      sp = (JSSymbol **)&sym->entry.next) {
  290.     if (sym_id(sym) == id) {
  291.         *sp = (JSSymbol *)sym->entry.next;
  292.         js_free_symbol(cx, &sym->entry, HT_FREE_ENTRY);
  293.         return JS_TRUE;
  294.     }
  295.     }
  296.     return JS_FALSE;
  297. }
  298.  
  299. PR_STATIC_CALLBACK(void)
  300. js_list_scope_clear(JSContext *cx, JSScope *scope)
  301. {
  302.     JSSymbol *sym;
  303.  
  304.     PR_ASSERT(JS_IS_LOCKED(cx));
  305.     while ((sym = scope->data) != NULL) {
  306.     scope->data = sym->entry.next;
  307.     js_free_symbol(cx, &sym->entry, HT_FREE_ENTRY);
  308.     }
  309. }
  310.  
  311. JSScopeOps js_list_scope_ops = {
  312.     js_list_scope_lookup,
  313.     js_list_scope_add,
  314.     js_list_scope_remove,
  315.     js_list_scope_clear
  316. };
  317.  
  318. /************************************************************************/
  319.  
  320. JSScope *
  321. js_GetMutableScope(JSContext *cx, JSObject *obj)
  322. {
  323.     JSScope *scope, *newscope;
  324.  
  325.     PR_ASSERT(JS_IS_LOCKED(cx));
  326.     scope = (JSScope *)obj->map;
  327.     if (scope->object == obj)
  328.     return scope;
  329.     newscope = js_NewScope(cx, obj->map->clasp, obj);
  330.     if (!newscope)
  331.     return NULL;
  332.     obj->map = (JSObjectMap *)js_HoldScope(cx, newscope);
  333.     js_DropScope(cx, scope);
  334.     return newscope;
  335. }
  336.  
  337. #if SCOPE_TABLE
  338. static PR_HashTable *scope_table;
  339.  
  340. #define entry2scope(he) ((JSScope *)((char *)(he) - offsetof(JSScope, entry)))
  341.  
  342. PR_STATIC_CALLBACK(PRHashNumber)
  343. js_scope_hash(const void *key)
  344. {
  345. }
  346.  
  347. PR_STATIC_CALLBACK(int)
  348. js_scope_compare(const void *v1, const void *v2)
  349. {
  350. }
  351.  
  352. PR_STATIC_CALLBACK(PRHashEntry *)
  353. js_alloc_scope(void *priv, const void *key)
  354. {
  355.     JSScope *scope;
  356.  
  357.     scope = JS_malloc(priv, sizeof(JSScope));
  358.     if (!scope)
  359.     return NULL;
  360.     return &scope->entry;
  361. }
  362.  
  363. PR_STATIC_CALLBACK(void)
  364. js_free_scope(void *priv, PRHashEntry *he, uintN flag)
  365. {
  366.     JSScope *scope;
  367.  
  368.     scope = entry2scope(he);
  369.     JS_free(priv, scope);
  370. }
  371.  
  372. static PRHashAllocOps scope_table_alloc_ops = {
  373.     js_alloc_scope_space, js_free_scope_space,
  374.     js_alloc_scope, js_free_scope
  375. };
  376. #endif /* SCOPE_TABLE */
  377.  
  378. JSScope *
  379. js_MutateScope(JSContext *cx, JSObject *obj, jsval id,
  380.            JSPropertyOp getter, JSPropertyOp setter, uintN flags,
  381.            JSProperty **propp)
  382. {
  383.     PR_ASSERT(JS_IS_LOCKED(cx));
  384.     /* XXX pessimal */
  385.     *propp = NULL;
  386.     return js_GetMutableScope(cx, obj);
  387. }
  388.  
  389. JSScope *
  390. js_NewScope(JSContext *cx, JSClass *clasp, JSObject *obj)
  391. {
  392.     JSScope *scope;
  393. #if SCOPE_TABLE
  394.     PRHashEntry *he, **hep;
  395.  
  396.     if (scope_table) {
  397.     scope_table = PR_NewHashTable(INIT_SCOPE_TABLE_SIZE, js_scope_hash,
  398.                       js_scope_compare, PR_CompareValues,
  399.                       &scope_table_alloc_ops, cx);
  400.     if (!scope_table)
  401.         return NULL;
  402.     } else {
  403.     scope_table->allocPriv = cx;
  404.     }
  405.     hep = PR_HashTableRawLookup(scope_table, NULL, NULL);
  406.     he  = PR_HashTableRawAdd(scope_table, hep, NULL, NULL, hep);
  407.     if (!he)
  408.     return NULL;
  409.     scope = entry2scope(he);
  410. #else
  411.     scope = JS_malloc(cx, sizeof(JSScope));
  412.     if (!scope)
  413.     return NULL;
  414. #endif
  415.     scope->map.nrefs = 0;
  416.     scope->map.nslots = 0;
  417.     if (clasp->flags & JSCLASS_HAS_PRIVATE)
  418.     scope->map.freeslot = JSSLOT_PRIVATE + 1;
  419.     else
  420.     scope->map.freeslot = JSSLOT_START;
  421.     scope->map.clasp = clasp;
  422.     scope->map.props = NULL;
  423.     scope->object = obj;
  424.     scope->proptail = &scope->map.props;
  425.     scope->ops = &js_list_scope_ops;
  426.     scope->data = NULL;
  427.     return scope;
  428. }
  429.  
  430. void
  431. js_DestroyScope(JSContext *cx, JSScope *scope)
  432. {
  433.     JSProperty *prop;
  434.  
  435.     PR_ASSERT(JS_IS_LOCKED(cx));
  436.     for (prop = scope->map.props; prop; prop = prop->next)
  437.     prop->object = NULL;
  438.     scope->ops->clear(cx, scope);
  439. #if SCOPE_TABLE
  440.     PR_HashTableRawRemove(scope_table, scope->entry.value, &scope->entry);
  441. #endif
  442.     JS_free(cx, scope);
  443. }
  444.  
  445. JSScope *
  446. js_HoldScope(JSContext *cx, JSScope *scope)
  447. {
  448.     PR_ASSERT(JS_IS_LOCKED(cx));
  449.     PR_ASSERT(scope->map.nrefs >= 0);
  450.     scope->map.nrefs++;
  451.     return scope;
  452. }
  453.  
  454. JSScope *
  455. js_DropScope(JSContext *cx, JSScope *scope)
  456. {
  457.     PR_ASSERT(JS_IS_LOCKED(cx));
  458.     PR_ASSERT(scope->map.nrefs > 0);
  459.     if (--scope->map.nrefs == 0) {
  460.     js_DestroyScope(cx, scope);
  461.     return NULL;
  462.     }
  463.     return scope;
  464. }
  465.  
  466. PRHashNumber
  467. js_HashValue(jsval v)
  468. {
  469.     return js_hash_id((const void *)v);
  470. }
  471.  
  472. jsval
  473. js_IdToValue(jsval id)
  474. {
  475.     JSAtom *atom;
  476.  
  477.     if (JSVAL_IS_INT(id))
  478.     return id;
  479.     atom = (JSAtom *)id;
  480.     return ATOM_KEY(atom);
  481. }
  482.  
  483. JSProperty *
  484. js_NewProperty(JSContext *cx, JSScope *scope, jsval id,
  485.            JSPropertyOp getter, JSPropertyOp setter, uintN flags)
  486. {
  487.     uint32 slot;
  488.     JSProperty *prop;
  489.  
  490.     PR_ASSERT(JS_IS_LOCKED(cx));
  491.     if (!js_AllocSlot(cx, scope->object, &slot))
  492.     return NULL;
  493.     prop = JS_malloc(cx, sizeof(JSProperty));
  494.     if (!prop) {
  495.     js_FreeSlot(cx, scope->object, slot);
  496.     return NULL;
  497.     }
  498.     prop->nrefs = 0;
  499.     prop->id = js_IdToValue(id);
  500.     prop->getter = getter;
  501.     prop->setter = setter;
  502.     prop->slot = slot;
  503.     prop->flags = flags;
  504.     prop->spare = 0;
  505.     prop->object = scope->object;
  506.     prop->symbols = NULL;
  507.     prop->next = NULL;
  508.     prop->prevp = scope->proptail;
  509.     *scope->proptail = prop;
  510.     scope->proptail = &prop->next;
  511.     return prop;
  512. }
  513.  
  514. void
  515. js_DestroyProperty(JSContext *cx, JSProperty *prop)
  516. {
  517.     JSObject *obj;
  518.     JSScope *scope;
  519.  
  520.     /*
  521.      * Test whether obj was finalized before prop's last dereference.
  522.      *
  523.      * This can happen when a deleted property is held by a property iterator
  524.      * object (which points to obj, keeping obj alive so long as the property
  525.      * iterator is reachable).  As soon as the GC finds the iterator to be
  526.      * unreachable, it will finalize the iterator, and may also finalize obj,
  527.      * in an unpredictable order.  If obj is finalized first, its map will be
  528.      * null below, and we need only free prop.
  529.      *
  530.      * In the more typical case of a property whose last reference (from a
  531.      * symbol in obj's scope) goes away before obj is finalized, we must be
  532.      * sure to free prop's slot and unlink it from obj's property list.
  533.      */
  534.     obj = prop->object;
  535.     if (obj) {
  536.     scope = (JSScope *)obj->map;
  537.     if (scope && scope->object) {
  538.         js_FreeSlot(cx, obj, prop->slot);
  539.         *prop->prevp = prop->next;
  540.         if (prop->next)
  541.         prop->next->prevp = prop->prevp;
  542.         else
  543.         scope->proptail = prop->prevp;
  544.     }
  545.     }
  546.  
  547.     /* Purge any cached weak links to prop, then free it. */
  548.     js_FlushPropertyCacheByProp(cx, prop);
  549.     JS_free(cx, prop);
  550. }
  551.  
  552. JSProperty *
  553. js_HoldProperty(JSContext *cx, JSProperty *prop)
  554. {
  555.     PR_ASSERT(JS_IS_LOCKED(cx));
  556.     if (prop) {
  557.     PR_ASSERT(prop->nrefs >= 0);
  558.     prop->nrefs++;
  559.     }
  560.     return prop;
  561. }
  562.  
  563. JSProperty *
  564. js_DropProperty(JSContext *cx, JSProperty *prop)
  565. {
  566.     PR_ASSERT(JS_IS_LOCKED(cx));
  567.     if (prop) {
  568.     PR_ASSERT(prop->nrefs > 0);
  569.     if (--prop->nrefs == 0) {
  570.         js_DestroyProperty(cx, prop);
  571.         prop = NULL;
  572.     }
  573.     }
  574.     return prop;
  575. }
  576.