static bool CloneValue(JSContext *cx, const Value &selfHostedValue, MutableHandleValue vp, CloneMemory &clonedObjects) { if (selfHostedValue.isObject()) { JSObject *selfHostedObject = &selfHostedValue.toObject(); RootedObject clone(cx, CloneObject(cx, selfHostedObject, clonedObjects)); if (!clone) return false; vp.setObject(*clone); } else if (selfHostedValue.isBoolean() || selfHostedValue.isNumber() || selfHostedValue.isNullOrUndefined()) { // Nothing to do here: these are represented inline in the value. vp.set(selfHostedValue); } else if (selfHostedValue.isString()) { if (!selfHostedValue.toString()->isFlat()) MOZ_CRASH(); JSFlatString *selfHostedString = &selfHostedValue.toString()->asFlat(); RootedString clone(cx, js_NewStringCopyN<CanGC>(cx, selfHostedString->chars(), selfHostedString->length())); if (!clone) return false; vp.setString(clone); } else { MOZ_ASSUME_UNREACHABLE("Self-hosting CloneValue can't clone given value."); } return true; }
/* * When the jschars reside in a freshly allocated buffer the memory can be used * as a new JSAtom's storage without copying. The contract is that the caller no * longer owns the memory and this method is responsible for freeing the memory. */ MOZ_ALWAYS_INLINE static JSAtom * AtomizeAndtake(ExclusiveContext *cx, jschar *tbchars, size_t length, InternBehavior ib) { JS_ASSERT(tbchars[length] == 0); if (JSAtom *s = cx->staticStrings().lookup(tbchars, length)) { js_free(tbchars); return s; } AtomHasher::Lookup lookup(tbchars, length); AtomSet::Ptr pp = cx->permanentAtoms().readonlyThreadsafeLookup(lookup); if (pp) { js_free(tbchars); return pp->asPtr(); } AutoLockForExclusiveAccess lock(cx); /* * If a GC occurs at js_NewStringCopy then |p| will still have the correct * hash, allowing us to avoid rehashing it. Even though the hash is * unchanged, we need to re-lookup the table position because a last-ditch * GC will potentially free some table entries. */ AtomSet& atoms = cx->atoms(); AtomSet::AddPtr p = atoms.lookupForAdd(lookup); if (p) { JSAtom *atom = p->asPtr(); p->setTagged(bool(ib)); js_free(tbchars); return atom; } AutoCompartment ac(cx, cx->atomsCompartment()); JSFlatString *flat = js_NewString<NoGC>(cx, tbchars, length); if (!flat) { js_free(tbchars); js_ReportOutOfMemory(cx); return nullptr; } JSAtom *atom = flat->morphAtomizedStringIntoAtom(); if (!atoms.relookupOrAdd(p, lookup, AtomStateEntry(atom, bool(ib)))) { js_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */ return nullptr; } return atom; }
MOZ_ALWAYS_INLINE static JSAtom * AtomizeAndCopyChars(ExclusiveContext *cx, const CharT *tbchars, size_t length, InternBehavior ib) { if (JSAtom *s = cx->staticStrings().lookup(tbchars, length)) return s; AtomHasher::Lookup lookup(tbchars, length); // Note: when this function is called while the permanent atoms table is // being initialized (in initializeAtoms()), |permanentAtoms| is not yet // initialized so this lookup is always skipped. Only once // transformToPermanentAtoms() is called does |permanentAtoms| get // initialized and then this lookup will go ahead. if (cx->isPermanentAtomsInitialized()) { AtomSet::Ptr pp = cx->permanentAtoms().readonlyThreadsafeLookup(lookup); if (pp) return pp->asPtr(); } AutoLockForExclusiveAccess lock(cx); AtomSet& atoms = cx->atoms(); AtomSet::AddPtr p = atoms.lookupForAdd(lookup); if (p) { JSAtom *atom = p->asPtr(); p->setTagged(bool(ib)); return atom; } AutoCompartment ac(cx, cx->atomsCompartment()); JSFlatString *flat = NewStringCopyN<NoGC>(cx, tbchars, length); if (!flat) { // Grudgingly forgo last-ditch GC. The alternative would be to release // the lock, manually GC here, and retry from the top. If you fix this, // please also fix or comment the similar case in Symbol::new_. ReportOutOfMemory(cx); return nullptr; } JSAtom *atom = flat->morphAtomizedStringIntoAtom(); // We have held the lock since looking up p, and the operations we've done // since then can't GC; therefore the atoms table has not been modified and // p is still valid. if (!atoms.add(p, AtomStateEntry(atom, bool(ib)))) { ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */ return nullptr; } return atom; }
/* * When the jschars reside in a freshly allocated buffer the memory can be used * as a new JSAtom's storage without copying. The contract is that the caller no * longer owns the memory and this method is responsible for freeing the memory. */ JS_ALWAYS_INLINE static JSAtom * AtomizeAndTakeOwnership(JSContext *cx, const jschar *tbchars, size_t length, InternBehavior ib) { JS_ASSERT(tbchars[length] == 0); if (JSAtom *s = cx->runtime->staticStrings.lookup(tbchars, length)) { js_free((void*)tbchars); return s; } /* * If a GC occurs at js_NewStringCopy then |p| will still have the correct * hash, allowing us to avoid rehashing it. Even though the hash is * unchanged, we need to re-lookup the table position because a last-ditch * GC will potentially free some table entries. */ AtomSet& atoms = cx->runtime->atoms; AtomSet::AddPtr p = atoms.lookupForAdd(AtomHasher::Lookup(tbchars, length)); SkipRoot skipHash(cx, &p); /* Prevent the hash from being poisoned. */ if (p) { JSAtom *atom = p->asPtr(); p->setTagged(bool(ib)); js_free((void*)tbchars); return atom; } AutoEnterAtomsCompartment ac(cx); JSFlatString *flat = js_NewString<CanGC>(cx, const_cast<jschar*>(tbchars), length); if (!flat) { js_free((void*)tbchars); return NULL; } JSAtom *atom = flat->morphAtomizedStringIntoAtom(); if (!atoms.relookupOrAdd(p, AtomHasher::Lookup(tbchars, length), AtomStateEntry(atom, bool(ib)))) { JS_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */ return NULL; } return atom; }
JS_ALWAYS_INLINE static JSAtom * AtomizeAndCopyChars(ExclusiveContext *cx, const jschar *tbchars, size_t length, InternBehavior ib) { if (JSAtom *s = cx->staticStrings().lookup(tbchars, length)) return s; /* * If a GC occurs at js_NewStringCopy then |p| will still have the correct * hash, allowing us to avoid rehashing it. Even though the hash is * unchanged, we need to re-lookup the table position because a last-ditch * GC will potentially free some table entries. */ AutoLockForExclusiveAccess lock(cx); AtomSet& atoms = cx->atoms(); AtomSet::AddPtr p = atoms.lookupForAdd(AtomHasher::Lookup(tbchars, length)); SkipRoot skipHash(cx, &p); /* Prevent the hash from being poisoned. */ if (p) { JSAtom *atom = p->asPtr(); p->setTagged(bool(ib)); return atom; } AutoCompartment ac(cx, cx->atomsCompartment()); JSFlatString *flat = js_NewStringCopyN<allowGC>(cx, tbchars, length); if (!flat) return NULL; JSAtom *atom = flat->morphAtomizedStringIntoAtom(); if (!atoms.relookupOrAdd(p, AtomHasher::Lookup(tbchars, length), AtomStateEntry(atom, bool(ib)))) { if (allowGC) js_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */ return NULL; } return atom; }
static JSFlatString* FinishStringFlat(ExclusiveContext* cx, StringBuffer& sb, Buffer& cb) { size_t len = sb.length(); if (!sb.append('\0')) return nullptr; ScopedJSFreePtr<CharT> buf(ExtractWellSized<CharT>(cx, cb)); if (!buf) return nullptr; JSFlatString* str = NewStringDontDeflate<CanGC>(cx, buf.get(), len); if (!str) return nullptr; /* * The allocation was made on a TempAllocPolicy, so account for the string * data on the string's zone. */ str->zone()->updateMallocCounter(sizeof(CharT) * len); buf.forget(); return str; }
bool StaticStrings::init(JSContext* cx) { AutoLockForExclusiveAccess lock(cx); AutoCompartment ac(cx, cx->runtime()->atomsCompartment()); static_assert(UNIT_STATIC_LIMIT - 1 <= JSString::MAX_LATIN1_CHAR, "Unit strings must fit in Latin1Char."); for (uint32_t i = 0; i < UNIT_STATIC_LIMIT; i++) { Latin1Char buffer[] = { Latin1Char(i), '\0' }; JSFlatString* s = NewStringCopyN<NoGC>(cx, buffer, 1); if (!s) return false; unitStaticTable[i] = s->morphAtomizedStringIntoPermanentAtom(); } for (uint32_t i = 0; i < NUM_SMALL_CHARS * NUM_SMALL_CHARS; i++) { Latin1Char buffer[] = { FROM_SMALL_CHAR(i >> 6), FROM_SMALL_CHAR(i & 0x3F), '\0' }; JSFlatString* s = NewStringCopyN<NoGC>(cx, buffer, 2); if (!s) return false; length2StaticTable[i] = s->morphAtomizedStringIntoPermanentAtom(); } for (uint32_t i = 0; i < INT_STATIC_LIMIT; i++) { if (i < 10) { intStaticTable[i] = unitStaticTable[i + '0']; } else if (i < 100) { size_t index = ((size_t)TO_SMALL_CHAR((i / 10) + '0') << 6) + TO_SMALL_CHAR((i % 10) + '0'); intStaticTable[i] = length2StaticTable[index]; } else { Latin1Char buffer[] = { Latin1Char('0' + (i / 100)), Latin1Char('0' + ((i / 10) % 10)), Latin1Char('0' + (i % 10)), '\0' }; JSFlatString* s = NewStringCopyN<NoGC>(cx, buffer, 3); if (!s) return false; intStaticTable[i] = s->morphAtomizedStringIntoPermanentAtom(); } } return true; }
JSObject* Library::Create(JSContext* cx, jsval path, JSCTypesCallbacks* callbacks) { JSObject* libraryObj = JS_NewObject(cx, &sLibraryClass, NULL, NULL); if (!libraryObj) return NULL; js::AutoObjectRooter root(cx, libraryObj); // initialize the library if (!JS_SetReservedSlot(cx, libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(NULL))) return NULL; // attach API functions if (!JS_DefineFunctions(cx, libraryObj, sLibraryFunctions)) return NULL; if (!JSVAL_IS_STRING(path)) { JS_ReportError(cx, "open takes a string argument"); return NULL; } PRLibSpec libSpec; JSFlatString* pathStr = JS_FlattenString(cx, JSVAL_TO_STRING(path)); if (!pathStr) return NULL; #ifdef XP_WIN // On Windows, converting to native charset may corrupt path string. // So, we have to use Unicode path directly. const PRUnichar* pathChars = JS_GetFlatStringChars(pathStr); if (!pathChars) return NULL; libSpec.value.pathname_u = pathChars; libSpec.type = PR_LibSpec_PathnameU; #else // Convert to platform native charset if the appropriate callback has been // provided. char* pathBytes; if (callbacks && callbacks->unicodeToNative) { pathBytes = callbacks->unicodeToNative(cx, pathStr->chars(), pathStr->length()); if (!pathBytes) return NULL; } else { // Fallback: assume the platform native charset is UTF-8. This is true // for Mac OS X, Android, and probably Linux. size_t nbytes = js_GetDeflatedUTF8StringLength(cx, pathStr->chars(), pathStr->length()); if (nbytes == (size_t) -1) return NULL; pathBytes = static_cast<char*>(JS_malloc(cx, nbytes + 1)); if (!pathBytes) return NULL; ASSERT_OK(js_DeflateStringToUTF8Buffer(cx, pathStr->chars(), pathStr->length(), pathBytes, &nbytes)); pathBytes[nbytes] = 0; } libSpec.value.pathname = pathBytes; libSpec.type = PR_LibSpec_Pathname; #endif PRLibrary* library = PR_LoadLibraryWithFlags(libSpec, 0); #ifndef XP_WIN JS_free(cx, pathBytes); #endif if (!library) { JS_ReportError(cx, "couldn't open library"); return NULL; } // stash the library if (!JS_SetReservedSlot(cx, libraryObj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(library))) return NULL; return libraryObj; }