JSAtom * js_AtomizeDouble(JSContext *cx, jsdouble d, uintN flags) { jsdouble *dp; JSHashNumber keyHash; jsval key; JSAtomState *state; JSHashTable *table; JSHashEntry *he, **hep; JSAtom *atom; char buf[2 * ALIGNMENT(double)]; dp = ALIGN(buf, double); *dp = d; keyHash = HASH_DOUBLE(dp); key = DOUBLE_TO_JSVAL(dp); state = &cx->runtime->atomState; JS_LOCK(&state->lock, cx); table = state->table; hep = JS_HashTableRawLookup(table, keyHash, (void *)key); if ((he = *hep) == NULL) { #ifdef JS_THREADSAFE uint32 gen = state->tablegen; #endif JS_UNLOCK(&state->lock,cx); if (!js_NewDoubleValue(cx, d, &key)) return NULL; JS_LOCK(&state->lock, cx); #ifdef JS_THREADSAFE if (state->tablegen != gen) { hep = JS_HashTableRawLookup(table, keyHash, (void *)key); if ((he = *hep) != NULL) { atom = (JSAtom *)he; goto out; } } #endif he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL); if (!he) { JS_ReportOutOfMemory(cx); atom = NULL; goto out; } } atom = (JSAtom *)he; atom->flags |= flags; cx->lastAtom = atom; out: JS_UNLOCK(&state->lock,cx); return atom; }
JSAtom * js_AtomizeDouble(JSContext *cx, jsdouble d) { JSAtomState *state; JSDHashTable *table; JSAtomHashEntry *entry; uint32 gen; jsdouble *key; jsval v; state = &cx->runtime->atomState; table = &state->doubleAtoms; JS_LOCK(cx, &state->lock); entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, &d, JS_DHASH_ADD)); if (!entry) goto failed_hash_add; if (entry->keyAndFlags == 0) { gen = ++table->generation; JS_UNLOCK(cx, &state->lock); key = js_NewWeaklyRootedDouble(cx, d); if (!key) return NULL; JS_LOCK(cx, &state->lock); if (table->generation == gen) { JS_ASSERT(entry->keyAndFlags == 0); } else { entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, key, JS_DHASH_ADD)); if (!entry) goto failed_hash_add; if (entry->keyAndFlags != 0) goto finish; ++table->generation; } INIT_ATOM_ENTRY(entry, key); } finish: v = DOUBLE_TO_JSVAL((jsdouble *)ATOM_ENTRY_KEY(entry)); cx->weakRoots.lastAtom = v; JS_UNLOCK(cx, &state->lock); return (JSAtom *)v; failed_hash_add: JS_UNLOCK(cx, &state->lock); JS_ReportOutOfMemory(cx); return NULL; }
JSAtom * js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length) { JSString str, *str2; JSAtomState *state; JSDHashEntryHdr *hdr; if (length == 1) { jschar c = *chars; if (c < UNIT_STRING_LIMIT) return (JSAtom *) STRING_TO_JSVAL(JSString::unitString(c)); } str.initFlat((jschar *)chars, length); state = &cx->runtime->atomState; JS_LOCK(cx, &state->lock); hdr = JS_DHashTableOperate(&state->stringAtoms, &str, JS_DHASH_LOOKUP); str2 = JS_DHASH_ENTRY_IS_BUSY(hdr) ? (JSString *)ATOM_ENTRY_KEY(TO_ATOM_ENTRY(hdr)) : NULL; JS_UNLOCK(cx, &state->lock); return str2 ? (JSAtom *)STRING_TO_JSVAL(str2) : NULL; }
static JSAtom * js_AtomizeHashedKey(JSContext *cx, jsval key, JSHashNumber keyHash, uintN flags) { JSAtomState *state; JSHashTable *table; JSHashEntry *he, **hep; JSAtom *atom; state = &cx->runtime->atomState; JS_LOCK(&state->lock, cx); table = state->table; hep = JS_HashTableRawLookup(table, keyHash, (void *)key); if ((he = *hep) == NULL) { he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL); if (!he) { JS_ReportOutOfMemory(cx); atom = NULL; goto out; } } atom = (JSAtom *)he; atom->flags |= flags; out: JS_UNLOCK(&state->lock,cx); return atom; }
JSBool js_SetClassPrototype(JSContext *cx, JSFunction *fun, JSObject *obj) { JSBool ok; ok = JS_TRUE; JS_LOCK(cx); if (!js_DefineProperty(cx, fun->object, (jsval)cx->runtime->atomState.classPrototypeAtom, OBJECT_TO_JSVAL(obj), NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT)) { ok = JS_FALSE; goto out; } if (!js_DefineProperty(cx, obj, (jsval)cx->runtime->atomState.constructorAtom, OBJECT_TO_JSVAL(fun->object), NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT)) { ok = JS_FALSE; goto out; } out: JS_UNLOCK(cx); return ok; }
JSBool js_GetClassPrototype(JSContext *cx, JSClass *clasp, JSObject **protop) { JSBool ok; JSObject *ctor; jsval pval; JSProperty *prop; *protop = NULL; JS_LOCK(cx); ok = FindConstructor(cx, clasp, &pval); if (!ok || !JSVAL_IS_FUNCTION(pval)) goto out; ctor = JSVAL_TO_OBJECT(pval); if (!js_LookupProperty(cx, ctor, (jsval)cx->runtime->atomState.classPrototypeAtom, NULL, &prop)) { ok = JS_FALSE; goto out; } if (prop) { pval = prop->object->slots[prop->slot]; if (JSVAL_IS_OBJECT(pval)) *protop = JSVAL_TO_OBJECT(pval); } out: JS_UNLOCK(cx); return ok; }
static JSBool obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { jsint count; JSProperty *prop; count = 0; JS_LOCK(cx); for (prop = obj->map->props; prop; prop = prop->next) if (prop->flags & JSPROP_ENUMERATE) count++; JS_UNLOCK(cx); *vp = INT_TO_JSVAL(count); return JS_TRUE; }
obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSObject *obj2; jsint slot; JSBool ok; if (!JSVAL_IS_OBJECT(*vp)) return JS_TRUE; obj2 = JSVAL_TO_OBJECT(*vp); slot = JSVAL_TO_INT(id); JS_LOCK(cx); while (obj2) { if (obj2 == obj) { JS_ReportError(cx, "cyclic %s value", object_props[slot].name); ok = JS_FALSE; goto out; } obj2 = JSVAL_TO_OBJECT(js_GetSlot(cx, obj2, slot)); } ok = js_SetSlot(cx, obj, slot, *vp); out: JS_UNLOCK(cx); return ok; }
JSAtom * js_AtomizeString(JSContext *cx, JSString *str, uintN flags) { jsval v; JSAtomState *state; JSDHashTable *table; JSAtomHashEntry *entry; JSString *key; uint32 gen; JS_ASSERT(!(flags & ~(ATOM_PINNED|ATOM_INTERNED|ATOM_TMPSTR|ATOM_NOCOPY))); JS_ASSERT_IF(flags & ATOM_NOCOPY, flags & ATOM_TMPSTR); if (str->isAtomized()) return (JSAtom *) STRING_TO_JSVAL(str); size_t length = str->length(); if (length == 1) { jschar c = str->chars()[0]; if (c < UNIT_STRING_LIMIT) return (JSAtom *) STRING_TO_JSVAL(JSString::unitString(c)); } /* * Here we know that JSString::intStringTable covers only 256 (or at least * not 1000 or more) chars. We rely on order here to resolve the unit vs. * int string atom identity issue by giving priority to unit strings for * '0' through '9' (see JSString::intString in jsstrinlines.h). */ JS_STATIC_ASSERT(INT_STRING_LIMIT <= 999); if (2 <= length && length <= 3) { const jschar *chars = str->chars(); if ('1' <= chars[0] && chars[0] <= '9' && '0' <= chars[1] && chars[1] <= '9' && (length == 2 || ('0' <= chars[2] && chars[2] <= '9'))) { jsint i = (chars[0] - '0') * 10 + chars[1] - '0'; if (length == 3) i = i * 10 + chars[2] - '0'; if (jsuint(i) < INT_STRING_LIMIT) return (JSAtom *) STRING_TO_JSVAL(JSString::intString(i)); } } state = &cx->runtime->atomState; table = &state->stringAtoms; JS_LOCK(cx, &state->lock); entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, str, JS_DHASH_ADD)); if (!entry) goto failed_hash_add; if (entry->keyAndFlags != 0) { key = (JSString *)ATOM_ENTRY_KEY(entry); } else { /* * We created a new hashtable entry. Unless str is already allocated * from the GC heap and flat, we have to release state->lock as * string construction is a complex operation. For example, it can * trigger GC which may rehash the table and make the entry invalid. */ ++table->generation; if (!(flags & ATOM_TMPSTR) && str->isFlat()) { str->flatClearMutable(); key = str; } else { gen = table->generation; JS_UNLOCK(cx, &state->lock); if (flags & ATOM_TMPSTR) { if (flags & ATOM_NOCOPY) { key = js_NewString(cx, str->flatChars(), str->flatLength()); if (!key) return NULL; /* Finish handing off chars to the GC'ed key string. */ str->mChars = NULL; } else { key = js_NewStringCopyN(cx, str->flatChars(), str->flatLength()); if (!key) return NULL; } } else { JS_ASSERT(str->isDependent()); if (!js_UndependString(cx, str)) return NULL; key = str; } JS_LOCK(cx, &state->lock); if (table->generation == gen) { JS_ASSERT(entry->keyAndFlags == 0); } else { entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, key, JS_DHASH_ADD)); if (!entry) goto failed_hash_add; if (entry->keyAndFlags != 0) { key = (JSString *)ATOM_ENTRY_KEY(entry); goto finish; } ++table->generation; } } INIT_ATOM_ENTRY(entry, key); key->flatSetAtomized(); } finish: ADD_ATOM_ENTRY_FLAGS(entry, flags & (ATOM_PINNED | ATOM_INTERNED)); JS_ASSERT(key->isAtomized()); v = STRING_TO_JSVAL(key); cx->weakRoots.lastAtom = v; JS_UNLOCK(cx, &state->lock); return (JSAtom *)v; failed_hash_add: JS_UNLOCK(cx, &state->lock); JS_ReportOutOfMemory(cx); return NULL; }
JSAtom * js_AtomizeString(JSContext *cx, JSString *str, uintN flags) { JSHashNumber keyHash; jsval key; JSAtomState *state; JSHashTable *table; JSHashEntry *he, **hep; JSAtom *atom; keyHash = js_HashString(str); key = STRING_TO_JSVAL(str); state = &cx->runtime->atomState; JS_LOCK(&state->lock, cx); table = state->table; hep = JS_HashTableRawLookup(table, keyHash, (void *)key); if ((he = *hep) == NULL) { #ifdef JS_THREADSAFE uint32 gen = state->tablegen; JS_UNLOCK(&state->lock, cx); #endif if (flags & ATOM_TMPSTR) { str = (flags & ATOM_NOCOPY) ? js_NewString(cx, str->chars, str->length, 0) : js_NewStringCopyN(cx, str->chars, str->length, 0); if (!str) return NULL; key = STRING_TO_JSVAL(str); } else { if (!JS_MakeStringImmutable(cx, str)) return NULL; } #ifdef JS_THREADSAFE JS_LOCK(&state->lock, cx); if (state->tablegen != gen) { hep = JS_HashTableRawLookup(table, keyHash, (void *)key); if ((he = *hep) != NULL) { atom = (JSAtom *)he; if (flags & ATOM_NOCOPY) str->chars = NULL; goto out; } } #endif he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL); if (!he) { JS_ReportOutOfMemory(cx); atom = NULL; goto out; } } atom = (JSAtom *)he; atom->flags |= flags & (ATOM_PINNED | ATOM_INTERNED); out: JS_UNLOCK(&state->lock,cx); return atom; }
JSBool js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jschar *chars; size_t nchars; char *clazz, *prefix; JSString *str; #if JS_HAS_OBJECT_LITERAL if (cx->version >= JSVERSION_1_2) { JSProperty *list, *prop; JSBool ok; char *comma, *idquote, *valquote; jsval id, val; JSString *idstr, *valstr; #if JS_HAS_SHARP_VARS PRHashEntry *he; #endif /* Early returns after this must unlock, or goto done with ok set. */ JS_LOCK(cx); list = obj->map->props; #if JS_HAS_SHARP_VARS he = js_EnterSharpObject(cx, obj, &chars); if (!he) { JS_UNLOCK(cx); return JS_FALSE; } if (IS_SHARP(he)) { /* we didn't enter -- obj is already sharp */ JS_UNLOCK(cx); nchars = js_strlen(chars); goto make_string; } MAKE_SHARP(he); #else /* Shut off recursion by temporarily clearing obj's property list. */ obj->map->props = NULL; chars = NULL; #endif ok = JS_TRUE; /* Allocate 2 + 1 for "{}" and the terminator. */ if (!chars) { chars = malloc((2 + 1) * sizeof(jschar)); nchars = 0; } else { nchars = js_strlen(chars); chars = realloc(chars, (nchars + 2 + 1) * sizeof(jschar)); } if (!chars) goto done; chars[nchars++] = '{'; comma = NULL; for (prop = list; prop; prop = prop->next) { if (!prop->symbols || !(prop->flags & JSPROP_ENUMERATE)) continue; /* Get strings for id and val and GC-root them via argv. */ id = js_IdToValue(sym_id(prop->symbols)); idstr = js_ValueToString(cx, id); if (idstr) argv[0] = STRING_TO_JSVAL(idstr); val = prop->object->slots[prop->slot]; valstr = js_ValueToString(cx, val); if (!idstr || !valstr) { ok = JS_FALSE; goto done; } argv[1] = STRING_TO_JSVAL(valstr); /* If id is a non-identifier string, it needs to be quoted. */ if (JSVAL_IS_STRING(id) && !js_IsIdentifier(idstr)) { idquote = "'"; idstr = js_EscapeString(cx, idstr, *idquote); if (!idstr) { ok = JS_FALSE; goto done; } argv[0] = STRING_TO_JSVAL(idstr); } else { idquote = NULL; } /* Same for val, except it can't be an identifier. */ if (JSVAL_IS_STRING(val)) { valquote = "\""; valstr = js_EscapeString(cx, valstr, *valquote); if (!valstr) { ok = JS_FALSE; goto done; } argv[1] = STRING_TO_JSVAL(valstr); } else { valquote = NULL; } /* Allocate 1 + 1 at end for closing brace and terminating 0. */ chars = realloc(chars, (nchars + (comma ? 2 : 0) + (idquote ? 2 : 0) + idstr->length + 1 + (valquote ? 2 : 0) + valstr->length + 1 + 1) * sizeof(jschar)); if (!chars) goto done; if (comma) { chars[nchars++] = comma[0]; chars[nchars++] = comma[1]; } comma = ", "; if (idquote) chars[nchars++] = *idquote; js_strncpy(&chars[nchars], idstr->chars, idstr->length); nchars += idstr->length; if (idquote) chars[nchars++] = *idquote; chars[nchars++] = ':'; if (valquote) chars[nchars++] = *valquote; js_strncpy(&chars[nchars], valstr->chars, valstr->length); nchars += valstr->length; if (valquote) chars[nchars++] = *valquote; } done: if (chars) { chars[nchars++] = '}'; chars[nchars] = 0; } #if JS_HAS_SHARP_VARS js_LeaveSharpObject(cx); #else /* Restore obj's property list before bailing on error. */ obj->map->props = list; #endif JS_UNLOCK(cx); if (!ok) { if (chars) free(chars); return ok; } } else #endif /* JS_HAS_OBJECT_LITERAL */ { clazz = obj->map->clasp->name; nchars = 9 + strlen(clazz); /* 9 for "[object ]" */ chars = malloc((nchars + 1) * sizeof(jschar)); if (chars) { prefix = "[object "; nchars = 0; while ((chars[nchars] = (jschar)*prefix) != 0) nchars++, prefix++; while ((chars[nchars] = (jschar)*clazz) != 0) nchars++, clazz++; chars[nchars++] = ']'; chars[nchars] = 0; } } #if JS_HAS_SHARP_VARS make_string: #endif if (!chars) { JS_ReportOutOfMemory(cx); return JS_FALSE; } str = js_NewString(cx, chars, nchars, 0); if (!str) { free(chars); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; }