TypedValue HHVM_FUNCTION(preg_match, StringArg pattern, StringArg subject, OutputArg matches /* = null */, int flags /* = 0 */, int offset /* = 0 */) { return tvReturn(preg_match(StrNR(pattern.get()), StrNR(subject.get()), matches.get() ? matches->var() : nullptr, flags, offset)); }
CachedUnit createUnitFromUrl(const StringData* const requestedPath) { auto const w = Stream::getWrapperFromURI(StrNR(requestedPath)); if (!w) return CachedUnit{}; auto const f = w->open(StrNR(requestedPath), "r", 0, null_variant); if (!f) return CachedUnit{}; StringBuffer sb; sb.read(f); return createUnitFromString(requestedPath->data(), sb.detach()); }
// When running via the CLI server special access checks may need to be // performed, and in the event that the server is unable to load the file an // alternative per client cache may be used. NonRepoUnitCache& getNonRepoCache(const StringData* rpath, Stream::Wrapper*& w) { if (auto uc = get_cli_ucred()) { if (!(w = Stream::getWrapperFromURI(StrNR(rpath)))) { return s_nonRepoUnitCache; } auto unit_check_quarantine = [&] () -> NonRepoUnitCache& { if (!RuntimeOption::EvalUnixServerQuarantineUnits) { return s_nonRepoUnitCache; } auto iter = s_perUserUnitCaches.find(uc->uid); if (iter != s_perUserUnitCaches.end()) return *iter->second; auto cache = new NonRepoUnitCache; auto res = s_perUserUnitCaches.insert(uc->uid, cache); if (!res.second) delete cache; return *res.first->second; }; // If the server cannot access rpath attempt to open the unit on the // client. When UnixServerQuarantineUnits is set store units opened by // clients in per UID caches which are never accessible by server web // requests. if (access(rpath->data(), R_OK) == -1) { return unit_check_quarantine(); } // When UnixServerVerifyExeAccess is set clients may not execute units if // they cannot read them, even when the server has access. To verify that // clients have access they are asked to open the file for read access, // and using fstat the server verifies that the file it sees is identical // to the unit opened by the client. if (RuntimeOption::EvalUnixServerVerifyExeAccess) { // Stream wrappers can reenter PHP via user defined callbacks. Roll this // operation into a single event rqtrace::EventGuard trace{"STREAM_WRAPPER_OPEN"}; rqtrace::DisableTracing disable; struct stat local, remote; auto remoteFile = w->open(StrNR(rpath), "r", 0, nullptr); if (!remoteFile || fcntl(remoteFile->fd(), F_GETFL) != O_RDONLY || fstat(remoteFile->fd(), &remote) != 0 || stat(rpath->data(), &local) != 0 || remote.st_dev != local.st_dev || remote.st_ino != local.st_ino) { return unit_check_quarantine(); } } // When the server is able to read the file prefer to access it that way, // in all modes units loaded by the server are cached for all clients. w = nullptr; } return s_nonRepoUnitCache; }
NamedEntity* NamedEntity::get(const StringData* str, bool allowCreate /* = true */, String* normalizedStr /* = nullptr */) { if (UNLIKELY(!s_namedDataMap)) { initializeNamedDataMap(); } auto it = s_namedDataMap->find(str); if (LIKELY(it != s_namedDataMap->end())) { return &it->second; } if (needsNSNormalization(str)) { auto normStr = normalizeNS(StrNR(str).asString()); if (normalizedStr) { *normalizedStr = normStr; } return get(normStr.get(), allowCreate, normalizedStr); } if (LIKELY(allowCreate)) { return insertNamedEntity(str); } return nullptr; }
Variant HHVM_FUNCTION(class_uses, const Variant& obj, bool autoload /* = true */) { Class* cls; if (obj.isString()) { cls = Unit::getClass(obj.getStringData(), autoload); if (!cls) { String err = "class_uses(): Class %s does not exist"; if (autoload) { err += " and could not be loaded"; } raise_warning(err.c_str(), obj.toString().c_str()); return false; } } else if (obj.isObject()) { cls = obj.getObjectData()->getVMClass(); } else { raise_warning("class_uses(): object or string expected"); return false; } auto &usedTraits = cls->preClass()->usedTraits(); ArrayInit ret(usedTraits.size(), ArrayInit::Map{}); for (auto const& traitName : usedTraits) { ret.set(StrNR(traitName), VarNR(traitName).tv()); } return ret.toArray(); }
CVarRef GlobalArrayWrapper::get(const StringData* k, bool error /* = false */) const { if (exists(k)) { return m_globals->get(StrNR(k)); } return null_variant; }
bool findFile(const StringData* path, struct stat* s, bool allow_dir, Stream::Wrapper* w, const Native::FuncTable& nativeFuncs) { // We rely on this side-effect in RepoAuthoritative mode right now, since the // stat information is an output-param of resolveVmInclude, but we aren't // really going to call stat. s->st_mode = 0; if (RuntimeOption::RepoAuthoritative) { return lookupUnitRepoAuth(path, nativeFuncs).unit != nullptr; } if (StatCache::stat(path->data(), s) == 0) { // The call explicitly populates the struct for dirs, but returns false for // them because it is geared toward file includes. return allow_dir || !S_ISDIR(s->st_mode); } if (w) { // Stream wrappers can reenter PHP via user defined callbacks. Roll this // operation into a single event rqtrace::EventGuard trace{"STREAM_WRAPPER_STAT"}; rqtrace::DisableTracing disable; if (w->stat(StrNR(path), s) == 0) { return allow_dir || !S_ISDIR(s->st_mode); } } return false; }
const EnumValues* EnumCache::loadEnumValues(const Class* klass, bool recurse) { auto const numConstants = klass->numConstants(); auto values = Array::CreateDArray(); auto names = Array::CreateDArray(); auto const consts = klass->constants(); bool persist = true; for (size_t i = 0; i < numConstants; i++) { if (consts[i].isAbstract() || consts[i].isType()) { continue; } if (consts[i].cls != klass && !recurse) { continue; } Cell value = consts[i].val; // Handle dynamically set constants if (value.m_type == KindOfUninit) { persist = false; value = klass->clsCnsGet(consts[i].name); } assertx(value.m_type != KindOfUninit); if (UNLIKELY(!(isIntType(value.m_type) || tvIsString(&value)))) { // only int and string values allowed for enums. std::string msg; msg += klass->name()->data(); msg += " enum can only contain string and int values"; EnumCache::failLookup(msg); } values.set(StrNR(consts[i].name), cellAsCVarRef(value)); // Manually perform int-like key coercion even if names is a dict for // backwards compatibility. int64_t n; if (tvIsString(&value) && value.m_data.pstr->isStrictlyInteger(n)) { names.set(n, make_tv<KindOfPersistentString>(consts[i].name)); } else { names.set(value, make_tv<KindOfPersistentString>(consts[i].name), true); } } assertx(names.isDictOrDArray()); assertx(values.isDictOrDArray()); // If we saw dynamic constants we cannot cache the enum values across requests // as they may not be the same in every request. return persist ? cachePersistentEnumValues( klass, recurse, std::move(names), std::move(values)) : cacheRequestEnumValues( klass, recurse, std::move(names), std::move(values)); }
ArrayData *VectorArray::addLval(CVarRef k, Variant *&ret, bool copy) { ASSERT(!exists(k)); Variant::TypedValueAccessor tva = k.getTypedAccessor(); if (isIntKey(tva)) return VectorArray::addLval(getIntKey(tva), ret, copy); ASSERT(k.isString()); ZendArray *a = escalateToZendArray(); a->addLval(StrNR(getStringKey(tva)), ret, false); return a; }
CachedUnit lookupUnitNonRepoAuth(StringData* requestedPath, const struct stat& statInfo) { if (strstr(requestedPath->data(), "://") != nullptr) { // URL-based units are not currently cached in memory, but the Repo still // caches them on disk. return createUnitFromUrl(requestedPath); } // The string we're using as a key must be static, because we're using it as // a key in the cache (across requests). auto const path = makeStaticString( // XXX: it seems weird we have to do this even though we already ran // resolveVmInclude. (requestedPath->data()[0] == '/' ? requestedPath : String(SourceRootInfo::GetCurrentSourceRoot()) + StrNR(requestedPath) ).get() ); NonRepoUnitCache::accessor acc; if (!s_nonRepoUnitCache.insert(acc, path)) { if (!isChanged(acc->second, statInfo)) { return acc->second.cachedUnit; } } /* * NB: the new-unit creation path is here, and is done while holding the tbb * lock on s_nonRepoUnitCache. This was originally done deliberately to * avoid wasting time in the compiler (during server startup, many requests * hit the same code initial paths that are shared, and would all be * compiling the same files). It's not 100% clear if this is the best way to * handle that idea, though (tbb locks spin aggressively and are expected to * be low contention). */ /* * Don't cache if createNewUnit returns an empty CachedUnit---we'll need to * try again anyway if someone tries to load this path, since it might exist * later. * * If there was a unit for this path already, we need to put it on the * Treadmill for eventual reclaimation. We can't delete it immediately * because other requests may still be using it. */ auto const cu = createUnitFromFile(path); if (auto const oldUnit = acc->second.cachedUnit.unit) { Treadmill::enqueue([oldUnit] { reclaimUnit(oldUnit); }); } acc->second.cachedUnit = cu; acc->second.mtime = statInfo.st_mtim; acc->second.ino = statInfo.st_ino; acc->second.devId = statInfo.st_dev; return cu; }
CachedUnit createUnitFromUrl(const StringData* const requestedPath, const Native::FuncTable& nativeFuncs, FileLoadFlags& flags) { auto const w = Stream::getWrapperFromURI(StrNR(requestedPath)); StringBuffer sb; { // Stream wrappers can reenter PHP via user defined callbacks. Roll this // operation into a single event rqtrace::EventGuard trace{"STREAM_WRAPPER_OPEN"}; rqtrace::DisableTracing disable; if (!w) return CachedUnit{}; auto const f = w->open(StrNR(requestedPath), "r", 0, nullptr); if (!f) return CachedUnit{}; sb.read(f.get()); } OptLog ent; return createUnitFromString(requestedPath->data(), sb.detach(), nullptr, ent, nativeFuncs, RepoOptions::defaults(), flags); }
HOT_FUNC_HPHP ArrayData *VectorArray::set(CVarRef k, CVarRef v, bool copy) { Variant::TypedValueAccessor tva = k.getTypedAccessor(); if (isIntKey(tva)) { return VectorArray::set(getIntKey(tva), v, copy); } ASSERT(k.isString()); ZendArray *a = escalateToZendArray(); a->add(StrNR(getStringKey(tva)), v, false); return a; }
Class* lookupKnownClass(Class** cache, const StringData* clsName) { Class* cls = *cache; assert(!cls); // the caller should already have checked assert(clsName->data()[0] != '\\'); // namespace names should be done earlier AutoloadHandler::s_instance->invokeHandler( StrNR(const_cast<StringData*>(clsName))); cls = *cache; if (UNLIKELY(!cls)) raise_error(Strings::UNKNOWN_CLASS, clsName->data()); return cls; }
const EnumCache::EnumValues* EnumCache::loadEnumValues(const Class* klass, bool recurse) { auto const numConstants = klass->numConstants(); size_t foundOnClass = 0; Array values; Array names; auto const consts = klass->constants(); for (size_t i = 0; i < numConstants; i++) { if (consts[i].isAbstract() || consts[i].isType()) { continue; } if (consts[i].m_class == klass) foundOnClass++; else if (!recurse) continue; Cell value = consts[i].m_val; // Handle dynamically set constants if (value.m_type == KindOfUninit) { value = klass->clsCnsGet(consts[i].m_name); } assert(value.m_type != KindOfUninit); if (UNLIKELY(!(isIntType(value.m_type) || (tvIsString(&value) && value.m_data.pstr->isStatic())))) { // only int and string values allowed for enums. Moreover the strings // must be static std::string msg; msg += klass->name()->data(); msg += " enum can only contain static string and int values"; EnumCache::failLookup(msg); } values.set(StrNR(consts[i].m_name), cellAsCVarRef(value)); names.set(cellAsCVarRef(value), VarNR(consts[i].m_name)); } if (UNLIKELY(foundOnClass == 0)) { std::string msg; msg += klass->name()->data(); msg += " enum must contain values"; EnumCache::failLookup(msg); } { std::unique_ptr<EnumCache::EnumValues> enums(new EnumCache::EnumValues()); enums->values = ArrayData::GetScalarArray(values.get()); enums->names = ArrayData::GetScalarArray(names.get()); intptr_t key = getKey(klass, recurse); EnumValuesMap::accessor acc; if (!m_enumValuesMap.insert(acc, key)) { return acc->second; } // add to the map the newly created values acc->second = enums.release(); return acc->second; } }
static Array HHVM_METHOD(Closure, __debugInfo) { auto closure = c_Closure::fromObject(this_); Array ret = Array::Create(); // Serialize 'use' parameters. if (auto useVars = closure->getUseVars()) { Array use; auto cls = this_->getVMClass(); auto propsInfo = cls->declProperties(); auto nProps = cls->numDeclProperties(); for (size_t i = 0; i < nProps; ++i) { auto value = &useVars[i]; use.setWithRef(Variant(StrNR(propsInfo[i].name)), tvAsCVarRef(value)); } if (!use.empty()) { ret.set(s_static, use); } } auto const func = closure->getInvokeFunc(); // Serialize function parameters. if (auto nParams = func->numParams()) { Array params; auto lNames = func->localNames(); for (int i = 0; i < nParams; ++i) { auto str = String::attach( StringData::Make(s_varprefix.get(), lNames[i]) ); bool optional = func->params()[i].phpCode; if (auto mi = func->methInfo()) { optional = optional || mi->parameters[i]->valueText; } params.set(str, optional ? s_optional : s_required); } ret.set(s_parameter, params); } // Serialize 'this' object. if (closure->hasThis()) { ret.set(s_this, Object(closure->getThis())); } return ret; }
const EnumValues* EnumCache::loadEnumValues(const Class* klass, bool recurse) { auto const numConstants = klass->numConstants(); auto values = Array::Create(); auto names = Array::Create(); auto const consts = klass->constants(); bool persist = true; for (size_t i = 0; i < numConstants; i++) { if (consts[i].isAbstract() || consts[i].isType()) { continue; } if (consts[i].cls != klass && !recurse) { continue; } Cell value = consts[i].val; // Handle dynamically set constants if (value.m_type == KindOfUninit) { persist = false; value = klass->clsCnsGet(consts[i].name); } assert(value.m_type != KindOfUninit); if (UNLIKELY(!(isIntType(value.m_type) || (tvIsString(&value) && value.m_data.pstr->isStatic())))) { // only int and string values allowed for enums. Moreover the strings // must be static std::string msg; msg += klass->name()->data(); msg += " enum can only contain static string and int values"; EnumCache::failLookup(msg); } values.set(StrNR(consts[i].name), cellAsCVarRef(value)); names.set(value, make_tv<KindOfPersistentString>(consts[i].name)); } // If we saw dynamic constants we cannot cache the enum values across requests // as they may not be the same in every request. return persist ? cachePersistentEnumValues( klass, recurse, std::move(names), std::move(values)) : cacheRequestEnumValues( klass, recurse, std::move(names), std::move(values)); }
bool register_intercept(const String& name, const Variant& callback, const Variant& data) { StringIMap<Variant> &handlers = s_intercept_data->m_intercept_handlers; if (!callback.toBoolean()) { if (name.empty()) { s_intercept_data->m_global_handler.unset(); StringIMap<Variant> empty; handlers.swap(empty); } else { auto tmp = handlers[name]; auto it = handlers.find(name); if (it != handlers.end()) { auto tmp = it->second; handlers.erase(it); } } return true; } EventHook::EnableIntercept(); Array handler = make_packed_array(callback, data); if (name.empty()) { s_intercept_data->m_global_handler = handler; StringIMap<Variant> empty; handlers.swap(empty); } else { handlers[name] = handler; } Lock lock(s_mutex); if (name.empty()) { for (auto& entry : s_registered_flags) { flag_maybe_intercepted(entry.second.second); } } else { StringData* sd = name.get(); if (!sd->isStatic()) { sd = makeStaticString(sd); } auto &entry = s_registered_flags[StrNR(sd)]; entry.first = true; flag_maybe_intercepted(entry.second); } return true; }
Array c_Closure::t___debuginfo() { Array ret = Array::Create(); // Serialize 'use' parameters. if (auto propValues = propVec()) { Array use; auto propsInfo = getVMClass()->declProperties(); for (size_t i = 0; i < getVMClass()->numDeclProperties(); ++i) { auto value = &propValues[i]; use.setWithRef(Variant(StrNR(propsInfo[i].name)), tvAsCVarRef(value)); } if (!use.empty()) { ret.set(s_static, use); } } auto const func = getInvokeFunc(); // Serialize function parameters. if (func->numParams()) { Array params; for (int i = 0; i < func->numParams(); ++i) { auto str = String::attach( StringData::Make(s_varprefix.get(), func->localNames()[i]) ); bool optional = func->params()[i].phpCode; if (auto mi = func->methInfo()) { optional = optional || mi->parameters[i]->valueText; } params.set(str, optional ? s_optional : s_required); } ret.set(s_parameter, params); } // Serialize 'this' object. if (hasThis()) { ret.set(s_this, Object(getThis())); } return ret; }
Variant f_class_uses(const Variant& obj, bool autoload /* = true */) { Class* cls; if (obj.isString()) { cls = Unit::getClass(obj.getStringData(), autoload); if (!cls) { return false; } } else if (obj.isObject()) { cls = obj.getObjectData()->getVMClass(); } else { return false; } Array ret(Array::Create()); for (auto const& traitName : cls->preClass()->usedTraits()) { ret.set(StrNR(traitName), VarNR(traitName)); } return ret; }
HOT_FUNC bool f_array_key_exists(CVarRef key, CVarRef search) { const ArrayData *ad; auto const searchCell = search.asCell(); if (LIKELY(searchCell->m_type == KindOfArray)) { ad = searchCell->m_data.parr; } else if (searchCell->m_type == KindOfObject) { ObjectData* obj = searchCell->m_data.pobj; if (obj->isCollection()) { return collectionOffsetContains(obj, key); } return f_array_key_exists(key, toArray(search)); } else { throw_bad_type_exception("array_key_exists expects an array or an object; " "false returned."); return false; } auto const cell = key.asCell(); switch (cell->m_type) { case KindOfString: case KindOfStaticString: { int64_t n = 0; StringData *sd = cell->m_data.pstr; if (sd->isStrictlyInteger(n)) { return ad->exists(n); } return ad->exists(StrNR(sd)); } case KindOfInt64: return ad->exists(cell->m_data.num); case KindOfUninit: case KindOfNull: return ad->exists(empty_string); default: break; } raise_warning("Array key should be either a string or an integer"); return false; }
bool register_intercept(const String& name, CVarRef callback, CVarRef data) { StringIMap<Variant> &handlers = s_intercept_data->m_intercept_handlers; if (!callback.toBoolean()) { if (name.empty()) { s_intercept_data->m_global_handler.reset(); handlers.clear(); } else { handlers.erase(name); } return true; } EventHook::EnableIntercept(); Array handler = make_packed_array(callback, data); if (name.empty()) { s_intercept_data->m_global_handler = handler; handlers.clear(); } else { handlers[name] = handler; } Lock lock(s_mutex); if (name.empty()) { for (auto& entry : s_registered_flags) { flag_maybe_intercepted(entry.second.second); } } else { StringData* sd = name.get(); if (!sd->isStatic()) { sd = makeStaticString(sd); } auto &entry = s_registered_flags[StrNR(sd)]; entry.first = true; flag_maybe_intercepted(entry.second); } return true; }
Variant *get_intercept_handler(CStrRef name, char* flag) { TRACE(1, "get_intercept_handler %s flag is %d\n", name.get()->data(), (int)*flag); if (*flag == -1) { Lock lock(s_mutex); if (*flag == -1) { StringData *sd = name.get(); if (!sd->isStatic()) { sd = StringData::GetStaticString(sd); } s_registered_flags[StrNR(sd)].push_back(flag); *flag = 0; } } Variant *handler = get_enabled_intercept_handler(name); if (handler == nullptr) { return nullptr; } *flag = 1; return handler; }
Variant *get_intercept_handler(const String& name, char* flag) { TRACE(1, "get_intercept_handler %s flag is %d\n", name.get()->data(), (int)*flag); if (*flag == -1) { Lock lock(s_mutex); if (*flag == -1) { StringData *sd = name.get(); if (!sd->isStatic()) { sd = makeStaticString(sd); } auto &entry = s_registered_flags[StrNR(sd)]; entry.second.push_back(flag); *flag = entry.first; } if (!*flag) return nullptr; } Variant *handler = get_enabled_intercept_handler(name); if (handler == nullptr) { return nullptr; } assert(*flag); return handler; }
Variant f_class_uses(const Variant& obj, bool autoload /* = true */) { Class* cls; if (obj.isString()) { cls = Unit::getClass(obj.getStringData(), autoload); if (!cls) { String err = "class_uses(): Class %s does not exist"; if (autoload) { err += " and could not be loaded"; } raise_warning(err.c_str(), obj.toString().c_str()); return false; } } else if (obj.isObject()) { cls = obj.getObjectData()->getVMClass(); } else { raise_warning("class_uses(): object or string expected"); return false; } Array ret(Array::Create()); for (auto const& traitName : cls->preClass()->usedTraits()) { ret.set(StrNR(traitName), VarNR(traitName)); } return ret; }
HOT_FUNC bool f_array_key_exists(CVarRef key, CVarRef search) { const ArrayData *ad; Variant::TypedValueAccessor sacc = search.getTypedAccessor(); DataType saccType = Variant::GetAccessorType(sacc); if (LIKELY(saccType == KindOfArray)) { ad = Variant::GetArrayData(sacc); } else if (saccType == KindOfObject) { return f_array_key_exists(key, toArray(search)); } else { throw_bad_type_exception("array_key_exists expects an array or an object; " "false returned."); return false; } Variant::TypedValueAccessor kacc = key.getTypedAccessor(); switch (Variant::GetAccessorType(kacc)) { case KindOfString: case KindOfStaticString: { int64_t n = 0; StringData *sd = Variant::GetStringData(kacc); if (sd->isStrictlyInteger(n)) { return ad->exists(n); } return ad->exists(StrNR(sd)); } case KindOfInt64: return ad->exists(Variant::GetInt64(kacc)); case KindOfUninit: case KindOfNull: return ad->exists(empty_string); default: break; } raise_warning("Array key should be either a string or an integer"); return false; }
ArrayData *GlobalArrayWrapper::set(StringData* k, CVarRef v, bool copy) { m_globals->get(StrNR(k)).assignVal(v); return NULL; }
CachedUnit loadUnitNonRepoAuth(StringData* requestedPath, const struct stat* statInfo, OptLog& ent, const Native::FuncTable& nativeFuncs, const RepoOptions& options, FileLoadFlags& flags) { LogTimer loadTime("load_ms", ent); if (strstr(requestedPath->data(), "://") != nullptr) { // URL-based units are not currently cached in memory, but the Repo still // caches them on disk. return createUnitFromUrl(requestedPath, nativeFuncs, flags); } rqtrace::EventGuard trace{"WRITE_UNIT"}; // The string we're using as a key must be static, because we're using it as // a key in the cache (across requests). auto const path = makeStaticString( // XXX: it seems weird we have to do this even though we already ran // resolveVmInclude. (FileUtil::isAbsolutePath(requestedPath->toCppString()) ? String{requestedPath} : String(SourceRootInfo::GetCurrentSourceRoot()) + StrNR(requestedPath) ).get() ); auto const rpath = [&] () -> const StringData* { if (RuntimeOption::CheckSymLink) { std::string rp = StatCache::realpath(path->data()); if (rp.size() != 0) { if (rp.size() != path->size() || memcmp(rp.data(), path->data(), rp.size())) { return makeStaticString(rp); } } } return path; }(); Stream::Wrapper* w = nullptr; auto& cache = getNonRepoCache(rpath, w); assertx( !w || &cache != &s_nonRepoUnitCache || !RuntimeOption::EvalUnixServerQuarantineUnits ); // Freeing a unit while holding the tbb lock would cause a rank violation when // recycle-tc is enabled as reclaiming dead functions requires that the code // and metadata locks be acquired. Unit* releaseUnit = nullptr; SCOPE_EXIT { if (releaseUnit) delete releaseUnit; }; auto const updateAndUnlock = [] (auto& cachedUnit, auto p) { auto old = cachedUnit.update_and_unlock(std::move(p)); if (old) { // We don't need to do anything explicitly; the copy_ptr // destructor will take care of it. Treadmill::enqueue([unit_to_delete = std::move(old)] () {}); } }; auto cuptr = [&] { NonRepoUnitCache::const_accessor rpathAcc; cache.insert(rpathAcc, rpath); auto& cachedUnit = rpathAcc->second.cachedUnit; if (auto const tmp = cachedUnit.copy()) { if (!isChanged(tmp, statInfo, options)) { flags = FileLoadFlags::kHitMem; if (ent) ent->setStr("type", "cache_hit_readlock"); return tmp; } } cachedUnit.lock_for_update(); try { if (auto const tmp = cachedUnit.copy()) { if (!isChanged(tmp, statInfo, options)) { cachedUnit.unlock(); flags = FileLoadFlags::kWaited; if (ent) ent->setStr("type", "cache_hit_writelock"); return tmp; } if (ent) ent->setStr("type", "cache_stale"); } else { if (ent) ent->setStr("type", "cache_miss"); } trace.finish(); auto const cu = createUnitFromFile(rpath, &releaseUnit, w, ent, nativeFuncs, options, flags); auto const isICE = cu.unit && cu.unit->isICE(); auto p = copy_ptr<CachedUnitWithFree>(cu, statInfo, isICE, options); // Don't cache the unit if it was created in response to an internal error // in ExternCompiler. Such units represent transient events. if (UNLIKELY(isICE)) { cachedUnit.unlock(); return p; } updateAndUnlock(cachedUnit, p); return p; } catch (...) { cachedUnit.unlock(); throw; } }(); auto const ret = cuptr->cu; if (!ret.unit || !ret.unit->isICE()) { if (path != rpath) { NonRepoUnitCache::const_accessor pathAcc; cache.insert(pathAcc, path); if (pathAcc->second.cachedUnit.get().get() != cuptr) { auto& cachedUnit = pathAcc->second.cachedUnit; cachedUnit.lock_for_update(); updateAndUnlock(cachedUnit, std::move(cuptr)); } } } return ret; }
Variant NameValueTableWrapper::getKey(ssize_t pos) const { NameValueTable::Iterator iter(m_tab, pos); return iter.valid() ? Variant(StrNR(iter.curKey())) : uninit_null(); }
TypedValue* ArrayData::nvGetCell(const StringData* key) const { TypedValue* tv = (TypedValue*)&get(StrNR(key), false); return LIKELY(tv != (TypedValue*)&null_variant) ? tvToCell(tv) : nvGetNotFound(key); }
static inline StrNR ctxClassName() { Class* ctx = g_context->getContextClass(); return ctx ? ctx->nameStr() : StrNR(staticEmptyString()); }