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

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs · C++ · 1064 lines · 839 code · 101 blank · 124 comment · 85 complexity · 1363ab9dbc2f9d35f90c607e514bd8c2 MD5 · raw file

  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. * JS atom table.
  41. */
  42. #include "jsstddef.h"
  43. #include <stdlib.h>
  44. #include <string.h>
  45. #include "jstypes.h"
  46. #include "jsutil.h" /* Added by JSIFY */
  47. #include "jshash.h" /* Added by JSIFY */
  48. #include "jsprf.h"
  49. #include "jsapi.h"
  50. #include "jsatom.h"
  51. #include "jscntxt.h"
  52. #include "jsversion.h"
  53. #include "jsgc.h"
  54. #include "jslock.h"
  55. #include "jsnum.h"
  56. #include "jsscan.h"
  57. #include "jsstr.h"
  58. const char *
  59. js_AtomToPrintableString(JSContext *cx, JSAtom *atom)
  60. {
  61. return js_ValueToPrintableString(cx, ATOM_KEY(atom));
  62. }
  63. #define JS_PROTO(name,code,init) const char js_##name##_str[] = #name;
  64. #include "jsproto.tbl"
  65. #undef JS_PROTO
  66. /*
  67. * String constants for common atoms defined in JSAtomState starting from
  68. * JSAtomState.emptyAtom until JSAtomState.lazy.
  69. *
  70. * The elements of the array after the first empty string define strings
  71. * corresponding to the two boolean literals, false and true, followed by the
  72. * JSType enumerators from jspubtd.h starting with "undefined" for JSTYPE_VOID
  73. * (which is pseudo-boolean 2) and continuing as initialized below. The static
  74. * asserts check these relations.
  75. */
  76. JS_STATIC_ASSERT(JSTYPE_LIMIT == 8);
  77. JS_STATIC_ASSERT(JSVAL_TO_BOOLEAN(JSVAL_VOID) == 2);
  78. JS_STATIC_ASSERT(JSTYPE_VOID == 0);
  79. const char *const js_common_atom_names[] = {
  80. "", /* emptyAtom */
  81. js_false_str, /* booleanAtoms[0] */
  82. js_true_str, /* booleanAtoms[1] */
  83. js_undefined_str, /* typeAtoms[JSTYPE_VOID] */
  84. js_object_str, /* typeAtoms[JSTYPE_OBJECT] */
  85. js_function_str, /* typeAtoms[JSTYPE_FUNCTION] */
  86. "string", /* typeAtoms[JSTYPE_STRING] */
  87. "number", /* typeAtoms[JSTYPE_NUMBER] */
  88. "boolean", /* typeAtoms[JSTYPE_BOOLEAN] */
  89. js_null_str, /* typeAtoms[JSTYPE_NULL] */
  90. "xml", /* typeAtoms[JSTYPE_XML] */
  91. js_null_str, /* nullAtom */
  92. #define JS_PROTO(name,code,init) js_##name##_str,
  93. #include "jsproto.tbl"
  94. #undef JS_PROTO
  95. js_anonymous_str, /* anonymousAtom */
  96. js_apply_str, /* applyAtom */
  97. js_arguments_str, /* argumentsAtom */
  98. js_arity_str, /* arityAtom */
  99. js_call_str, /* callAtom */
  100. js_callee_str, /* calleeAtom */
  101. js_caller_str, /* callerAtom */
  102. js_class_prototype_str, /* classPrototypeAtom */
  103. js_constructor_str, /* constructorAtom */
  104. js_count_str, /* countAtom */
  105. js_each_str, /* eachAtom */
  106. js_eval_str, /* evalAtom */
  107. js_fileName_str, /* fileNameAtom */
  108. js_get_str, /* getAtom */
  109. js_getter_str, /* getterAtom */
  110. js_index_str, /* indexAtom */
  111. js_input_str, /* inputAtom */
  112. js_iterator_str, /* iteratorAtom */
  113. js_length_str, /* lengthAtom */
  114. js_lineNumber_str, /* lineNumberAtom */
  115. js_message_str, /* messageAtom */
  116. js_name_str, /* nameAtom */
  117. js_next_str, /* nextAtom */
  118. js_noSuchMethod_str, /* noSuchMethodAtom */
  119. js_parent_str, /* parentAtom */
  120. js_proto_str, /* protoAtom */
  121. js_set_str, /* setAtom */
  122. js_setter_str, /* setterAtom */
  123. js_stack_str, /* stackAtom */
  124. js_toLocaleString_str, /* toLocaleStringAtom */
  125. js_toSource_str, /* toSourceAtom */
  126. js_toString_str, /* toStringAtom */
  127. js_valueOf_str, /* valueOfAtom */
  128. js_toJSON_str, /* toJSONAtom */
  129. "(void 0)", /* void0Atom */
  130. #if JS_HAS_XML_SUPPORT
  131. js_etago_str, /* etagoAtom */
  132. js_namespace_str, /* namespaceAtom */
  133. js_ptagc_str, /* ptagcAtom */
  134. js_qualifier_str, /* qualifierAtom */
  135. js_space_str, /* spaceAtom */
  136. js_stago_str, /* stagoAtom */
  137. js_star_str, /* starAtom */
  138. js_starQualifier_str, /* starQualifierAtom */
  139. js_tagc_str, /* tagcAtom */
  140. js_xml_str, /* xmlAtom */
  141. #endif
  142. #ifdef NARCISSUS
  143. js___call___str, /* __call__Atom */
  144. js___construct___str, /* __construct__Atom */
  145. js___hasInstance___str, /* __hasInstance__Atom */
  146. js_ExecutionContext_str, /* ExecutionContextAtom */
  147. js_current_str, /* currentAtom */
  148. #endif
  149. };
  150. JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names) * sizeof(JSAtom *) ==
  151. LAZY_ATOM_OFFSET_START - ATOM_OFFSET_START);
  152. /*
  153. * Interpreter macros called by the trace recorder assume common atom indexes
  154. * fit in one byte of immediate operand.
  155. */
  156. JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names) < 256);
  157. const size_t js_common_atom_count = JS_ARRAY_LENGTH(js_common_atom_names);
  158. const char js_anonymous_str[] = "anonymous";
  159. const char js_apply_str[] = "apply";
  160. const char js_arguments_str[] = "arguments";
  161. const char js_arity_str[] = "arity";
  162. const char js_call_str[] = "call";
  163. const char js_callee_str[] = "callee";
  164. const char js_caller_str[] = "caller";
  165. const char js_class_prototype_str[] = "prototype";
  166. const char js_constructor_str[] = "constructor";
  167. const char js_count_str[] = "__count__";
  168. const char js_each_str[] = "each";
  169. const char js_eval_str[] = "eval";
  170. const char js_fileName_str[] = "fileName";
  171. const char js_get_str[] = "get";
  172. const char js_getter_str[] = "getter";
  173. const char js_index_str[] = "index";
  174. const char js_input_str[] = "input";
  175. const char js_iterator_str[] = "__iterator__";
  176. const char js_length_str[] = "length";
  177. const char js_lineNumber_str[] = "lineNumber";
  178. const char js_message_str[] = "message";
  179. const char js_name_str[] = "name";
  180. const char js_next_str[] = "next";
  181. const char js_noSuchMethod_str[] = "__noSuchMethod__";
  182. const char js_object_str[] = "object";
  183. const char js_parent_str[] = "__parent__";
  184. const char js_proto_str[] = "__proto__";
  185. const char js_setter_str[] = "setter";
  186. const char js_set_str[] = "set";
  187. const char js_stack_str[] = "stack";
  188. const char js_toSource_str[] = "toSource";
  189. const char js_toString_str[] = "toString";
  190. const char js_toLocaleString_str[] = "toLocaleString";
  191. const char js_undefined_str[] = "undefined";
  192. const char js_valueOf_str[] = "valueOf";
  193. const char js_toJSON_str[] = "toJSON";
  194. #if JS_HAS_XML_SUPPORT
  195. const char js_etago_str[] = "</";
  196. const char js_namespace_str[] = "namespace";
  197. const char js_ptagc_str[] = "/>";
  198. const char js_qualifier_str[] = "::";
  199. const char js_space_str[] = " ";
  200. const char js_stago_str[] = "<";
  201. const char js_star_str[] = "*";
  202. const char js_starQualifier_str[] = "*::";
  203. const char js_tagc_str[] = ">";
  204. const char js_xml_str[] = "xml";
  205. #endif
  206. #if JS_HAS_GENERATORS
  207. const char js_close_str[] = "close";
  208. const char js_send_str[] = "send";
  209. #endif
  210. #ifdef NARCISSUS
  211. const char js___call___str[] = "__call__";
  212. const char js___construct___str[] = "__construct__";
  213. const char js___hasInstance___str[] = "__hasInstance__";
  214. const char js_ExecutionContext_str[] = "ExecutionContext";
  215. const char js_current_str[] = "current";
  216. #endif
  217. /*
  218. * JSAtomState.doubleAtoms and JSAtomState.stringAtoms hashtable entry. To
  219. * support pinned and interned string atoms, we use the lowest bits of the
  220. * keyAndFlags field to store ATOM_PINNED and ATOM_INTERNED flags.
  221. */
  222. typedef struct JSAtomHashEntry {
  223. JSDHashEntryHdr hdr;
  224. jsuword keyAndFlags;
  225. } JSAtomHashEntry;
  226. #define ATOM_ENTRY_FLAG_MASK (ATOM_PINNED | ATOM_INTERNED)
  227. JS_STATIC_ASSERT(ATOM_ENTRY_FLAG_MASK < JSVAL_ALIGN);
  228. /*
  229. * Helper macros to access and modify JSAtomHashEntry.
  230. */
  231. #define TO_ATOM_ENTRY(hdr) ((JSAtomHashEntry *) hdr)
  232. #define ATOM_ENTRY_KEY(entry) \
  233. ((void *)((entry)->keyAndFlags & ~ATOM_ENTRY_FLAG_MASK))
  234. #define ATOM_ENTRY_FLAGS(entry) \
  235. ((uintN)((entry)->keyAndFlags & ATOM_ENTRY_FLAG_MASK))
  236. #define INIT_ATOM_ENTRY(entry, key) \
  237. ((void)((entry)->keyAndFlags = (jsuword)(key)))
  238. #define ADD_ATOM_ENTRY_FLAGS(entry, flags) \
  239. ((void)((entry)->keyAndFlags |= (jsuword)(flags)))
  240. #define CLEAR_ATOM_ENTRY_FLAGS(entry, flags) \
  241. ((void)((entry)->keyAndFlags &= ~(jsuword)(flags)))
  242. static JSDHashNumber
  243. HashDouble(JSDHashTable *table, const void *key);
  244. static JSBool
  245. MatchDouble(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key);
  246. static JSDHashNumber
  247. HashString(JSDHashTable *table, const void *key);
  248. static JSBool
  249. MatchString(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key);
  250. static const JSDHashTableOps DoubleHashOps = {
  251. JS_DHashAllocTable,
  252. JS_DHashFreeTable,
  253. HashDouble,
  254. MatchDouble,
  255. JS_DHashMoveEntryStub,
  256. JS_DHashClearEntryStub,
  257. JS_DHashFinalizeStub,
  258. NULL
  259. };
  260. static const JSDHashTableOps StringHashOps = {
  261. JS_DHashAllocTable,
  262. JS_DHashFreeTable,
  263. HashString,
  264. MatchString,
  265. JS_DHashMoveEntryStub,
  266. JS_DHashClearEntryStub,
  267. JS_DHashFinalizeStub,
  268. NULL
  269. };
  270. #define IS_DOUBLE_TABLE(table) ((table)->ops == &DoubleHashOps)
  271. #define IS_STRING_TABLE(table) ((table)->ops == &StringHashOps)
  272. #define IS_INITIALIZED_STATE(state) IS_DOUBLE_TABLE(&(state)->doubleAtoms)
  273. static JSDHashNumber
  274. HashDouble(JSDHashTable *table, const void *key)
  275. {
  276. jsdouble d;
  277. JS_ASSERT(IS_DOUBLE_TABLE(table));
  278. d = *(jsdouble *)key;
  279. return JSDOUBLE_HI32(d) ^ JSDOUBLE_LO32(d);
  280. }
  281. static JSDHashNumber
  282. HashString(JSDHashTable *table, const void *key)
  283. {
  284. JS_ASSERT(IS_STRING_TABLE(table));
  285. return js_HashString((JSString *)key);
  286. }
  287. static JSBool
  288. MatchDouble(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key)
  289. {
  290. JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
  291. jsdouble d1, d2;
  292. JS_ASSERT(IS_DOUBLE_TABLE(table));
  293. if (entry->keyAndFlags == 0) {
  294. /* See comments in MatchString. */
  295. return JS_FALSE;
  296. }
  297. d1 = *(jsdouble *)ATOM_ENTRY_KEY(entry);
  298. d2 = *(jsdouble *)key;
  299. if (JSDOUBLE_IS_NaN(d1))
  300. return JSDOUBLE_IS_NaN(d2);
  301. #if defined(XP_WIN)
  302. /* XXX MSVC miscompiles such that (NaN == 0) */
  303. if (JSDOUBLE_IS_NaN(d2))
  304. return JS_FALSE;
  305. #endif
  306. return d1 == d2;
  307. }
  308. static JSBool
  309. MatchString(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key)
  310. {
  311. JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
  312. JS_ASSERT(IS_STRING_TABLE(table));
  313. if (entry->keyAndFlags == 0) {
  314. /*
  315. * This happens when js_AtomizeString adds a new hash entry and
  316. * releases the lock but before it takes the lock the second time to
  317. * initialize keyAndFlags for the entry.
  318. *
  319. * We always return false for such entries so JS_DHashTableOperate
  320. * never finds them. We clean them during GC's sweep phase.
  321. *
  322. * It means that with a contested lock or when GC is triggered outside
  323. * the lock we may end up adding two entries, but this is a price for
  324. * simpler code.
  325. */
  326. return JS_FALSE;
  327. }
  328. return js_EqualStrings((JSString *)ATOM_ENTRY_KEY(entry), (JSString *)key);
  329. }
  330. /*
  331. * For a browser build from 2007-08-09 after the browser starts up there are
  332. * just 55 double atoms, but over 15000 string atoms. Not to penalize more
  333. * economical embeddings allocating too much memory initially we initialize
  334. * atomized strings with just 1K entries.
  335. */
  336. #define JS_STRING_HASH_COUNT 1024
  337. #define JS_DOUBLE_HASH_COUNT 64
  338. JSBool
  339. js_InitAtomState(JSRuntime *rt)
  340. {
  341. JSAtomState *state = &rt->atomState;
  342. /*
  343. * The caller must zero the state before calling this function.
  344. */
  345. JS_ASSERT(!state->stringAtoms.ops);
  346. JS_ASSERT(!state->doubleAtoms.ops);
  347. if (!JS_DHashTableInit(&state->stringAtoms, &StringHashOps,
  348. NULL, sizeof(JSAtomHashEntry),
  349. JS_DHASH_DEFAULT_CAPACITY(JS_STRING_HASH_COUNT))) {
  350. state->stringAtoms.ops = NULL;
  351. return JS_FALSE;
  352. }
  353. JS_ASSERT(IS_STRING_TABLE(&state->stringAtoms));
  354. if (!JS_DHashTableInit(&state->doubleAtoms, &DoubleHashOps,
  355. NULL, sizeof(JSAtomHashEntry),
  356. JS_DHASH_DEFAULT_CAPACITY(JS_DOUBLE_HASH_COUNT))) {
  357. state->doubleAtoms.ops = NULL;
  358. JS_DHashTableFinish(&state->stringAtoms);
  359. state->stringAtoms.ops = NULL;
  360. return JS_FALSE;
  361. }
  362. JS_ASSERT(IS_DOUBLE_TABLE(&state->doubleAtoms));
  363. #ifdef JS_THREADSAFE
  364. js_InitLock(&state->lock);
  365. #endif
  366. JS_ASSERT(IS_INITIALIZED_STATE(state));
  367. return JS_TRUE;
  368. }
  369. static JSDHashOperator
  370. js_string_uninterner(JSDHashTable *table, JSDHashEntryHdr *hdr,
  371. uint32 number, void *arg)
  372. {
  373. JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
  374. JSRuntime *rt = (JSRuntime *)arg;
  375. JSString *str;
  376. /*
  377. * Any string entry that remains at this point must be initialized, as the
  378. * last GC should clean any uninitialized ones.
  379. */
  380. JS_ASSERT(IS_STRING_TABLE(table));
  381. JS_ASSERT(entry->keyAndFlags != 0);
  382. str = (JSString *)ATOM_ENTRY_KEY(entry);
  383. /* Pass null as context. */
  384. js_FinalizeStringRT(rt, str, js_GetExternalStringGCType(str), NULL);
  385. return JS_DHASH_NEXT;
  386. }
  387. void
  388. js_FinishAtomState(JSRuntime *rt)
  389. {
  390. JSAtomState *state = &rt->atomState;
  391. if (!IS_INITIALIZED_STATE(state)) {
  392. /*
  393. * We are called with uninitialized state when JS_NewRuntime fails and
  394. * calls JS_DestroyRuntime on a partially initialized runtime.
  395. */
  396. return;
  397. }
  398. JS_DHashTableEnumerate(&state->stringAtoms, js_string_uninterner, rt);
  399. JS_DHashTableFinish(&state->stringAtoms);
  400. JS_DHashTableFinish(&state->doubleAtoms);
  401. #ifdef JS_THREADSAFE
  402. js_FinishLock(&state->lock);
  403. #endif
  404. #ifdef DEBUG
  405. memset(state, JS_FREE_PATTERN, sizeof *state);
  406. #endif
  407. }
  408. JSBool
  409. js_InitCommonAtoms(JSContext *cx)
  410. {
  411. JSAtomState *state = &cx->runtime->atomState;
  412. uintN i;
  413. JSAtom **atoms;
  414. atoms = COMMON_ATOMS_START(state);
  415. for (i = 0; i < JS_ARRAY_LENGTH(js_common_atom_names); i++, atoms++) {
  416. *atoms = js_Atomize(cx, js_common_atom_names[i],
  417. strlen(js_common_atom_names[i]), ATOM_PINNED);
  418. if (!*atoms)
  419. return JS_FALSE;
  420. }
  421. JS_ASSERT((uint8 *)atoms - (uint8 *)state == LAZY_ATOM_OFFSET_START);
  422. memset(atoms, 0, ATOM_OFFSET_LIMIT - LAZY_ATOM_OFFSET_START);
  423. return JS_TRUE;
  424. }
  425. static JSDHashOperator
  426. js_atom_unpinner(JSDHashTable *table, JSDHashEntryHdr *hdr,
  427. uint32 number, void *arg)
  428. {
  429. JS_ASSERT(IS_STRING_TABLE(table));
  430. CLEAR_ATOM_ENTRY_FLAGS(TO_ATOM_ENTRY(hdr), ATOM_PINNED);
  431. return JS_DHASH_NEXT;
  432. }
  433. void
  434. js_FinishCommonAtoms(JSContext *cx)
  435. {
  436. JSAtomState *state = &cx->runtime->atomState;
  437. JS_DHashTableEnumerate(&state->stringAtoms, js_atom_unpinner, NULL);
  438. #ifdef DEBUG
  439. memset(COMMON_ATOMS_START(state), JS_FREE_PATTERN,
  440. ATOM_OFFSET_LIMIT - ATOM_OFFSET_START);
  441. #endif
  442. }
  443. static JSDHashOperator
  444. js_locked_atom_tracer(JSDHashTable *table, JSDHashEntryHdr *hdr,
  445. uint32 number, void *arg)
  446. {
  447. JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
  448. JSTracer *trc = (JSTracer *)arg;
  449. if (entry->keyAndFlags == 0) {
  450. /* Ignore uninitialized entries during tracing. */
  451. return JS_DHASH_NEXT;
  452. }
  453. JS_SET_TRACING_INDEX(trc, "locked_atom", (size_t)number);
  454. JS_CallTracer(trc, ATOM_ENTRY_KEY(entry),
  455. IS_STRING_TABLE(table) ? JSTRACE_STRING : JSTRACE_DOUBLE);
  456. return JS_DHASH_NEXT;
  457. }
  458. static JSDHashOperator
  459. js_pinned_atom_tracer(JSDHashTable *table, JSDHashEntryHdr *hdr,
  460. uint32 number, void *arg)
  461. {
  462. JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
  463. JSTracer *trc = (JSTracer *)arg;
  464. uintN flags = ATOM_ENTRY_FLAGS(entry);
  465. JS_ASSERT(IS_STRING_TABLE(table));
  466. if (flags & (ATOM_PINNED | ATOM_INTERNED)) {
  467. JS_SET_TRACING_INDEX(trc,
  468. flags & ATOM_PINNED
  469. ? "pinned_atom"
  470. : "interned_atom",
  471. (size_t)number);
  472. JS_CallTracer(trc, ATOM_ENTRY_KEY(entry), JSTRACE_STRING);
  473. }
  474. return JS_DHASH_NEXT;
  475. }
  476. void
  477. js_TraceAtomState(JSTracer *trc, JSBool allAtoms)
  478. {
  479. JSAtomState *state;
  480. state = &trc->context->runtime->atomState;
  481. if (allAtoms) {
  482. JS_DHashTableEnumerate(&state->doubleAtoms, js_locked_atom_tracer, trc);
  483. JS_DHashTableEnumerate(&state->stringAtoms, js_locked_atom_tracer, trc);
  484. } else {
  485. JS_DHashTableEnumerate(&state->stringAtoms, js_pinned_atom_tracer, trc);
  486. }
  487. }
  488. static JSDHashOperator
  489. js_atom_sweeper(JSDHashTable *table, JSDHashEntryHdr *hdr,
  490. uint32 number, void *arg)
  491. {
  492. JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
  493. JSContext *cx = (JSContext *)arg;
  494. /* Remove uninitialized entries. */
  495. if (entry->keyAndFlags == 0)
  496. return JS_DHASH_REMOVE;
  497. if (ATOM_ENTRY_FLAGS(entry) & (ATOM_PINNED | ATOM_INTERNED)) {
  498. /* Pinned or interned key cannot be finalized. */
  499. JS_ASSERT(!js_IsAboutToBeFinalized(cx, ATOM_ENTRY_KEY(entry)));
  500. } else if (js_IsAboutToBeFinalized(cx, ATOM_ENTRY_KEY(entry))) {
  501. /* Remove entries with things about to be GC'ed. */
  502. return JS_DHASH_REMOVE;
  503. }
  504. return JS_DHASH_NEXT;
  505. }
  506. void
  507. js_SweepAtomState(JSContext *cx)
  508. {
  509. JSAtomState *state = &cx->runtime->atomState;
  510. JS_DHashTableEnumerate(&state->doubleAtoms, js_atom_sweeper, cx);
  511. JS_DHashTableEnumerate(&state->stringAtoms, js_atom_sweeper, cx);
  512. /*
  513. * Optimize for simplicity and mutate table generation numbers even if the
  514. * sweeper has not removed any entries.
  515. */
  516. state->doubleAtoms.generation++;
  517. state->stringAtoms.generation++;
  518. }
  519. JSAtom *
  520. js_AtomizeDouble(JSContext *cx, jsdouble d)
  521. {
  522. JSAtomState *state;
  523. JSDHashTable *table;
  524. JSAtomHashEntry *entry;
  525. uint32 gen;
  526. jsdouble *key;
  527. jsval v;
  528. state = &cx->runtime->atomState;
  529. table = &state->doubleAtoms;
  530. JS_LOCK(cx, &state->lock);
  531. entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, &d, JS_DHASH_ADD));
  532. if (!entry)
  533. goto failed_hash_add;
  534. if (entry->keyAndFlags == 0) {
  535. gen = ++table->generation;
  536. JS_UNLOCK(cx, &state->lock);
  537. key = js_NewWeaklyRootedDouble(cx, d);
  538. if (!key)
  539. return NULL;
  540. JS_LOCK(cx, &state->lock);
  541. if (table->generation == gen) {
  542. JS_ASSERT(entry->keyAndFlags == 0);
  543. } else {
  544. entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, key,
  545. JS_DHASH_ADD));
  546. if (!entry)
  547. goto failed_hash_add;
  548. if (entry->keyAndFlags != 0)
  549. goto finish;
  550. ++table->generation;
  551. }
  552. INIT_ATOM_ENTRY(entry, key);
  553. }
  554. finish:
  555. v = DOUBLE_TO_JSVAL((jsdouble *)ATOM_ENTRY_KEY(entry));
  556. cx->weakRoots.lastAtom = v;
  557. JS_UNLOCK(cx, &state->lock);
  558. return (JSAtom *)v;
  559. failed_hash_add:
  560. JS_UNLOCK(cx, &state->lock);
  561. JS_ReportOutOfMemory(cx);
  562. return NULL;
  563. }
  564. JSAtom *
  565. js_AtomizeString(JSContext *cx, JSString *str, uintN flags)
  566. {
  567. jsval v;
  568. JSAtomState *state;
  569. JSDHashTable *table;
  570. JSAtomHashEntry *entry;
  571. JSString *key;
  572. uint32 gen;
  573. JS_ASSERT(!(flags & ~(ATOM_PINNED|ATOM_INTERNED|ATOM_TMPSTR|ATOM_NOCOPY)));
  574. JS_ASSERT_IF(flags & ATOM_NOCOPY, flags & ATOM_TMPSTR);
  575. state = &cx->runtime->atomState;
  576. table = &state->stringAtoms;
  577. JS_LOCK(cx, &state->lock);
  578. entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, str, JS_DHASH_ADD));
  579. if (!entry)
  580. goto failed_hash_add;
  581. if (entry->keyAndFlags != 0) {
  582. key = (JSString *)ATOM_ENTRY_KEY(entry);
  583. } else {
  584. /*
  585. * We created a new hashtable entry. Unless str is already allocated
  586. * from the GC heap and flat, we have to release state->lock as
  587. * string construction is a complex operation. For example, it can
  588. * trigger GC which may rehash the table and make the entry invalid.
  589. */
  590. ++table->generation;
  591. if (!(flags & ATOM_TMPSTR) && JSSTRING_IS_FLAT(str)) {
  592. JSFLATSTR_CLEAR_MUTABLE(str);
  593. key = str;
  594. } else {
  595. gen = table->generation;
  596. JS_UNLOCK(cx, &state->lock);
  597. if (flags & ATOM_TMPSTR) {
  598. if (flags & ATOM_NOCOPY) {
  599. key = js_NewString(cx, JSFLATSTR_CHARS(str),
  600. JSFLATSTR_LENGTH(str));
  601. if (!key)
  602. return NULL;
  603. /* Finish handing off chars to the GC'ed key string. */
  604. str->u.chars = NULL;
  605. } else {
  606. key = js_NewStringCopyN(cx, JSFLATSTR_CHARS(str),
  607. JSFLATSTR_LENGTH(str));
  608. if (!key)
  609. return NULL;
  610. }
  611. } else {
  612. JS_ASSERT(JSSTRING_IS_DEPENDENT(str));
  613. if (!js_UndependString(cx, str))
  614. return NULL;
  615. key = str;
  616. }
  617. JS_LOCK(cx, &state->lock);
  618. if (table->generation == gen) {
  619. JS_ASSERT(entry->keyAndFlags == 0);
  620. } else {
  621. entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, key,
  622. JS_DHASH_ADD));
  623. if (!entry)
  624. goto failed_hash_add;
  625. if (entry->keyAndFlags != 0) {
  626. key = (JSString *)ATOM_ENTRY_KEY(entry);
  627. goto finish;
  628. }
  629. ++table->generation;
  630. }
  631. }
  632. INIT_ATOM_ENTRY(entry, key);
  633. JSFLATSTR_SET_ATOMIZED(key);
  634. }
  635. finish:
  636. ADD_ATOM_ENTRY_FLAGS(entry, flags & (ATOM_PINNED | ATOM_INTERNED));
  637. JS_ASSERT(JSSTRING_IS_ATOMIZED(key));
  638. v = STRING_TO_JSVAL(key);
  639. cx->weakRoots.lastAtom = v;
  640. JS_UNLOCK(cx, &state->lock);
  641. return (JSAtom *)v;
  642. failed_hash_add:
  643. JS_UNLOCK(cx, &state->lock);
  644. JS_ReportOutOfMemory(cx);
  645. return NULL;
  646. }
  647. JSAtom *
  648. js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags)
  649. {
  650. jschar *chars;
  651. JSString str;
  652. JSAtom *atom;
  653. /*
  654. * Avoiding the malloc in js_InflateString on shorter strings saves us
  655. * over 20,000 malloc calls on mozilla browser startup. This compares to
  656. * only 131 calls where the string is longer than a 31 char (net) buffer.
  657. * The vast majority of atomized strings are already in the hashtable. So
  658. * js_AtomizeString rarely has to copy the temp string we make.
  659. */
  660. #define ATOMIZE_BUF_MAX 32
  661. jschar inflated[ATOMIZE_BUF_MAX];
  662. size_t inflatedLength = ATOMIZE_BUF_MAX - 1;
  663. if (length < ATOMIZE_BUF_MAX) {
  664. js_InflateStringToBuffer(cx, bytes, length, inflated, &inflatedLength);
  665. inflated[inflatedLength] = 0;
  666. chars = inflated;
  667. } else {
  668. inflatedLength = length;
  669. chars = js_InflateString(cx, bytes, &inflatedLength);
  670. if (!chars)
  671. return NULL;
  672. flags |= ATOM_NOCOPY;
  673. }
  674. JSFLATSTR_INIT(&str, (jschar *)chars, inflatedLength);
  675. atom = js_AtomizeString(cx, &str, ATOM_TMPSTR | flags);
  676. if (chars != inflated && str.u.chars)
  677. JS_free(cx, chars);
  678. return atom;
  679. }
  680. JSAtom *
  681. js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags)
  682. {
  683. JSString str;
  684. JSFLATSTR_INIT(&str, (jschar *)chars, length);
  685. return js_AtomizeString(cx, &str, ATOM_TMPSTR | flags);
  686. }
  687. JSAtom *
  688. js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length)
  689. {
  690. JSString str, *str2;
  691. JSAtomState *state;
  692. JSDHashEntryHdr *hdr;
  693. JSFLATSTR_INIT(&str, (jschar *)chars, length);
  694. state = &cx->runtime->atomState;
  695. JS_LOCK(cx, &state->lock);
  696. hdr = JS_DHashTableOperate(&state->stringAtoms, &str, JS_DHASH_LOOKUP);
  697. str2 = JS_DHASH_ENTRY_IS_BUSY(hdr)
  698. ? (JSString *)ATOM_ENTRY_KEY(TO_ATOM_ENTRY(hdr))
  699. : NULL;
  700. JS_UNLOCK(cx, &state->lock);
  701. return str2 ? (JSAtom *)STRING_TO_JSVAL(str2) : NULL;
  702. }
  703. JSBool
  704. js_AtomizePrimitiveValue(JSContext *cx, jsval v, JSAtom **atomp)
  705. {
  706. JSAtom *atom;
  707. if (JSVAL_IS_STRING(v)) {
  708. atom = js_AtomizeString(cx, JSVAL_TO_STRING(v), 0);
  709. if (!atom)
  710. return JS_FALSE;
  711. } else if (JSVAL_IS_DOUBLE(v)) {
  712. atom = js_AtomizeDouble(cx, *JSVAL_TO_DOUBLE(v));
  713. if (!atom)
  714. return JS_FALSE;
  715. } else {
  716. JS_ASSERT(JSVAL_IS_INT(v) || JSVAL_IS_BOOLEAN(v) ||
  717. JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v));
  718. atom = (JSAtom *)v;
  719. }
  720. *atomp = atom;
  721. return JS_TRUE;
  722. }
  723. JSBool
  724. js_ValueToStringId(JSContext *cx, jsval v, jsid *idp)
  725. {
  726. JSString *str;
  727. JSAtom *atom;
  728. /*
  729. * Optimize for the common case where v is an already-atomized string. The
  730. * comment in jsstr.h before the JSSTRING_SET_ATOMIZED macro's definition
  731. * explains why this is thread-safe. The extra rooting via lastAtom (which
  732. * would otherwise be done in js_js_AtomizeString) ensures the caller that
  733. * the resulting id at is least weakly rooted.
  734. */
  735. if (JSVAL_IS_STRING(v)) {
  736. str = JSVAL_TO_STRING(v);
  737. if (JSSTRING_IS_ATOMIZED(str)) {
  738. cx->weakRoots.lastAtom = v;
  739. *idp = ATOM_TO_JSID((JSAtom *) v);
  740. return JS_TRUE;
  741. }
  742. } else {
  743. str = js_ValueToString(cx, v);
  744. if (!str)
  745. return JS_FALSE;
  746. }
  747. atom = js_AtomizeString(cx, str, 0);
  748. if (!atom)
  749. return JS_FALSE;
  750. *idp = ATOM_TO_JSID(atom);
  751. return JS_TRUE;
  752. }
  753. #ifdef DEBUG
  754. static JSDHashOperator
  755. atom_dumper(JSDHashTable *table, JSDHashEntryHdr *hdr,
  756. uint32 number, void *arg)
  757. {
  758. JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
  759. FILE *fp = (FILE *)arg;
  760. void *key;
  761. uintN flags;
  762. fprintf(fp, "%3u %08x ", number, (uintN)entry->hdr.keyHash);
  763. if (entry->keyAndFlags == 0) {
  764. fputs("<uninitialized>", fp);
  765. } else {
  766. key = ATOM_ENTRY_KEY(entry);
  767. if (IS_DOUBLE_TABLE(table)) {
  768. fprintf(fp, "%.16g", *(jsdouble *)key);
  769. } else {
  770. JS_ASSERT(IS_STRING_TABLE(table));
  771. js_FileEscapedString(fp, (JSString *)key, '"');
  772. }
  773. flags = ATOM_ENTRY_FLAGS(entry);
  774. if (flags != 0) {
  775. fputs((flags & (ATOM_PINNED | ATOM_INTERNED))
  776. ? " pinned | interned"
  777. : (flags & ATOM_PINNED) ? " pinned" : " interned",
  778. fp);
  779. }
  780. }
  781. putc('\n', fp);
  782. return JS_DHASH_NEXT;
  783. }
  784. JS_FRIEND_API(void)
  785. js_DumpAtoms(JSContext *cx, FILE *fp)
  786. {
  787. JSAtomState *state = &cx->runtime->atomState;
  788. fprintf(fp, "stringAtoms table contents:\n");
  789. JS_DHashTableEnumerate(&state->stringAtoms, atom_dumper, fp);
  790. #ifdef JS_DHASHMETER
  791. JS_DHashTableDumpMeter(&state->stringAtoms, atom_dumper, fp);
  792. #endif
  793. putc('\n', fp);
  794. fprintf(fp, "doubleAtoms table contents:\n");
  795. JS_DHashTableEnumerate(&state->doubleAtoms, atom_dumper, fp);
  796. #ifdef JS_DHASHMETER
  797. JS_DHashTableDumpMeter(&state->doubleAtoms, atom_dumper, fp);
  798. #endif
  799. putc('\n', fp);
  800. }
  801. #endif
  802. static JSHashNumber
  803. js_hash_atom_ptr(const void *key)
  804. {
  805. const JSAtom *atom = (const JSAtom *) key;
  806. return ATOM_HASH(atom);
  807. }
  808. static void *
  809. js_alloc_temp_space(void *priv, size_t size)
  810. {
  811. JSContext *cx = (JSContext *) priv;
  812. void *space;
  813. JS_ARENA_ALLOCATE(space, &cx->tempPool, size);
  814. if (!space)
  815. js_ReportOutOfScriptQuota(cx);
  816. return space;
  817. }
  818. static void
  819. js_free_temp_space(void *priv, void *item)
  820. {
  821. }
  822. static JSHashEntry *
  823. js_alloc_temp_entry(void *priv, const void *key)
  824. {
  825. JSContext *cx = (JSContext *) priv;
  826. JSAtomListElement *ale;
  827. JS_ARENA_ALLOCATE_TYPE(ale, JSAtomListElement, &cx->tempPool);
  828. if (!ale) {
  829. js_ReportOutOfScriptQuota(cx);
  830. return NULL;
  831. }
  832. return &ale->entry;
  833. }
  834. static void
  835. js_free_temp_entry(void *priv, JSHashEntry *he, uintN flag)
  836. {
  837. }
  838. static JSHashAllocOps temp_alloc_ops = {
  839. js_alloc_temp_space, js_free_temp_space,
  840. js_alloc_temp_entry, js_free_temp_entry
  841. };
  842. JSAtomListElement *
  843. js_IndexAtom(JSContext *cx, JSAtom *atom, JSAtomList *al)
  844. {
  845. JSAtomListElement *ale, *ale2, *next;
  846. JSHashEntry **hep;
  847. ATOM_LIST_LOOKUP(ale, hep, al, atom);
  848. if (!ale) {
  849. if (al->count < 10) {
  850. /* Few enough for linear search, no hash table needed. */
  851. JS_ASSERT(!al->table);
  852. ale = (JSAtomListElement *)js_alloc_temp_entry(cx, atom);
  853. if (!ale)
  854. return NULL;
  855. ALE_SET_ATOM(ale, atom);
  856. ale->entry.next = al->list;
  857. al->list = &ale->entry;
  858. } else {
  859. /* We want to hash. Have we already made a hash table? */
  860. if (!al->table) {
  861. /* No hash table yet, so hep had better be null! */
  862. JS_ASSERT(!hep);
  863. al->table = JS_NewHashTable(al->count + 1, js_hash_atom_ptr,
  864. JS_CompareValues, JS_CompareValues,
  865. &temp_alloc_ops, cx);
  866. if (!al->table)
  867. return NULL;
  868. /*
  869. * Set ht->nentries explicitly, because we are moving entries
  870. * from al to ht, not calling JS_HashTable(Raw|)Add.
  871. */
  872. al->table->nentries = al->count;
  873. /* Insert each ale on al->list into the new hash table. */
  874. for (ale2 = (JSAtomListElement *)al->list; ale2; ale2 = next) {
  875. next = ALE_NEXT(ale2);
  876. ale2->entry.keyHash = ATOM_HASH(ALE_ATOM(ale2));
  877. hep = JS_HashTableRawLookup(al->table, ale2->entry.keyHash,
  878. ale2->entry.key);
  879. ale2->entry.next = *hep;
  880. *hep = &ale2->entry;
  881. }
  882. al->list = NULL;
  883. /* Set hep for insertion of atom's ale, immediately below. */
  884. hep = JS_HashTableRawLookup(al->table, ATOM_HASH(atom), atom);
  885. }
  886. /* Finally, add an entry for atom into the hash bucket at hep. */
  887. ale = (JSAtomListElement *)
  888. JS_HashTableRawAdd(al->table, hep, ATOM_HASH(atom), atom,
  889. NULL);
  890. if (!ale)
  891. return NULL;
  892. }
  893. ALE_SET_INDEX(ale, al->count++);
  894. }
  895. return ale;
  896. }
  897. static intN
  898. js_map_atom(JSHashEntry *he, intN i, void *arg)
  899. {
  900. JSAtomListElement *ale = (JSAtomListElement *)he;
  901. JSAtom **vector = (JSAtom **) arg;
  902. vector[ALE_INDEX(ale)] = ALE_ATOM(ale);
  903. return HT_ENUMERATE_NEXT;
  904. }
  905. #ifdef DEBUG
  906. static jsrefcount js_atom_map_count;
  907. static jsrefcount js_atom_map_hash_table_count;
  908. #endif
  909. void
  910. js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al)
  911. {
  912. JSAtom **vector;
  913. JSAtomListElement *ale;
  914. uint32 count;
  915. /* Map length must already be initialized. */
  916. JS_ASSERT(al->count == map->length);
  917. #ifdef DEBUG
  918. JS_ATOMIC_INCREMENT(&js_atom_map_count);
  919. #endif
  920. ale = (JSAtomListElement *)al->list;
  921. if (!ale && !al->table) {
  922. JS_ASSERT(!map->vector);
  923. return;
  924. }
  925. count = al->count;
  926. vector = map->vector;
  927. if (al->table) {
  928. #ifdef DEBUG
  929. JS_ATOMIC_INCREMENT(&js_atom_map_hash_table_count);
  930. #endif
  931. JS_HashTableEnumerateEntries(al->table, js_map_atom, vector);
  932. } else {
  933. do {
  934. vector[ALE_INDEX(ale)] = ALE_ATOM(ale);
  935. } while ((ale = ALE_NEXT(ale)) != NULL);
  936. }
  937. ATOM_LIST_INIT(al);
  938. }