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; } }
Variant HHVM_FUNCTION(constant, const String& name) { if (!name.get()) return init_null(); const char *data = name.data(); int len = name.length(); char *colon; if ((colon = (char*)memchr(data, ':', len)) && colon[1] == ':') { // class constant int classNameLen = colon - data; char *constantName = colon + 2; Class* cls = getClassByName(data, classNameLen); if (cls) { String cnsName(constantName, data + len - constantName, CopyString); Cell cns = cls->clsCnsGet(cnsName.get()); if (cns.m_type != KindOfUninit) { return cellAsCVarRef(cns); } } } else { auto const cns = Unit::loadCns(name.get()); if (cns) return tvAsCVarRef(cns); } raise_warning("constant(): Couldn't find constant %s", data); return init_null(); }
void EventHook::onFunctionReturn(ActRec* ar, TypedValue retval) { // The locals are already gone. Null out everything. ar->setThisOrClassAllowNull(nullptr); ar->setLocalsDecRefd(); ar->setVarEnv(nullptr); try { ssize_t flags = CheckSurprise(); onFunctionExit(ar, &retval, nullptr, flags); // Async profiler if ((flags & AsyncEventHookFlag) && ar->func()->isAsyncFunction() && ar->resumed()) { auto session = AsioSession::Get(); // Return @ resumed execution => AsyncFunctionWaitHandle succeeded. if (session->hasOnResumableSuccessCallback()) { auto afwh = frame_afwh(ar); session->onResumableSuccess(afwh, cellAsCVarRef(retval)); } } } catch (...) { /* * We're responsible for freeing the return value if we exit with an * exception. See irgen-ret. */ tvRefcountedDecRef(retval); throw; } }
void EventHook::onFunctionReturn(ActRec* ar, const TypedValue& retval) { // Null out $this for the exiting function, it has been decref'd so it's // garbage. ar->setThisOrClassAllowNull(nullptr); // The locals are already gone. Mark them as decref'd so that if this hook // fails and unwinder kicks in, it won't try to decref them again. ar->setLocalsDecRefd(); // TODO(#5758054): does this need setVarEnv(nullptr) ? ssize_t flags = CheckSurprise(); onFunctionExit(ar, &retval, nullptr, flags); // Async profiler if ((flags & RequestInjectionData::AsyncEventHookFlag) && ar->func()->isAsyncFunction() && ar->resumed()) { auto session = AsioSession::Get(); // Return @ resumed execution => AsyncFunctionWaitHandle succeeded. if (session->hasOnResumableSuccessCallback()) { auto afwh = frame_afwh(ar); session->onResumableSuccess(afwh, cellAsCVarRef(retval)); } } }
Array HHVM_FUNCTION(get_class_constants, const String& className) { auto const cls = Unit::loadClass(className.get()); if (cls == NULL) { return Array::attach(MixedArray::MakeReserve(0)); } auto const numConstants = cls->numConstants(); ArrayInit arrayInit(numConstants, ArrayInit::Map{}); auto const consts = cls->constants(); for (size_t i = 0; i < numConstants; i++) { // Note: hphpc doesn't include inherited constants in // get_class_constants(), so mimic that behavior if (consts[i].cls == cls && !consts[i].isAbstract() && !consts[i].isType()) { auto const name = const_cast<StringData*>(consts[i].name.get()); Cell value = consts[i].val; // Handle dynamically set constants if (value.m_type == KindOfUninit) { value = cls->clsCnsGet(consts[i].name); } assert(value.m_type != KindOfUninit); arrayInit.set(name, cellAsCVarRef(value)); } } return arrayInit.toArray(); }
void c_AsyncFunctionWaitHandle::markAsSucceeded() { AsioSession* session = AsioSession::Get(); if (UNLIKELY(session->hasOnAsyncFunctionSuccessCallback())) { session->onAsyncFunctionSuccess(this, cellAsCVarRef(m_resultOrException)); } done(); }
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)); }
Variant c_WaitHandle::result() { assert(isFinished()); if (LIKELY(isSucceeded())) { // succeeded? return result return cellAsCVarRef(getResult()); } else { // failed? throw exception throw Object{getException()}; } }
void c_AsyncFunctionWaitHandle::markAsSucceeded(const Cell& result) { AsioSession* session = AsioSession::Get(); if (UNLIKELY(session->hasOnAsyncFunctionSuccessCallback())) { session->onAsyncFunctionSuccess(this, cellAsCVarRef(result)); } setResult(result); // free m_continuation / m_child later, result may be stored there m_continuation = nullptr; m_child = nullptr; }
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)); }
Variant c_WaitHandle::t_join() { if (!isFinished()) { // run the full blown machinery assert(instanceof(c_WaitableWaitHandle::classof())); static_cast<c_WaitableWaitHandle*>(this)->join(); } assert(isFinished()); if (LIKELY(isSucceeded())) { // succeeded? return result return cellAsCVarRef(getResult()); } else { // failed? throw exception throw Object{getException()}; } }
Variant f_constant(const String& name) { if (!name.get()) return uninit_null(); const char *data = name.data(); int len = name.length(); // slice off starting backslash bool hadInitialBackslash = false; if (len > 0 && data[0] == '\\') { data += 1; len -= 1; hadInitialBackslash = true; } char *colon; if ((colon = (char*)memchr(data, ':', len)) && colon[1] == ':') { // class constant int classNameLen = colon - data; char *constantName = colon + 2; Class* cls = getClassByName(data, classNameLen); if (cls) { String cnsName(constantName, data + len - constantName, CopyString); Cell cns = cls->clsCnsGet(cnsName.get()); if (cns.m_type != KindOfUninit) { return cellAsCVarRef(cns); } } raise_warning("Couldn't find constant %s", data); } else { TypedValue* cns; if (hadInitialBackslash) { String s(data, len, CopyString); cns = Unit::loadCns(s.get()); } else { cns = Unit::loadCns(name.get()); } if (cns) return tvAsVariant(cns); } return uninit_null(); }