/* * Convert string indexes that convert to int jsvals as ints to save memory. * Care must be taken to use this macro every time a property name is used, or * else double-sets, incorrect property cache misses, or other mistakes could * occur. */ jsid js_CheckForStringIndex(jsid id) { if (!JSID_IS_ATOM(id)) return id; JSAtom *atom = JSID_TO_ATOM(id); const jschar *s = atom->chars(); jschar ch = *s; JSBool negative = (ch == '-'); if (negative) ch = *++s; if (!JS7_ISDEC(ch)) return id; size_t n = atom->length() - negative; if (n > sizeof(JSBOXEDWORD_INT_MAX_STRING) - 1) return id; const jschar *cp = s; const jschar *end = s + n; jsuint index = JS7_UNDEC(*cp++); jsuint oldIndex = 0; jsuint c = 0; if (index != 0) { while (JS7_ISDEC(*cp)) { oldIndex = index; c = JS7_UNDEC(*cp); index = 10 * index + c; cp++; } } /* * Non-integer indexes can't be represented as integers. Also, distinguish * index "-0" from "0", because JSBOXEDWORD_INT cannot. */ if (cp != end || (negative && index == 0)) return id; if (negative) { if (oldIndex < -(JSID_INT_MIN / 10) || (oldIndex == -(JSID_INT_MIN / 10) && c <= (-JSID_INT_MIN % 10))) { id = INT_TO_JSID(-jsint(index)); } } else { if (oldIndex < JSID_INT_MAX / 10 || (oldIndex == JSID_INT_MAX / 10 && c <= (JSID_INT_MAX % 10))) { id = INT_TO_JSID(jsint(index)); } } return id; }
Symbol* Symbol::for_(js::ExclusiveContext* cx, HandleString description) { JSAtom* atom = AtomizeString(cx, description); if (!atom) return nullptr; AutoLockForExclusiveAccess lock(cx); SymbolRegistry& registry = cx->symbolRegistry(lock); SymbolRegistry::AddPtr p = registry.lookupForAdd(atom); if (p) return *p; AutoCompartment ac(cx, cx->atomsCompartment(lock), &lock); Symbol* sym = newInternal(cx, SymbolCode::InSymbolRegistry, atom->hash(), atom, lock); if (!sym) return nullptr; // p is still valid here because we have held the lock since the // lookupForAdd call, and newInternal can't GC. if (!registry.add(p, sym)) { // SystemAllocPolicy does not report OOM. ReportOutOfMemory(cx); return nullptr; } return sym; }
void GetDynamicName(JSContext *cx, JSObject *scopeChain, JSString *str, Value *vp) { // Lookup a string on the scope chain, returning either the value found or // undefined through rval. This function is infallible, and cannot GC or // invalidate. JSAtom *atom; if (str->isAtom()) { atom = &str->asAtom(); } else { atom = AtomizeString(cx, str); if (!atom) { vp->setUndefined(); return; } } if (!frontend::IsIdentifier(atom) || frontend::IsKeyword(atom)) { vp->setUndefined(); return; } Shape *shape = nullptr; JSObject *scope = nullptr, *pobj = nullptr; if (LookupNameNoGC(cx, atom->asPropertyName(), scopeChain, &scope, &pobj, &shape)) { if (FetchNameNoGC(pobj, shape, MutableHandleValue::fromMarkedLocation(vp))) return; } vp->setUndefined(); }
JSFlatString * RegExpObject::toString(JSContext *cx) const { JSAtom *src = getSource(); StringBuffer sb(cx); if (size_t len = src->length()) { if (!sb.reserve(len + 2)) return NULL; sb.infallibleAppend('/'); sb.infallibleAppend(src->chars(), len); sb.infallibleAppend('/'); } else { if (!sb.append("/(?:)/")) return NULL; } if (global() && !sb.append('g')) return NULL; if (ignoreCase() && !sb.append('i')) return NULL; if (multiline() && !sb.append('m')) return NULL; if (sticky() && !sb.append('y')) return NULL; return sb.finishString(); }
/* static */ bool SavedFrame::HashPolicy::match(SavedFrame *existing, const Lookup &lookup) { if (existing->getLine() != lookup.line) return false; if (existing->getColumn() != lookup.column) return false; if (existing->getParent() != lookup.parent) return false; if (existing->getPrincipals() != lookup.principals) return false; JSAtom *source = existing->getSource(); if (source->length() != lookup.source->length()) return false; if (source != lookup.source) return false; JSAtom *functionDisplayName = existing->getFunctionDisplayName(); if (functionDisplayName) { if (!lookup.functionDisplayName) return false; if (functionDisplayName->length() != lookup.functionDisplayName->length()) return false; if (0 != CompareAtoms(functionDisplayName, lookup.functionDisplayName)) return false; } else if (lookup.functionDisplayName) { return false; } return true; }
/* * Serializes the script/function pair into a "descriptive string" which is * allowed to fail. This function cannot trigger a GC because it could finalize * some scripts, resize the hash table of profile strings, and invalidate the * AddPtr held while invoking allocProfileString. */ const char* SPSProfiler::allocProfileString(JSScript* script, JSFunction* maybeFun) { // Note: this profiler string is regexp-matched by // browser/devtools/profiler/cleopatra/js/parserWorker.js. // Get the function name, if any. JSAtom* atom = maybeFun ? maybeFun->displayAtom() : nullptr; // Get the script filename, if any, and its length. const char* filename = script->filename(); if (filename == nullptr) filename = "<unknown>"; size_t lenFilename = strlen(filename); // Get the line number and its length as a string. uint64_t lineno = script->lineno(); size_t lenLineno = 1; for (uint64_t i = lineno; i /= 10; lenLineno++); // Determine the required buffer size. size_t len = lenFilename + lenLineno + 1; // +1 for the ":" separating them. if (atom) { len += JS::GetDeflatedUTF8StringLength(atom) + 3; // +3 for the " (" and ")" it adds. } // Allocate the buffer. char* cstr = js_pod_malloc<char>(len + 1); if (cstr == nullptr) return nullptr; // Construct the descriptive string. DebugOnly<size_t> ret; if (atom) { JS::AutoCheckCannotGC nogc; auto atomStr = mozilla::UniquePtr<char, JS::FreePolicy>( atom->hasLatin1Chars() ? JS::CharsToNewUTF8CharsZ(nullptr, atom->latin1Range(nogc)).c_str() : JS::CharsToNewUTF8CharsZ(nullptr, atom->twoByteRange(nogc)).c_str()); if (!atomStr) return nullptr; ret = JS_snprintf(cstr, len + 1, "%s (%s:%llu)", atomStr.get(), filename, lineno); } else { ret = JS_snprintf(cstr, len + 1, "%s:%llu", filename, lineno); } MOZ_ASSERT(ret == len, "Computed length should match actual length!"); return cstr; }
/* static */ bool ArgumentsObject::obj_mayResolve(const JSAtomState& names, jsid id, JSObject*) { // Arguments might resolve indexes, Symbol.iterator, or length/callee. if (JSID_IS_ATOM(id)) { JSAtom* atom = JSID_TO_ATOM(id); uint32_t index; if (atom->isIndex(&index)) return true; return atom == names.length || atom == names.callee; } if (JSID_IS_SYMBOL(id)) return JSID_TO_SYMBOL(id)->code() == JS::SymbolCode::iterator; return true; }
bool JSRuntime::initializeAtoms(JSContext *cx) { atoms_ = cx->new_<AtomSet>(); if (!atoms_ || !atoms_->init(JS_STRING_HASH_COUNT)) return false; if (parentRuntime) { staticStrings = parentRuntime->staticStrings; commonNames = parentRuntime->commonNames; emptyString = parentRuntime->emptyString; permanentAtoms = parentRuntime->permanentAtoms; return true; } permanentAtoms = cx->new_<AtomSet>(); if (!permanentAtoms || !permanentAtoms->init(JS_STRING_HASH_COUNT)) return false; staticStrings = cx->new_<StaticStrings>(); if (!staticStrings || !staticStrings->init(cx)) return false; static const CommonNameInfo cachedNames[] = { #define COMMON_NAME_INFO(idpart, id, text) { js_##idpart##_str, sizeof(text) - 1 }, FOR_EACH_COMMON_PROPERTYNAME(COMMON_NAME_INFO) #undef COMMON_NAME_INFO #define COMMON_NAME_INFO(name, code, init, clasp) { js_##name##_str, sizeof(#name) - 1 }, JS_FOR_EACH_PROTOTYPE(COMMON_NAME_INFO) #undef COMMON_NAME_INFO }; commonNames = cx->new_<JSAtomState>(); if (!commonNames) return false; FixedHeapPtr<PropertyName> *names = reinterpret_cast<FixedHeapPtr<PropertyName> *>(commonNames); for (size_t i = 0; i < ArrayLength(cachedNames); i++, names++) { JSAtom *atom = Atomize(cx, cachedNames[i].str, cachedNames[i].length, InternAtom); if (!atom) return false; names->init(atom->asPropertyName()); } JS_ASSERT(uintptr_t(names) == uintptr_t(commonNames + 1)); emptyString = commonNames->empty; return true; }
uint32_t GetIndexFromString(JSString *str) { // Masks the return value UINT32_MAX as failure to get the index. // I.e. it is impossible to distinguish between failing to get the index // or the actual index UINT32_MAX. if (!str->isAtom()) return UINT32_MAX; uint32_t index; JSAtom *atom = &str->asAtom(); if (!atom->isIndex(&index)) return UINT32_MAX; return index; }
bool js_InitCommonAtoms(JSContext *cx) { JSAtomState *state = &cx->runtime->atomState; JSAtom **atoms = state->commonAtomsStart(); for (size_t i = 0; i < ArrayLength(js_common_atom_names); i++, atoms++) { JSAtom *atom = js_Atomize(cx, js_common_atom_names[i], strlen(js_common_atom_names[i]), InternAtom); if (!atom) return false; *atoms = atom->asPropertyName(); } state->clearLazyAtoms(); cx->runtime->emptyString = state->emptyAtom; return true; }
void js::gc::TraceTypeNewScript(ObjectGroup *group) { const size_t bufLength = 128; static char buffer[bufLength]; MOZ_ASSERT(group->hasNewScript()); JSAtom *funName = group->newScript()->fun->displayAtom(); if (!funName) return; size_t length = funName->length(); MOZ_ALWAYS_TRUE(length < bufLength); CopyChars(reinterpret_cast<Latin1Char *>(buffer), *funName); buffer[length] = 0; TraceEvent(TraceEventTypeNewScript, uint64_t(group)); TraceString(buffer); }
const StructField * StructTypeRepresentation::fieldNamed(jsid id) const { if (!JSID_IS_ATOM(id)) return nullptr; uint32_t unused; JSAtom *atom = JSID_TO_ATOM(id); if (atom->isIndex(&unused)) return nullptr; PropertyName *name = atom->asPropertyName(); for (size_t i = 0; i < fieldCount(); i++) { if (field(i).propertyName.get() == name) return &field(i); } return nullptr; }
bool JSRuntime::transformToPermanentAtoms() { JS_ASSERT(!parentRuntime); // All static strings were created as permanent atoms, now move the contents // of the atoms table into permanentAtoms and mark each as permanent. JS_ASSERT(permanentAtoms && permanentAtoms->empty()); AtomSet *temp = atoms_; atoms_ = permanentAtoms; permanentAtoms = temp; for (AtomSet::Enum e(*permanentAtoms); !e.empty(); e.popFront()) { AtomStateEntry entry = e.front(); JSAtom *atom = entry.asPtr(); atom->morphIntoPermanentAtom(); } return true; }
bool JSRuntime::transformToPermanentAtoms(JSContext *cx) { MOZ_ASSERT(!parentRuntime); // All static strings were created as permanent atoms, now move the contents // of the atoms table into permanentAtoms and mark each as permanent. MOZ_ASSERT(!permanentAtoms); permanentAtoms = cx->new_<FrozenAtomSet>(atoms_); // takes ownership of atoms_ atoms_ = cx->new_<AtomSet>(); if (!atoms_ || !atoms_->init(JS_STRING_HASH_COUNT)) return false; for (FrozenAtomSet::Range r(permanentAtoms->all()); !r.empty(); r.popFront()) { AtomStateEntry entry = r.front(); JSAtom *atom = entry.asPtr(); atom->morphIntoPermanentAtom(); } return true; }
Symbol* Symbol::for_(JSContext* cx, HandleString description) { JSAtom* atom = AtomizeString(cx, description); if (!atom) return nullptr; AutoLockForExclusiveAccess lock(cx); SymbolRegistry& registry = cx->symbolRegistry(lock); SymbolRegistry::AddPtr p = registry.lookupForAdd(atom); if (p) { cx->markAtom(*p); return *p; } Symbol* sym; { AutoAtomsCompartment ac(cx, lock); // Rehash the hash of the atom to give the corresponding symbol a hash // that is different than the hash of the corresponding atom. HashNumber hash = mozilla::HashGeneric(atom->hash()); sym = newInternal(cx, SymbolCode::InSymbolRegistry, hash, atom, lock); if (!sym) return nullptr; // p is still valid here because we have held the lock since the // lookupForAdd call, and newInternal can't GC. if (!registry.add(p, sym)) { // SystemAllocPolicy does not report OOM. ReportOutOfMemory(cx); return nullptr; } } cx->markAtom(sym); return sym; }
bool js::InitCommonNames(JSContext *cx) { static const CommonNameInfo cachedNames[] = { #define COMMON_NAME_INFO(idpart, id, text) { js_##idpart##_str, sizeof(text) - 1 }, FOR_EACH_COMMON_PROPERTYNAME(COMMON_NAME_INFO) #undef COMMON_NAME_INFO #define COMMON_NAME_INFO(name, code, init) { js_##name##_str, sizeof(#name) - 1 }, JS_FOR_EACH_PROTOTYPE(COMMON_NAME_INFO) #undef COMMON_NAME_INFO }; FixedHeapPtr<PropertyName> *names = &cx->runtime->firstCachedName; for (size_t i = 0; i < ArrayLength(cachedNames); i++, names++) { JSAtom *atom = Atomize(cx, cachedNames[i].str, cachedNames[i].length, InternAtom); if (!atom) return false; names->init(atom->asPropertyName()); } JS_ASSERT(uintptr_t(names) == uintptr_t(&cx->runtime->atomState + 1)); cx->runtime->emptyString = cx->names().empty; return true; }
JSObject * ParallelArrayObject::initClass(JSContext *cx, HandleObject obj) { JS_ASSERT(obj->isNative()); // Cache constructor names. { const char *ctorStrs[NumCtors] = { "ParallelArrayConstructEmpty", "ParallelArrayConstructFromArray", "ParallelArrayConstructFromFunction", "ParallelArrayConstructFromFunctionMode" }; for (uint32_t i = 0; i < NumCtors; i++) { JSAtom *atom = Atomize(cx, ctorStrs[i], strlen(ctorStrs[i]), InternAtom); if (!atom) return NULL; ctorNames[i].init(atom->asPropertyName()); } } Rooted<GlobalObject *> global(cx, &obj->asGlobal()); RootedObject proto(cx, global->createBlankPrototype(cx, &protoClass)); if (!proto) return NULL; JSProtoKey key = JSProto_ParallelArray; RootedFunction ctor(cx, global->createConstructor(cx, construct, cx->names().ParallelArray, 0)); if (!ctor || !LinkConstructorAndPrototype(cx, ctor, proto) || !DefinePropertiesAndBrand(cx, proto, NULL, methods) || !DefineConstructorAndPrototype(cx, global, key, ctor, proto)) { return NULL; } // Define the length getter. { const char lengthStr[] = "ParallelArrayLength"; JSAtom *atom = Atomize(cx, lengthStr, strlen(lengthStr)); if (!atom) return NULL; Rooted<PropertyName *> lengthProp(cx, atom->asPropertyName()); RootedValue lengthValue(cx); if (!cx->global()->getIntrinsicValue(cx, lengthProp, &lengthValue)) return NULL; RootedObject lengthGetter(cx, &lengthValue.toObject()); if (!lengthGetter) return NULL; RootedId lengthId(cx, AtomToId(cx->names().length)); unsigned flags = JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_GETTER; RootedValue value(cx, UndefinedValue()); if (!DefineNativeProperty(cx, proto, lengthId, value, JS_DATA_TO_FUNC_PTR(PropertyOp, lengthGetter.get()), NULL, flags, 0, 0)) { return NULL; } } return proto; }
bool JSRuntime::initializeAtoms(JSContext *cx) { atoms_ = cx->new_<AtomSet>(); if (!atoms_ || !atoms_->init(JS_STRING_HASH_COUNT)) return false; // |permanentAtoms| hasn't been created yet. MOZ_ASSERT(!permanentAtoms); if (parentRuntime) { staticStrings = parentRuntime->staticStrings; commonNames = parentRuntime->commonNames; emptyString = parentRuntime->emptyString; permanentAtoms = parentRuntime->permanentAtoms; wellKnownSymbols = parentRuntime->wellKnownSymbols; return true; } staticStrings = cx->new_<StaticStrings>(); if (!staticStrings || !staticStrings->init(cx)) return false; static const CommonNameInfo cachedNames[] = { #define COMMON_NAME_INFO(idpart, id, text) { js_##idpart##_str, sizeof(text) - 1 }, FOR_EACH_COMMON_PROPERTYNAME(COMMON_NAME_INFO) #undef COMMON_NAME_INFO #define COMMON_NAME_INFO(name, code, init, clasp) { js_##name##_str, sizeof(#name) - 1 }, JS_FOR_EACH_PROTOTYPE(COMMON_NAME_INFO) #undef COMMON_NAME_INFO }; commonNames = cx->new_<JSAtomState>(); if (!commonNames) return false; ImmutablePropertyNamePtr *names = reinterpret_cast<ImmutablePropertyNamePtr *>(commonNames); for (size_t i = 0; i < ArrayLength(cachedNames); i++, names++) { JSAtom *atom = Atomize(cx, cachedNames[i].str, cachedNames[i].length, InternAtom); if (!atom) return false; names->init(atom->asPropertyName()); } MOZ_ASSERT(uintptr_t(names) == uintptr_t(commonNames + 1)); emptyString = commonNames->empty; // Create the well-known symbols. wellKnownSymbols = cx->new_<WellKnownSymbols>(); if (!wellKnownSymbols) return false; ImmutablePropertyNamePtr *descriptions = commonNames->wellKnownSymbolDescriptions(); ImmutableSymbolPtr *symbols = reinterpret_cast<ImmutableSymbolPtr *>(wellKnownSymbols); for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++) { JS::Symbol *symbol = JS::Symbol::new_(cx, JS::SymbolCode(i), descriptions[i]); if (!symbol) { ReportOutOfMemory(cx); return false; } symbols[i].init(symbol); } return true; }