PageRenderTime 59ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jsscope.cpp

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
C++ | 1879 lines | 1427 code | 168 blank | 284 comment | 305 complexity | 7f81c515cc3e707442909528ca0f62db MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2. * vim: set ts=8 sw=4 et tw=78:
  3. *
  4. * ***** BEGIN LICENSE BLOCK *****
  5. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  6. *
  7. * The contents of this file are subject to the Mozilla Public License Version
  8. * 1.1 (the "License"); you may not use this file except in compliance with
  9. * the License. You may obtain a copy of the License at
  10. * http://www.mozilla.org/MPL/
  11. *
  12. * Software distributed under the License is distributed on an "AS IS" basis,
  13. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  14. * for the specific language governing rights and limitations under the
  15. * License.
  16. *
  17. * The Original Code is Mozilla Communicator client code, released
  18. * March 31, 1998.
  19. *
  20. * The Initial Developer of the Original Code is
  21. * Netscape Communications Corporation.
  22. * Portions created by the Initial Developer are Copyright (C) 1998
  23. * the Initial Developer. All Rights Reserved.
  24. *
  25. * Contributor(s):
  26. *
  27. * Alternatively, the contents of this file may be used under the terms of
  28. * either of the GNU General Public License Version 2 or later (the "GPL"),
  29. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  30. * in which case the provisions of the GPL or the LGPL are applicable instead
  31. * of those above. If you wish to allow use of your version of this file only
  32. * under the terms of either the GPL or the LGPL, and not to allow others to
  33. * use your version of this file under the terms of the MPL, indicate your
  34. * decision by deleting the provisions above and replace them with the notice
  35. * and other provisions required by the GPL or the LGPL. If you do not delete
  36. * the provisions above, a recipient may use your version of this file under
  37. * the terms of any one of the MPL, the GPL or the LGPL.
  38. *
  39. * ***** END LICENSE BLOCK ***** */
  40. /*
  41. * JS symbol tables.
  42. */
  43. #include "jsstddef.h"
  44. #include <stdlib.h>
  45. #include <string.h>
  46. #include "jstypes.h"
  47. #include "jsarena.h"
  48. #include "jsbit.h"
  49. #include "jsclist.h"
  50. #include "jsdhash.h"
  51. #include "jsutil.h" /* Added by JSIFY */
  52. #include "jsapi.h"
  53. #include "jsatom.h"
  54. #include "jscntxt.h"
  55. #include "jsdbgapi.h"
  56. #include "jslock.h"
  57. #include "jsnum.h"
  58. #include "jsscope.h"
  59. #include "jsstr.h"
  60. JSScope *
  61. js_GetMutableScope(JSContext *cx, JSObject *obj)
  62. {
  63. JSScope *scope, *newscope;
  64. JSClass *clasp;
  65. uint32 freeslot;
  66. scope = OBJ_SCOPE(obj);
  67. JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope));
  68. if (scope->object == obj)
  69. return scope;
  70. newscope = js_NewScope(cx, 0, scope->map.ops, LOCKED_OBJ_GET_CLASS(obj),
  71. obj);
  72. if (!newscope)
  73. return NULL;
  74. JS_LOCK_SCOPE(cx, newscope);
  75. obj->map = js_HoldObjectMap(cx, &newscope->map);
  76. JS_ASSERT(newscope->map.freeslot == JSSLOT_FREE(STOBJ_GET_CLASS(obj)));
  77. clasp = STOBJ_GET_CLASS(obj);
  78. if (clasp->reserveSlots) {
  79. freeslot = JSSLOT_FREE(clasp) + clasp->reserveSlots(cx, obj);
  80. if (freeslot > STOBJ_NSLOTS(obj))
  81. freeslot = STOBJ_NSLOTS(obj);
  82. if (newscope->map.freeslot < freeslot)
  83. newscope->map.freeslot = freeslot;
  84. }
  85. scope = (JSScope *) js_DropObjectMap(cx, &scope->map, obj);
  86. JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope);
  87. return newscope;
  88. }
  89. /*
  90. * JSScope uses multiplicative hashing, _a la_ jsdhash.[ch], but specialized
  91. * to minimize footprint. But if a scope has fewer than SCOPE_HASH_THRESHOLD
  92. * entries, we use linear search and avoid allocating scope->table.
  93. */
  94. #define SCOPE_HASH_THRESHOLD 6
  95. #define MIN_SCOPE_SIZE_LOG2 4
  96. #define MIN_SCOPE_SIZE JS_BIT(MIN_SCOPE_SIZE_LOG2)
  97. #define SCOPE_TABLE_NBYTES(n) ((n) * sizeof(JSScopeProperty *))
  98. static void
  99. InitMinimalScope(JSScope *scope)
  100. {
  101. scope->shape = 0;
  102. scope->hashShift = JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2;
  103. scope->entryCount = scope->removedCount = 0;
  104. scope->table = NULL;
  105. scope->lastProp = NULL;
  106. }
  107. static JSBool
  108. CreateScopeTable(JSContext *cx, JSScope *scope, JSBool report)
  109. {
  110. int sizeLog2;
  111. JSScopeProperty *sprop, **spp;
  112. JS_ASSERT(!scope->table);
  113. JS_ASSERT(scope->lastProp);
  114. if (scope->entryCount > SCOPE_HASH_THRESHOLD) {
  115. /*
  116. * Either we're creating a table for a large scope that was populated
  117. * via property cache hit logic under JSOP_INITPROP, JSOP_SETNAME, or
  118. * JSOP_SETPROP; or else calloc failed at least once already. In any
  119. * event, let's try to grow, overallocating to hold at least twice the
  120. * current population.
  121. */
  122. sizeLog2 = JS_CeilingLog2(2 * scope->entryCount);
  123. scope->hashShift = JS_DHASH_BITS - sizeLog2;
  124. } else {
  125. JS_ASSERT(scope->hashShift == JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2);
  126. sizeLog2 = MIN_SCOPE_SIZE_LOG2;
  127. }
  128. scope->table = (JSScopeProperty **)
  129. calloc(JS_BIT(sizeLog2), sizeof(JSScopeProperty *));
  130. if (!scope->table) {
  131. if (report)
  132. JS_ReportOutOfMemory(cx);
  133. return JS_FALSE;
  134. }
  135. js_UpdateMallocCounter(cx, JS_BIT(sizeLog2) * sizeof(JSScopeProperty *));
  136. scope->hashShift = JS_DHASH_BITS - sizeLog2;
  137. for (sprop = scope->lastProp; sprop; sprop = sprop->parent) {
  138. spp = js_SearchScope(scope, sprop->id, JS_TRUE);
  139. SPROP_STORE_PRESERVING_COLLISION(spp, sprop);
  140. }
  141. return JS_TRUE;
  142. }
  143. JSScope *
  144. js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp,
  145. JSObject *obj)
  146. {
  147. JSScope *scope;
  148. scope = (JSScope *) JS_malloc(cx, sizeof(JSScope));
  149. if (!scope)
  150. return NULL;
  151. js_InitObjectMap(&scope->map, nrefs, ops, clasp);
  152. scope->object = obj;
  153. scope->flags = 0;
  154. InitMinimalScope(scope);
  155. #ifdef JS_THREADSAFE
  156. js_InitTitle(cx, &scope->title);
  157. #endif
  158. JS_RUNTIME_METER(cx->runtime, liveScopes);
  159. JS_RUNTIME_METER(cx->runtime, totalScopes);
  160. return scope;
  161. }
  162. #ifdef DEBUG_SCOPE_COUNT
  163. extern void
  164. js_unlog_scope(JSScope *scope);
  165. #endif
  166. #if defined DEBUG || defined JS_DUMP_PROPTREE_STATS
  167. # include "jsprf.h"
  168. # define LIVE_SCOPE_METER(cx,expr) JS_LOCK_RUNTIME_VOID(cx->runtime,expr)
  169. #else
  170. # define LIVE_SCOPE_METER(cx,expr) /* nothing */
  171. #endif
  172. void
  173. js_DestroyScope(JSContext *cx, JSScope *scope)
  174. {
  175. #ifdef DEBUG_SCOPE_COUNT
  176. js_unlog_scope(scope);
  177. #endif
  178. #ifdef JS_THREADSAFE
  179. js_FinishTitle(cx, &scope->title);
  180. #endif
  181. if (scope->table)
  182. JS_free(cx, scope->table);
  183. LIVE_SCOPE_METER(cx, cx->runtime->liveScopeProps -= scope->entryCount);
  184. JS_RUNTIME_UNMETER(cx->runtime, liveScopes);
  185. JS_free(cx, scope);
  186. }
  187. #ifdef JS_DUMP_PROPTREE_STATS
  188. typedef struct JSScopeStats {
  189. jsrefcount searches;
  190. jsrefcount hits;
  191. jsrefcount misses;
  192. jsrefcount hashes;
  193. jsrefcount steps;
  194. jsrefcount stepHits;
  195. jsrefcount stepMisses;
  196. jsrefcount adds;
  197. jsrefcount redundantAdds;
  198. jsrefcount addFailures;
  199. jsrefcount changeFailures;
  200. jsrefcount compresses;
  201. jsrefcount grows;
  202. jsrefcount removes;
  203. jsrefcount removeFrees;
  204. jsrefcount uselessRemoves;
  205. jsrefcount shrinks;
  206. } JSScopeStats;
  207. JS_FRIEND_DATA(JSScopeStats) js_scope_stats = {0};
  208. # define METER(x) JS_ATOMIC_INCREMENT(&js_scope_stats.x)
  209. #else
  210. # define METER(x) /* nothing */
  211. #endif
  212. JS_STATIC_ASSERT(sizeof(JSHashNumber) == 4);
  213. JS_STATIC_ASSERT(sizeof(jsid) == JS_BYTES_PER_WORD);
  214. #if JS_BYTES_PER_WORD == 4
  215. # define HASH_ID(id) ((JSHashNumber)(id))
  216. #elif JS_BYTES_PER_WORD == 8
  217. # define HASH_ID(id) ((JSHashNumber)(id) ^ (JSHashNumber)((id) >> 32))
  218. #else
  219. # error "Unsupported configuration"
  220. #endif
  221. /*
  222. * Double hashing needs the second hash code to be relatively prime to table
  223. * size, so we simply make hash2 odd. The inputs to multiplicative hash are
  224. * the golden ratio, expressed as a fixed-point 32 bit fraction, and the id
  225. * itself.
  226. */
  227. #define SCOPE_HASH0(id) (HASH_ID(id) * JS_GOLDEN_RATIO)
  228. #define SCOPE_HASH1(hash0,shift) ((hash0) >> (shift))
  229. #define SCOPE_HASH2(hash0,log2,shift) ((((hash0) << (log2)) >> (shift)) | 1)
  230. JS_FRIEND_API(JSScopeProperty **)
  231. js_SearchScope(JSScope *scope, jsid id, JSBool adding)
  232. {
  233. JSHashNumber hash0, hash1, hash2;
  234. int hashShift, sizeLog2;
  235. JSScopeProperty *stored, *sprop, **spp, **firstRemoved;
  236. uint32 sizeMask;
  237. METER(searches);
  238. if (!scope->table) {
  239. /* Not enough properties to justify hashing: search from lastProp. */
  240. JS_ASSERT(!SCOPE_HAD_MIDDLE_DELETE(scope));
  241. for (spp = &scope->lastProp; (sprop = *spp); spp = &sprop->parent) {
  242. if (sprop->id == id) {
  243. METER(hits);
  244. return spp;
  245. }
  246. }
  247. METER(misses);
  248. return spp;
  249. }
  250. /* Compute the primary hash address. */
  251. METER(hashes);
  252. hash0 = SCOPE_HASH0(id);
  253. hashShift = scope->hashShift;
  254. hash1 = SCOPE_HASH1(hash0, hashShift);
  255. spp = scope->table + hash1;
  256. /* Miss: return space for a new entry. */
  257. stored = *spp;
  258. if (SPROP_IS_FREE(stored)) {
  259. METER(misses);
  260. return spp;
  261. }
  262. /* Hit: return entry. */
  263. sprop = SPROP_CLEAR_COLLISION(stored);
  264. if (sprop && sprop->id == id) {
  265. METER(hits);
  266. return spp;
  267. }
  268. /* Collision: double hash. */
  269. sizeLog2 = JS_DHASH_BITS - hashShift;
  270. hash2 = SCOPE_HASH2(hash0, sizeLog2, hashShift);
  271. sizeMask = JS_BITMASK(sizeLog2);
  272. /* Save the first removed entry pointer so we can recycle it if adding. */
  273. if (SPROP_IS_REMOVED(stored)) {
  274. firstRemoved = spp;
  275. } else {
  276. firstRemoved = NULL;
  277. if (adding && !SPROP_HAD_COLLISION(stored))
  278. SPROP_FLAG_COLLISION(spp, sprop);
  279. }
  280. for (;;) {
  281. METER(steps);
  282. hash1 -= hash2;
  283. hash1 &= sizeMask;
  284. spp = scope->table + hash1;
  285. stored = *spp;
  286. if (SPROP_IS_FREE(stored)) {
  287. METER(stepMisses);
  288. return (adding && firstRemoved) ? firstRemoved : spp;
  289. }
  290. sprop = SPROP_CLEAR_COLLISION(stored);
  291. if (sprop && sprop->id == id) {
  292. METER(stepHits);
  293. return spp;
  294. }
  295. if (SPROP_IS_REMOVED(stored)) {
  296. if (!firstRemoved)
  297. firstRemoved = spp;
  298. } else {
  299. if (adding && !SPROP_HAD_COLLISION(stored))
  300. SPROP_FLAG_COLLISION(spp, sprop);
  301. }
  302. }
  303. /* NOTREACHED */
  304. return NULL;
  305. }
  306. static JSBool
  307. ChangeScope(JSContext *cx, JSScope *scope, int change)
  308. {
  309. int oldlog2, newlog2;
  310. uint32 oldsize, newsize, nbytes;
  311. JSScopeProperty **table, **oldtable, **spp, **oldspp, *sprop;
  312. if (!scope->table)
  313. return CreateScopeTable(cx, scope, JS_TRUE);
  314. /* Grow, shrink, or compress by changing scope->table. */
  315. oldlog2 = JS_DHASH_BITS - scope->hashShift;
  316. newlog2 = oldlog2 + change;
  317. oldsize = JS_BIT(oldlog2);
  318. newsize = JS_BIT(newlog2);
  319. nbytes = SCOPE_TABLE_NBYTES(newsize);
  320. table = (JSScopeProperty **) calloc(nbytes, 1);
  321. if (!table) {
  322. JS_ReportOutOfMemory(cx);
  323. return JS_FALSE;
  324. }
  325. /* Now that we have a new table allocated, update scope members. */
  326. scope->hashShift = JS_DHASH_BITS - newlog2;
  327. scope->removedCount = 0;
  328. oldtable = scope->table;
  329. scope->table = table;
  330. /* Treat the above calloc as a JS_malloc, to match CreateScopeTable. */
  331. cx->runtime->gcMallocBytes += nbytes;
  332. /* Copy only live entries, leaving removed and free ones behind. */
  333. for (oldspp = oldtable; oldsize != 0; oldspp++) {
  334. sprop = SPROP_FETCH(oldspp);
  335. if (sprop) {
  336. spp = js_SearchScope(scope, sprop->id, JS_TRUE);
  337. JS_ASSERT(SPROP_IS_FREE(*spp));
  338. *spp = sprop;
  339. }
  340. oldsize--;
  341. }
  342. /* Finally, free the old table storage. */
  343. JS_free(cx, oldtable);
  344. return JS_TRUE;
  345. }
  346. /*
  347. * Take care to exclude the mark bits in case we're called from the GC.
  348. */
  349. #define SPROP_FLAGS_NOT_MATCHED (SPROP_MARK | SPROP_FLAG_SHAPE_REGEN)
  350. static JSDHashNumber
  351. js_HashScopeProperty(JSDHashTable *table, const void *key)
  352. {
  353. const JSScopeProperty *sprop = (const JSScopeProperty *)key;
  354. JSDHashNumber hash;
  355. JSPropertyOp gsop;
  356. /* Accumulate from least to most random so the low bits are most random. */
  357. hash = 0;
  358. gsop = sprop->getter;
  359. if (gsop)
  360. hash = JS_ROTATE_LEFT32(hash, 4) ^ (jsword)gsop;
  361. gsop = sprop->setter;
  362. if (gsop)
  363. hash = JS_ROTATE_LEFT32(hash, 4) ^ (jsword)gsop;
  364. hash = JS_ROTATE_LEFT32(hash, 4)
  365. ^ (sprop->flags & ~SPROP_FLAGS_NOT_MATCHED);
  366. hash = JS_ROTATE_LEFT32(hash, 4) ^ sprop->attrs;
  367. hash = JS_ROTATE_LEFT32(hash, 4) ^ sprop->shortid;
  368. hash = JS_ROTATE_LEFT32(hash, 4) ^ sprop->slot;
  369. hash = JS_ROTATE_LEFT32(hash, 4) ^ sprop->id;
  370. return hash;
  371. }
  372. #define SPROP_MATCH(sprop, child) \
  373. SPROP_MATCH_PARAMS(sprop, (child)->id, (child)->getter, (child)->setter, \
  374. (child)->slot, (child)->attrs, (child)->flags, \
  375. (child)->shortid)
  376. #define SPROP_MATCH_PARAMS(sprop, aid, agetter, asetter, aslot, aattrs, \
  377. aflags, ashortid) \
  378. ((sprop)->id == (aid) && \
  379. SPROP_MATCH_PARAMS_AFTER_ID(sprop, agetter, asetter, aslot, aattrs, \
  380. aflags, ashortid))
  381. #define SPROP_MATCH_PARAMS_AFTER_ID(sprop, agetter, asetter, aslot, aattrs, \
  382. aflags, ashortid) \
  383. ((sprop)->getter == (agetter) && \
  384. (sprop)->setter == (asetter) && \
  385. (sprop)->slot == (aslot) && \
  386. (sprop)->attrs == (aattrs) && \
  387. (((sprop)->flags ^ (aflags)) & ~SPROP_FLAGS_NOT_MATCHED) == 0 && \
  388. (sprop)->shortid == (ashortid))
  389. static JSBool
  390. js_MatchScopeProperty(JSDHashTable *table,
  391. const JSDHashEntryHdr *hdr,
  392. const void *key)
  393. {
  394. const JSPropertyTreeEntry *entry = (const JSPropertyTreeEntry *)hdr;
  395. const JSScopeProperty *sprop = entry->child;
  396. const JSScopeProperty *kprop = (const JSScopeProperty *)key;
  397. return SPROP_MATCH(sprop, kprop);
  398. }
  399. static const JSDHashTableOps PropertyTreeHashOps = {
  400. JS_DHashAllocTable,
  401. JS_DHashFreeTable,
  402. js_HashScopeProperty,
  403. js_MatchScopeProperty,
  404. JS_DHashMoveEntryStub,
  405. JS_DHashClearEntryStub,
  406. JS_DHashFinalizeStub,
  407. NULL
  408. };
  409. /*
  410. * A property tree node on rt->propertyFreeList overlays the following prefix
  411. * struct on JSScopeProperty.
  412. */
  413. typedef struct FreeNode {
  414. jsid id;
  415. JSScopeProperty *next;
  416. JSScopeProperty **prevp;
  417. } FreeNode;
  418. #define FREENODE(sprop) ((FreeNode *) (sprop))
  419. #define FREENODE_INSERT(list, sprop) \
  420. JS_BEGIN_MACRO \
  421. FREENODE(sprop)->next = (list); \
  422. FREENODE(sprop)->prevp = &(list); \
  423. if (list) \
  424. FREENODE(list)->prevp = &FREENODE(sprop)->next; \
  425. (list) = (sprop); \
  426. JS_END_MACRO
  427. #define FREENODE_REMOVE(sprop) \
  428. JS_BEGIN_MACRO \
  429. *FREENODE(sprop)->prevp = FREENODE(sprop)->next; \
  430. if (FREENODE(sprop)->next) \
  431. FREENODE(FREENODE(sprop)->next)->prevp = FREENODE(sprop)->prevp; \
  432. JS_END_MACRO
  433. /* NB: Called with rt->gcLock held. */
  434. static JSScopeProperty *
  435. NewScopeProperty(JSRuntime *rt)
  436. {
  437. JSScopeProperty *sprop;
  438. sprop = rt->propertyFreeList;
  439. if (sprop) {
  440. FREENODE_REMOVE(sprop);
  441. } else {
  442. JS_ARENA_ALLOCATE_CAST(sprop, JSScopeProperty *,
  443. &rt->propertyArenaPool,
  444. sizeof(JSScopeProperty));
  445. if (!sprop)
  446. return NULL;
  447. }
  448. JS_RUNTIME_METER(rt, livePropTreeNodes);
  449. JS_RUNTIME_METER(rt, totalPropTreeNodes);
  450. return sprop;
  451. }
  452. #define CHUNKY_KIDS_TAG ((jsuword)1)
  453. #define KIDS_IS_CHUNKY(kids) ((jsuword)(kids) & CHUNKY_KIDS_TAG)
  454. #define KIDS_TO_CHUNK(kids) ((PropTreeKidsChunk *) \
  455. ((jsuword)(kids) & ~CHUNKY_KIDS_TAG))
  456. #define CHUNK_TO_KIDS(chunk) ((JSScopeProperty *) \
  457. ((jsuword)(chunk) | CHUNKY_KIDS_TAG))
  458. #define MAX_KIDS_PER_CHUNK 10
  459. #define CHUNK_HASH_THRESHOLD 30
  460. typedef struct PropTreeKidsChunk PropTreeKidsChunk;
  461. struct PropTreeKidsChunk {
  462. JSScopeProperty *kids[MAX_KIDS_PER_CHUNK];
  463. JSDHashTable *table;
  464. PropTreeKidsChunk *next;
  465. };
  466. static PropTreeKidsChunk *
  467. NewPropTreeKidsChunk(JSRuntime *rt)
  468. {
  469. PropTreeKidsChunk *chunk;
  470. chunk = (PropTreeKidsChunk *) calloc(1, sizeof *chunk);
  471. if (!chunk)
  472. return NULL;
  473. JS_ASSERT(((jsuword)chunk & CHUNKY_KIDS_TAG) == 0);
  474. JS_RUNTIME_METER(rt, propTreeKidsChunks);
  475. return chunk;
  476. }
  477. static void
  478. DestroyPropTreeKidsChunk(JSRuntime *rt, PropTreeKidsChunk *chunk)
  479. {
  480. JS_RUNTIME_UNMETER(rt, propTreeKidsChunks);
  481. if (chunk->table)
  482. JS_DHashTableDestroy(chunk->table);
  483. free(chunk);
  484. }
  485. /* NB: Called with rt->gcLock held. */
  486. static JSBool
  487. InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent,
  488. JSScopeProperty *child, PropTreeKidsChunk *sweptChunk)
  489. {
  490. JSDHashTable *table;
  491. JSPropertyTreeEntry *entry;
  492. JSScopeProperty **childp, *kids, *sprop;
  493. PropTreeKidsChunk *chunk, **chunkp;
  494. uintN i;
  495. JS_ASSERT(!parent || child->parent != parent);
  496. if (!parent) {
  497. table = &rt->propertyTreeHash;
  498. entry = (JSPropertyTreeEntry *)
  499. JS_DHashTableOperate(table, child, JS_DHASH_ADD);
  500. if (!entry)
  501. return JS_FALSE;
  502. childp = &entry->child;
  503. sprop = *childp;
  504. if (!sprop) {
  505. *childp = child;
  506. } else {
  507. /*
  508. * A "Duplicate child" case.
  509. *
  510. * We can't do away with child, as at least one live scope entry
  511. * still points at it. What's more, that scope's lastProp chains
  512. * through an ancestor line to reach child, and js_Enumerate and
  513. * others count on this linkage. We must leave child out of the
  514. * hash table, and not require it to be there when we eventually
  515. * GC it (see RemovePropertyTreeChild, below).
  516. *
  517. * It is necessary to leave the duplicate child out of the hash
  518. * table to preserve entry uniqueness. It is safe to leave the
  519. * child out of the hash table (unlike the duplicate child cases
  520. * below), because the child's parent link will be null, which
  521. * can't dangle.
  522. */
  523. JS_ASSERT(sprop != child && SPROP_MATCH(sprop, child));
  524. JS_RUNTIME_METER(rt, duplicatePropTreeNodes);
  525. }
  526. } else {
  527. childp = &parent->kids;
  528. kids = *childp;
  529. if (kids) {
  530. if (KIDS_IS_CHUNKY(kids)) {
  531. chunk = KIDS_TO_CHUNK(kids);
  532. table = chunk->table;
  533. if (table) {
  534. entry = (JSPropertyTreeEntry *)
  535. JS_DHashTableOperate(table, child, JS_DHASH_ADD);
  536. if (!entry)
  537. return JS_FALSE;
  538. if (!entry->child) {
  539. entry->child = child;
  540. while (chunk->next)
  541. chunk = chunk->next;
  542. for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) {
  543. childp = &chunk->kids[i];
  544. sprop = *childp;
  545. if (!sprop)
  546. goto insert;
  547. }
  548. chunkp = &chunk->next;
  549. goto new_chunk;
  550. }
  551. }
  552. do {
  553. for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) {
  554. childp = &chunk->kids[i];
  555. sprop = *childp;
  556. if (!sprop)
  557. goto insert;
  558. JS_ASSERT(sprop != child);
  559. if (SPROP_MATCH(sprop, child)) {
  560. /*
  561. * Duplicate child, see comment above. In this
  562. * case, we must let the duplicate be inserted at
  563. * this level in the tree, so we keep iterating,
  564. * looking for an empty slot in which to insert.
  565. */
  566. JS_ASSERT(sprop != child);
  567. JS_RUNTIME_METER(rt, duplicatePropTreeNodes);
  568. }
  569. }
  570. chunkp = &chunk->next;
  571. } while ((chunk = *chunkp) != NULL);
  572. new_chunk:
  573. if (sweptChunk) {
  574. chunk = sweptChunk;
  575. } else {
  576. chunk = NewPropTreeKidsChunk(rt);
  577. if (!chunk)
  578. return JS_FALSE;
  579. }
  580. *chunkp = chunk;
  581. childp = &chunk->kids[0];
  582. } else {
  583. sprop = kids;
  584. JS_ASSERT(sprop != child);
  585. if (SPROP_MATCH(sprop, child)) {
  586. /*
  587. * Duplicate child, see comment above. Once again, we
  588. * must let duplicates created by deletion pile up in a
  589. * kids-chunk-list, in order to find them when sweeping
  590. * and thereby avoid dangling parent pointers.
  591. */
  592. JS_RUNTIME_METER(rt, duplicatePropTreeNodes);
  593. }
  594. if (sweptChunk) {
  595. chunk = sweptChunk;
  596. } else {
  597. chunk = NewPropTreeKidsChunk(rt);
  598. if (!chunk)
  599. return JS_FALSE;
  600. }
  601. parent->kids = CHUNK_TO_KIDS(chunk);
  602. chunk->kids[0] = sprop;
  603. childp = &chunk->kids[1];
  604. }
  605. }
  606. insert:
  607. *childp = child;
  608. }
  609. child->parent = parent;
  610. return JS_TRUE;
  611. }
  612. /* NB: Called with rt->gcLock held. */
  613. static PropTreeKidsChunk *
  614. RemovePropertyTreeChild(JSRuntime *rt, JSScopeProperty *child)
  615. {
  616. PropTreeKidsChunk *freeChunk;
  617. JSScopeProperty *parent, *kids, *kid;
  618. JSDHashTable *table;
  619. PropTreeKidsChunk *list, *chunk, **chunkp, *lastChunk;
  620. uintN i, j;
  621. JSPropertyTreeEntry *entry;
  622. freeChunk = NULL;
  623. parent = child->parent;
  624. if (!parent) {
  625. /*
  626. * Don't remove child if it is not in rt->propertyTreeHash, but only
  627. * matches a root child in the table that has compatible members. See
  628. * the "Duplicate child" comments in InsertPropertyTreeChild, above.
  629. */
  630. table = &rt->propertyTreeHash;
  631. } else {
  632. kids = parent->kids;
  633. if (KIDS_IS_CHUNKY(kids)) {
  634. list = chunk = KIDS_TO_CHUNK(kids);
  635. chunkp = &list;
  636. table = chunk->table;
  637. do {
  638. for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) {
  639. if (chunk->kids[i] == child) {
  640. lastChunk = chunk;
  641. if (!lastChunk->next) {
  642. j = i + 1;
  643. } else {
  644. j = 0;
  645. do {
  646. chunkp = &lastChunk->next;
  647. lastChunk = *chunkp;
  648. } while (lastChunk->next);
  649. }
  650. for (; j < MAX_KIDS_PER_CHUNK; j++) {
  651. if (!lastChunk->kids[j])
  652. break;
  653. }
  654. --j;
  655. if (chunk != lastChunk || j > i)
  656. chunk->kids[i] = lastChunk->kids[j];
  657. lastChunk->kids[j] = NULL;
  658. if (j == 0) {
  659. *chunkp = NULL;
  660. if (!list)
  661. parent->kids = NULL;
  662. freeChunk = lastChunk;
  663. }
  664. goto out;
  665. }
  666. }
  667. chunkp = &chunk->next;
  668. } while ((chunk = *chunkp) != NULL);
  669. } else {
  670. table = NULL;
  671. kid = kids;
  672. if (kid == child)
  673. parent->kids = NULL;
  674. }
  675. }
  676. out:
  677. if (table) {
  678. entry = (JSPropertyTreeEntry *)
  679. JS_DHashTableOperate(table, child, JS_DHASH_LOOKUP);
  680. if (entry->child == child)
  681. JS_DHashTableRawRemove(table, &entry->hdr);
  682. }
  683. return freeChunk;
  684. }
  685. static JSDHashTable *
  686. HashChunks(PropTreeKidsChunk *chunk, uintN n)
  687. {
  688. JSDHashTable *table;
  689. uintN i;
  690. JSScopeProperty *sprop;
  691. JSPropertyTreeEntry *entry;
  692. table = JS_NewDHashTable(&PropertyTreeHashOps, NULL,
  693. sizeof(JSPropertyTreeEntry),
  694. JS_DHASH_DEFAULT_CAPACITY(n + 1));
  695. if (!table)
  696. return NULL;
  697. do {
  698. for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) {
  699. sprop = chunk->kids[i];
  700. if (!sprop)
  701. break;
  702. entry = (JSPropertyTreeEntry *)
  703. JS_DHashTableOperate(table, sprop, JS_DHASH_ADD);
  704. entry->child = sprop;
  705. }
  706. } while ((chunk = chunk->next) != NULL);
  707. return table;
  708. }
  709. /*
  710. * Called without cx->runtime->gcLock held. This function acquires that lock
  711. * only when inserting a new child. Thus there may be races to find or add a
  712. * node that result in duplicates. We expect such races to be rare!
  713. *
  714. * We use rt->gcLock, not rt->rtLock, to allow the GC potentially to nest here
  715. * under js_GenerateShape.
  716. */
  717. static JSScopeProperty *
  718. GetPropertyTreeChild(JSContext *cx, JSScopeProperty *parent,
  719. JSScopeProperty *child)
  720. {
  721. JSRuntime *rt;
  722. JSDHashTable *table;
  723. JSPropertyTreeEntry *entry;
  724. JSScopeProperty *sprop;
  725. PropTreeKidsChunk *chunk;
  726. uintN i, n;
  727. uint32 shape;
  728. rt = cx->runtime;
  729. if (!parent) {
  730. JS_LOCK_GC(rt);
  731. table = &rt->propertyTreeHash;
  732. entry = (JSPropertyTreeEntry *)
  733. JS_DHashTableOperate(table, child, JS_DHASH_ADD);
  734. if (!entry)
  735. goto out_of_memory;
  736. sprop = entry->child;
  737. if (sprop)
  738. goto out;
  739. } else {
  740. /*
  741. * Because chunks are appended at the end and never deleted except by
  742. * the GC, we can search without taking the runtime's GC lock. We may
  743. * miss a matching sprop added by another thread, and make a duplicate
  744. * one, but that is an unlikely, therefore small, cost. The property
  745. * tree has extremely low fan-out below its root in popular embeddings
  746. * with real-world workloads.
  747. *
  748. * Patterns such as defining closures that capture a constructor's
  749. * environment as getters or setters on the new object that is passed
  750. * in as |this| can significantly increase fan-out below the property
  751. * tree root -- see bug 335700 for details.
  752. */
  753. entry = NULL;
  754. sprop = parent->kids;
  755. if (sprop) {
  756. if (KIDS_IS_CHUNKY(sprop)) {
  757. chunk = KIDS_TO_CHUNK(sprop);
  758. table = chunk->table;
  759. if (table) {
  760. JS_LOCK_GC(rt);
  761. entry = (JSPropertyTreeEntry *)
  762. JS_DHashTableOperate(table, child, JS_DHASH_LOOKUP);
  763. sprop = entry->child;
  764. if (sprop) {
  765. JS_UNLOCK_GC(rt);
  766. return sprop;
  767. }
  768. goto locked_not_found;
  769. }
  770. n = 0;
  771. do {
  772. for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) {
  773. sprop = chunk->kids[i];
  774. if (!sprop) {
  775. n += i;
  776. if (n >= CHUNK_HASH_THRESHOLD) {
  777. chunk = KIDS_TO_CHUNK(parent->kids);
  778. if (!chunk->table) {
  779. table = HashChunks(chunk, n);
  780. JS_LOCK_GC(rt);
  781. if (!table)
  782. goto out_of_memory;
  783. if (chunk->table)
  784. JS_DHashTableDestroy(table);
  785. else
  786. chunk->table = table;
  787. goto locked_not_found;
  788. }
  789. }
  790. goto not_found;
  791. }
  792. if (SPROP_MATCH(sprop, child))
  793. return sprop;
  794. }
  795. n += MAX_KIDS_PER_CHUNK;
  796. } while ((chunk = chunk->next) != NULL);
  797. } else {
  798. if (SPROP_MATCH(sprop, child))
  799. return sprop;
  800. }
  801. }
  802. not_found:
  803. JS_LOCK_GC(rt);
  804. }
  805. locked_not_found:
  806. /*
  807. * Call js_GenerateShape before the allocation to prevent collecting the
  808. * new property when the shape generation triggers the GC.
  809. */
  810. shape = js_GenerateShape(cx, JS_TRUE, NULL);
  811. sprop = NewScopeProperty(rt);
  812. if (!sprop)
  813. goto out_of_memory;
  814. sprop->id = child->id;
  815. sprop->getter = child->getter;
  816. sprop->setter = child->setter;
  817. sprop->slot = child->slot;
  818. sprop->attrs = child->attrs;
  819. sprop->flags = child->flags;
  820. sprop->shortid = child->shortid;
  821. sprop->parent = sprop->kids = NULL;
  822. sprop->shape = shape;
  823. if (!parent) {
  824. entry->child = sprop;
  825. } else {
  826. if (!InsertPropertyTreeChild(rt, parent, sprop, NULL))
  827. goto out_of_memory;
  828. }
  829. out:
  830. JS_UNLOCK_GC(rt);
  831. return sprop;
  832. out_of_memory:
  833. JS_UNLOCK_GC(rt);
  834. JS_ReportOutOfMemory(cx);
  835. return NULL;
  836. }
  837. #ifdef DEBUG_notbrendan
  838. #define CHECK_ANCESTOR_LINE(scope, sparse) \
  839. JS_BEGIN_MACRO \
  840. if ((scope)->table) CheckAncestorLine(scope, sparse); \
  841. JS_END_MACRO
  842. static void
  843. CheckAncestorLine(JSScope *scope, JSBool sparse)
  844. {
  845. uint32 size;
  846. JSScopeProperty **spp, **start, **end, *ancestorLine, *sprop, *aprop;
  847. uint32 entryCount, ancestorCount;
  848. ancestorLine = SCOPE_LAST_PROP(scope);
  849. if (ancestorLine)
  850. JS_ASSERT(SCOPE_HAS_PROPERTY(scope, ancestorLine));
  851. entryCount = 0;
  852. size = SCOPE_CAPACITY(scope);
  853. start = scope->table;
  854. for (spp = start, end = start + size; spp < end; spp++) {
  855. sprop = SPROP_FETCH(spp);
  856. if (sprop) {
  857. entryCount++;
  858. for (aprop = ancestorLine; aprop; aprop = aprop->parent) {
  859. if (aprop == sprop)
  860. break;
  861. }
  862. JS_ASSERT(aprop);
  863. }
  864. }
  865. JS_ASSERT(entryCount == scope->entryCount);
  866. ancestorCount = 0;
  867. for (sprop = ancestorLine; sprop; sprop = sprop->parent) {
  868. if (SCOPE_HAD_MIDDLE_DELETE(scope) &&
  869. !SCOPE_HAS_PROPERTY(scope, sprop)) {
  870. JS_ASSERT(sparse);
  871. continue;
  872. }
  873. ancestorCount++;
  874. }
  875. JS_ASSERT(ancestorCount == scope->entryCount);
  876. }
  877. #else
  878. #define CHECK_ANCESTOR_LINE(scope, sparse) /* nothing */
  879. #endif
  880. static void
  881. ReportReadOnlyScope(JSContext *cx, JSScope *scope)
  882. {
  883. JSString *str;
  884. const char *bytes;
  885. str = js_ValueToString(cx, OBJECT_TO_JSVAL(scope->object));
  886. if (!str)
  887. return;
  888. bytes = js_GetStringBytes(cx, str);
  889. if (!bytes)
  890. return;
  891. JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_READ_ONLY, bytes);
  892. }
  893. JSScopeProperty *
  894. js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id,
  895. JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
  896. uintN attrs, uintN flags, intN shortid)
  897. {
  898. JSScopeProperty **spp, *sprop, *overwriting, **spvec, **spp2, child;
  899. uint32 size, splen, i;
  900. int change;
  901. JSTempValueRooter tvr;
  902. JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope));
  903. CHECK_ANCESTOR_LINE(scope, JS_TRUE);
  904. /*
  905. * You can't add properties to a sealed scope. But note well that you can
  906. * change property attributes in a sealed scope, even though that replaces
  907. * a JSScopeProperty * in the scope's hash table -- but no id is added, so
  908. * the scope remains sealed.
  909. */
  910. if (SCOPE_IS_SEALED(scope)) {
  911. ReportReadOnlyScope(cx, scope);
  912. return NULL;
  913. }
  914. /*
  915. * Normalize stub getter and setter values for faster is-stub testing in
  916. * the SPROP_CALL_[GS]ETTER macros.
  917. */
  918. if (getter == JS_PropertyStub)
  919. getter = NULL;
  920. if (setter == JS_PropertyStub)
  921. setter = NULL;
  922. /*
  923. * Search for id in order to claim its entry, allocating a property tree
  924. * node if one doesn't already exist for our parameters.
  925. */
  926. spp = js_SearchScope(scope, id, JS_TRUE);
  927. sprop = overwriting = SPROP_FETCH(spp);
  928. if (!sprop) {
  929. JS_COUNT_OPERATION(cx, JSOW_NEW_PROPERTY);
  930. /* Check whether we need to grow, if the load factor is >= .75. */
  931. size = SCOPE_CAPACITY(scope);
  932. if (scope->entryCount + scope->removedCount >= size - (size >> 2)) {
  933. if (scope->removedCount >= size >> 2) {
  934. METER(compresses);
  935. change = 0;
  936. } else {
  937. METER(grows);
  938. change = 1;
  939. }
  940. if (!ChangeScope(cx, scope, change) &&
  941. scope->entryCount + scope->removedCount == size - 1) {
  942. METER(addFailures);
  943. return NULL;
  944. }
  945. spp = js_SearchScope(scope, id, JS_TRUE);
  946. JS_ASSERT(!SPROP_FETCH(spp));
  947. }
  948. } else {
  949. /* Property exists: js_SearchScope must have returned a valid entry. */
  950. JS_ASSERT(!SPROP_IS_REMOVED(*spp));
  951. /*
  952. * If all property members match, this is a redundant add and we can
  953. * return early. If the caller wants to allocate a slot, but doesn't
  954. * care which slot, copy sprop->slot into slot so we can match sprop,
  955. * if all other members match.
  956. */
  957. if (!(attrs & JSPROP_SHARED) &&
  958. slot == SPROP_INVALID_SLOT &&
  959. SPROP_HAS_VALID_SLOT(sprop, scope)) {
  960. slot = sprop->slot;
  961. }
  962. if (SPROP_MATCH_PARAMS_AFTER_ID(sprop, getter, setter, slot, attrs,
  963. flags, shortid)) {
  964. METER(redundantAdds);
  965. return sprop;
  966. }
  967. /*
  968. * If we are clearing sprop to force an existing property to be
  969. * overwritten (apart from a duplicate formal parameter), we must
  970. * unlink it from the ancestor line at scope->lastProp, lazily if
  971. * sprop is not lastProp. And we must remove the entry at *spp,
  972. * precisely so the lazy "middle delete" fixup code further below
  973. * won't find sprop in scope->table, in spite of sprop being on
  974. * the ancestor line.
  975. *
  976. * When we finally succeed in finding or creating a new sprop
  977. * and storing its pointer at *spp, we'll use the |overwriting|
  978. * local saved when we first looked up id to decide whether we're
  979. * indeed creating a new entry, or merely overwriting an existing
  980. * property.
  981. */
  982. if (sprop == SCOPE_LAST_PROP(scope)) {
  983. do {
  984. SCOPE_REMOVE_LAST_PROP(scope);
  985. if (!SCOPE_HAD_MIDDLE_DELETE(scope))
  986. break;
  987. sprop = SCOPE_LAST_PROP(scope);
  988. } while (sprop && !SCOPE_HAS_PROPERTY(scope, sprop));
  989. } else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) {
  990. /*
  991. * If we have no hash table yet, we need one now. The middle
  992. * delete code is simple-minded that way!
  993. */
  994. if (!scope->table) {
  995. if (!CreateScopeTable(cx, scope, JS_TRUE))
  996. return NULL;
  997. spp = js_SearchScope(scope, id, JS_TRUE);
  998. sprop = overwriting = SPROP_FETCH(spp);
  999. }
  1000. SCOPE_SET_MIDDLE_DELETE(scope);
  1001. }
  1002. SCOPE_MAKE_UNIQUE_SHAPE(cx, scope);
  1003. /*
  1004. * If we fail later on trying to find or create a new sprop, we will
  1005. * goto fail_overwrite and restore *spp from |overwriting|. Note that
  1006. * we don't bother to keep scope->removedCount in sync, because we'll
  1007. * fix up *spp and scope->entryCount shortly, no matter how control
  1008. * flow returns from this function.
  1009. */
  1010. if (scope->table)
  1011. SPROP_STORE_PRESERVING_COLLISION(spp, NULL);
  1012. scope->entryCount--;
  1013. CHECK_ANCESTOR_LINE(scope, JS_TRUE);
  1014. sprop = NULL;
  1015. }
  1016. if (!sprop) {
  1017. /*
  1018. * If properties were deleted from the middle of the list starting at
  1019. * scope->lastProp, we may need to fork the property tree and squeeze
  1020. * all deleted properties out of scope's ancestor line. Otherwise we
  1021. * risk adding a node with the same id as a "middle" node, violating
  1022. * the rule that properties along an ancestor line have distinct ids.
  1023. */
  1024. if (SCOPE_HAD_MIDDLE_DELETE(scope)) {
  1025. JS_ASSERT(scope->table);
  1026. CHECK_ANCESTOR_LINE(scope, JS_TRUE);
  1027. splen = scope->entryCount;
  1028. if (splen == 0) {
  1029. JS_ASSERT(scope->lastProp == NULL);
  1030. } else {
  1031. /*
  1032. * Enumerate live entries in scope->table using a temporary
  1033. * vector, by walking the (possibly sparse, due to deletions)
  1034. * ancestor line from scope->lastProp.
  1035. */
  1036. spvec = (JSScopeProperty **)
  1037. JS_malloc(cx, SCOPE_TABLE_NBYTES(splen));
  1038. if (!spvec)
  1039. goto fail_overwrite;
  1040. i = splen;
  1041. sprop = SCOPE_LAST_PROP(scope);
  1042. JS_ASSERT(sprop);
  1043. do {
  1044. /*
  1045. * NB: test SCOPE_GET_PROPERTY, not SCOPE_HAS_PROPERTY --
  1046. * the latter insists that sprop->id maps to sprop, while
  1047. * the former simply tests whether sprop->id is bound in
  1048. * scope. We must allow for duplicate formal parameters
  1049. * along the ancestor line, and fork them as needed.
  1050. */
  1051. if (!SCOPE_GET_PROPERTY(scope, sprop->id))
  1052. continue;
  1053. JS_ASSERT(sprop != overwriting);
  1054. if (i == 0) {
  1055. /*
  1056. * If our original splen estimate, scope->entryCount,
  1057. * is less than the ancestor line height, there must
  1058. * be duplicate formal parameters in this (function
  1059. * object) scope. Count remaining ancestors in order
  1060. * to realloc spvec.
  1061. */
  1062. JSScopeProperty *tmp = sprop;
  1063. do {
  1064. if (SCOPE_GET_PROPERTY(scope, tmp->id))
  1065. i++;
  1066. } while ((tmp = tmp->parent) != NULL);
  1067. spp2 = (JSScopeProperty **)
  1068. JS_realloc(cx, spvec, SCOPE_TABLE_NBYTES(splen+i));
  1069. if (!spp2) {
  1070. JS_free(cx, spvec);
  1071. goto fail_overwrite;
  1072. }
  1073. spvec = spp2;
  1074. memmove(spvec + i, spvec, SCOPE_TABLE_NBYTES(splen));
  1075. splen += i;
  1076. }
  1077. spvec[--i] = sprop;
  1078. } while ((sprop = sprop->parent) != NULL);
  1079. JS_ASSERT(i == 0);
  1080. /*
  1081. * Now loop forward through spvec, forking the property tree
  1082. * whenever we see a "parent gap" due to deletions from scope.
  1083. * NB: sprop is null on first entry to the loop body.
  1084. */
  1085. do {
  1086. if (spvec[i]->parent == sprop) {
  1087. sprop = spvec[i];
  1088. } else {
  1089. sprop = GetPropertyTreeChild(cx, sprop, spvec[i]);
  1090. if (!sprop) {
  1091. JS_free(cx, spvec);
  1092. goto fail_overwrite;
  1093. }
  1094. spp2 = js_SearchScope(scope, sprop->id, JS_FALSE);
  1095. JS_ASSERT(SPROP_FETCH(spp2) == spvec[i]);
  1096. SPROP_STORE_PRESERVING_COLLISION(spp2, sprop);
  1097. }
  1098. } while (++i < splen);
  1099. JS_free(cx, spvec);
  1100. /*
  1101. * Now sprop points to the last property in scope, where the
  1102. * ancestor line from sprop to the root is dense w.r.t. scope:
  1103. * it contains no nodes not mapped by scope->table, apart from
  1104. * any stinking ECMA-mandated duplicate formal parameters.
  1105. */
  1106. scope->lastProp = sprop;
  1107. CHECK_ANCESTOR_LINE(scope, JS_FALSE);
  1108. JS_RUNTIME_METER(cx->runtime, middleDeleteFixups);
  1109. }
  1110. SCOPE_CLR_MIDDLE_DELETE(scope);
  1111. }
  1112. /*
  1113. * Aliases share another property's slot, passed in the |slot| param.
  1114. * Shared properties have no slot. Unshared properties that do not
  1115. * alias another property's slot get one here, but may lose it due to
  1116. * a JS_ClearScope call.
  1117. */
  1118. if (!(flags & SPROP_IS_ALIAS)) {
  1119. if (attrs & JSPROP_SHARED) {
  1120. slot = SPROP_INVALID_SLOT;
  1121. } else {
  1122. /*
  1123. * We may have set slot from a nearly-matching sprop, above.
  1124. * If so, we're overwriting that nearly-matching sprop, so we
  1125. * can reuse its slot -- we don't need to allocate a new one.
  1126. * Similarly, we use a specific slot if provided by the caller.
  1127. */
  1128. if (slot == SPROP_INVALID_SLOT &&
  1129. !js_AllocSlot(cx, scope->object, &slot)) {
  1130. goto fail_overwrite;
  1131. }
  1132. }
  1133. }
  1134. /*
  1135. * Check for a watchpoint on a deleted property; if one exists, change
  1136. * setter to js_watch_set.
  1137. * XXXbe this could get expensive with lots of watchpoints...
  1138. */
  1139. if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList) &&
  1140. js_FindWatchPoint(cx->runtime, scope, id)) {
  1141. JS_PUSH_TEMP_ROOT_SPROP(cx, overwriting, &tvr);
  1142. setter = js_WrapWatchedSetter(cx, id, attrs, setter);
  1143. JS_POP_TEMP_ROOT(cx, &tvr);
  1144. if (!setter)
  1145. goto fail_overwrite;
  1146. }
  1147. /* Find or create a property tree node labeled by our arguments. */
  1148. child.id = id;
  1149. child.getter = getter;
  1150. child.setter = setter;
  1151. child.slot = slot;
  1152. child.attrs = attrs;
  1153. child.flags = flags;
  1154. child.shortid = shortid;
  1155. sprop = GetPropertyTreeChild(cx, scope->lastProp, &child);
  1156. if (!sprop)
  1157. goto fail_overwrite;
  1158. /*
  1159. * The scope's shape defaults to its last property's shape, but may
  1160. * be regenerated later as the scope diverges (from the property cache
  1161. * point of view) from the structural type associated with sprop.
  1162. */
  1163. SCOPE_EXTEND_SHAPE(cx, scope, sprop);
  1164. /* Store the tree node pointer in the table entry for id. */
  1165. if (scope->table)
  1166. SPROP_STORE_PRESERVING_COLLISION(spp, sprop);
  1167. scope->entryCount++;
  1168. scope->lastProp = sprop;
  1169. CHECK_ANCESTOR_LINE(scope, JS_FALSE);
  1170. #ifdef DEBUG
  1171. if (!overwriting) {
  1172. LIVE_SCOPE_METER(cx, ++cx->runtime->liveScopeProps);
  1173. JS_RUNTIME_METER(cx->runtime, totalScopeProps);
  1174. }
  1175. #endif
  1176. /*
  1177. * If we reach the hashing threshold, try to allocate scope->table.
  1178. * If we can't (a rare event, preceded by swapping to death on most
  1179. * modern OSes), stick with linear search rather than whining about
  1180. * this little set-back. Therefore we must test !scope->table and
  1181. * scope->entryCount >= SCOPE_HASH_THRESHOLD, not merely whether the
  1182. * entry count just reached the threshold.
  1183. */
  1184. if (!scope->table && scope->entryCount >= SCOPE_HASH_THRESHOLD)
  1185. (void) CreateScopeTable(cx, scope, JS_FALSE);
  1186. }
  1187. METER(adds);
  1188. return sprop;
  1189. fail_overwrite:
  1190. if (overwriting) {
  1191. /*
  1192. * We may or may not have forked overwriting out of scope's ancestor
  1193. * line, so we must check (the alternative is to set a flag above, but
  1194. * that hurts the common, non-error case). If we did fork overwriting
  1195. * out, we'll add it back at scope->lastProp. This means enumeration
  1196. * order can change due to a failure to overwrite an id.
  1197. * XXXbe very minor incompatibility
  1198. */
  1199. for (sprop = SCOPE_LAST_PROP(scope); ; sprop = sprop->parent) {
  1200. if (!sprop) {
  1201. sprop = SCOPE_LAST_PROP(scope);
  1202. if (overwriting->parent == sprop) {
  1203. scope->lastProp = overwriting;
  1204. } else {
  1205. sprop = GetPropertyTreeChild(cx, sprop, overwriting);
  1206. if (sprop) {
  1207. JS_ASSERT(sprop != overwriting);
  1208. scope->lastProp = sprop;
  1209. }
  1210. overwriting = sprop;
  1211. }
  1212. break;
  1213. }
  1214. if (sprop == overwriting)
  1215. break;
  1216. }
  1217. if (overwriting) {
  1218. if (scope->table)
  1219. SPROP_STORE_PRESERVING_COLLISION(spp, overwriting);
  1220. scope->entryCount++;
  1221. }
  1222. CHECK_ANCESTOR_LINE(scope, JS_TRUE);
  1223. }
  1224. METER(addFailures);
  1225. return NULL;
  1226. }
  1227. JSScopeProperty *
  1228. js_ChangeScopePropertyAttrs(JSContext *cx, JSScope *scope,
  1229. JSScopeProperty *sprop, uintN attrs, uintN mask,
  1230. JSPropertyOp getter, JSPropertyOp setter)
  1231. {
  1232. JSScopeProperty child, *newsprop, **spp;
  1233. CHECK_ANCESTOR_LINE(scope, JS_TRUE);
  1234. /* Allow only shared (slot-less) => unshared (slot-full) transition. */
  1235. attrs |= sprop->attrs & mask;
  1236. JS_ASSERT(!((attrs ^ sprop->attrs) & JSPROP_SHARED) ||
  1237. !(attrs & JSPROP_SHARED));
  1238. if (getter == JS_PropertyStub)
  1239. getter = NULL;
  1240. if (setter == JS_PropertyStub)
  1241. setter = NULL;
  1242. if (sprop->attrs == attrs &&
  1243. sprop->getter == getter &&
  1244. sprop->setter == setter) {
  1245. return sprop;
  1246. }
  1247. child.id = sprop->id;
  1248. child.getter = getter;
  1249. child.setter = setter;
  1250. child.slot = sprop->slot;
  1251. child.attrs = attrs;
  1252. child.flags = sprop->flags;
  1253. child.shortid = sprop->shortid;
  1254. if (SCOPE_LAST_PROP(scope) == sprop) {
  1255. /*
  1256. * Optimize the case where the last property added to scope is changed
  1257. * to have a different attrs, getter, or setter. In the last property
  1258. * case, we need not fork the property tree. But since we do not call
  1259. * js_AddScopeProperty, we may need to allocate a new slot directly.
  1260. */
  1261. if ((sprop->attrs & JSPROP_SHARED) && !(attrs & JSPROP_SHARED)) {
  1262. JS_ASSERT(child.slot == SPROP_INVALID_SLOT);
  1263. if (!js_AllocSlot(cx, scope->object, &child.slot))
  1264. return NULL;
  1265. }
  1266. newsprop = GetPropertyTreeChild(cx, sprop->parent, &child);
  1267. if (newsprop) {
  1268. spp = js_SearchScope(scope, sprop->id, JS_FALSE);
  1269. JS_ASSERT(SPROP_FETCH(spp) == sprop);
  1270. if (scope->table)
  1271. SPROP_STORE_PRESERVING_COLLISION(spp, newsprop);
  1272. scope->lastProp = newsprop;
  1273. CHECK_ANCESTOR_LINE(scope, JS_TRUE);
  1274. }
  1275. } else {
  1276. /*
  1277. * Let js_AddScopeProperty handle this |overwriting| case, including
  1278. * the conservation of sprop->slot (if it's valid). We must not call
  1279. * js_RemoveScopeProperty here, it will free a valid sprop->slot and
  1280. * js_AddScopeProperty won't re-allocate it.
  1281. */
  1282. newsprop = js_AddScopeProperty(cx, scope, child.id,

Large files files are truncated, but you can click here to view the full file