static JSBool HandleString(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len) { JSObject *obj = GetTopOfObjectStack(cx, jp); JSString *str = js_NewStringCopyN(cx, buf, len); if (!obj || !str) return JS_FALSE; return PushValue(cx, jp, obj, STRING_TO_JSVAL(str)); }
JSAtom * js_AtomizeString(JSContext *cx, JSString *str, uintN flags) { PRHashTable *table; PRHashNumber keyHash; jsval key; PRHashEntry *he, **hep; JSAtom *atom; PR_ASSERT(JS_IS_LOCKED(cx)); table = cx->runtime->atomState.table; keyHash = js_HashString(str); key = STRING_TO_JSVAL(str); hep = PR_HashTableRawLookup(table, keyHash, (void *)key); if ((he = *hep) == NULL) { if (flags & ATOM_TMPSTR) { if (flags & ATOM_NOCOPY) str = js_NewString(cx, str->chars, str->length, 0); else str = js_NewStringCopyN(cx, str->chars, str->length, 0); if (!str) return NULL; key = STRING_TO_JSVAL(str); } he = PR_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL); if (!he) { JS_ReportOutOfMemory(cx); return NULL; } } atom = (JSAtom *)he; #ifdef DEBUG_DUPLICATE_ATOMS hep = PR_HashTableRawLookup(table, keyHash, (void *)key); he = *hep; PR_ASSERT(atom == (JSAtom *)he); #endif if (flags & ATOM_NOHOLD) return atom; return js_HoldAtom(cx, atom); }
static JSBool WriteCallback(const jschar *buf, uint32 len, void *data) { StringifyClosure *sc = static_cast<StringifyClosure*>(data); JSString *s1 = JSVAL_TO_STRING(sc->s[0]); JSString *s2 = js_NewStringCopyN(sc->cx, buf, len); if (!s2) return JS_FALSE; sc->s[1] = STRING_TO_JSVAL(s2); s1 = js_ConcatStrings(sc->cx, s1, s2); if (!s1) return JS_FALSE; sc->s[0] = STRING_TO_JSVAL(s1); sc->s[1] = JSVAL_VOID; return JS_TRUE; }
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; }
template<size_t N> JSFlatString * NewString(JSContext *cx, const jschar (&chars)[N]) { return js_NewStringCopyN(cx, chars, N); }
JSBool js_ReportCompileErrorNumber(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg, uintN flags, const uintN errorNumber, ...) { va_list ap; JSErrorReporter onError; JSErrorReport report; jschar *tokenptr; JSString *linestr = NULL; char *message; JSBool warning; if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx)) return JS_TRUE; memset(&report, 0, sizeof (struct JSErrorReport)); report.flags = flags; report.errorNumber = errorNumber; message = NULL; va_start(ap, errorNumber); if (!js_ExpandErrorArguments(cx, js_GetErrorMessage, NULL, errorNumber, &message, &report, &warning, JS_TRUE, ap)) { return JS_FALSE; } va_end(ap); js_AddRoot(cx, &linestr, "error line buffer"); JS_ASSERT(!ts || ts->linebuf.limit < ts->linebuf.base + JS_LINE_LIMIT); onError = cx->errorReporter; if (onError) { /* * We are typically called with non-null ts and null cg from jsparse.c. * We can be called with null ts from the regexp compilation functions. * The code generator (jsemit.c) may pass null ts and non-null cg. */ if (ts) { report.filename = ts->filename; report.lineno = ts->lineno; linestr = js_NewStringCopyN(cx, ts->linebuf.base, ts->linebuf.limit - ts->linebuf.base, 0); report.linebuf = linestr ? JS_GetStringBytes(linestr) : NULL; tokenptr = ts->tokens[(ts->cursor + ts->lookahead) & NTOKENS_MASK].ptr; report.tokenptr = linestr ? report.linebuf + (tokenptr - ts->linebuf.base) : NULL; report.uclinebuf = linestr ? JS_GetStringChars(linestr) : NULL; report.uctokenptr = linestr ? report.uclinebuf + (tokenptr - ts->linebuf.base) : NULL; } else if (cg) { report.filename = cg->filename; report.lineno = cg->currentLine; } #if JS_HAS_ERROR_EXCEPTIONS /* * If there's a runtime exception type associated with this error * number, set that as the pending exception. For errors occuring at * compile time, this is very likely to be a JSEXN_SYNTAXERR. * * If an exception is thrown but not caught, the JSREPORT_EXCEPTION * flag will be set in report.flags. Proper behavior for an error * reporter is to ignore a report with this flag for all but top-level * compilation errors. The exception will remain pending, and so long * as the non-top-level "load", "eval", or "compile" native function * returns false, the top-level reporter will eventually receive the * uncaught exception report. * * XXX it'd probably be best if there was only one call to this * function, but there seem to be two error reporter call points. */ /* * Only try to raise an exception if there isn't one already set - * otherwise the exception will describe only the last compile error, * which is likely spurious. */ if (!(ts && (ts->flags & TSF_ERROR))) if (js_ErrorToException(cx, message, &report)) onError = NULL; /* * Suppress any compiletime errors that don't occur at the top level. * This may still fail, as interplevel may be zero in contexts where we * don't really want to call the error reporter, as when js is called * by other code which could catch the error. */ if (cx->interpLevel != 0) onError = NULL; #endif if (cx->runtime->debugErrorHook && onError) { JSDebugErrorHook hook = cx->runtime->debugErrorHook; /* test local in case debugErrorHook changed on another thread */ if (hook && !hook(cx, message, &report, cx->runtime->debugErrorHookData)) { onError = NULL; } } if (onError) (*onError)(cx, message, &report); } if (message) JS_free(cx, message); if (report.messageArgs) { int i = 0; while (report.messageArgs[i]) JS_free(cx, (void *)report.messageArgs[i++]); JS_free(cx, (void *)report.messageArgs); } if (report.ucmessage) JS_free(cx, (void *)report.ucmessage); js_RemoveRoot(cx->runtime, &linestr); if (ts && !JSREPORT_IS_WARNING(flags)) { /* Set the error flag to suppress spurious reports. */ ts->flags |= TSF_ERROR; } return warning; }