const Func* StaticMethodCache::lookup(RDS::Handle handle, const NamedEntity *ne, const StringData* clsName, const StringData* methName) { StaticMethodCache* thiz = static_cast<StaticMethodCache*> (handleToPtr(handle)); Stats::inc(Stats::TgtCache_StaticMethodMiss); Stats::inc(Stats::TgtCache_StaticMethodHit, -1); TRACE(1, "miss %s :: %s caller %p\n", clsName->data(), methName->data(), __builtin_return_address(0)); Transl::VMRegAnchor _; // needed for lookupClsMethod. ActRec* ar = reinterpret_cast<ActRec*>(vmsp() - kNumActRecCells); const Func* f; VMExecutionContext* ec = g_vmContext; const Class* cls = Unit::loadClass(ne, clsName); if (UNLIKELY(!cls)) { raise_error(Strings::UNKNOWN_CLASS, clsName->data()); } LookupResult res = ec->lookupClsMethod(f, cls, methName, nullptr, // there may be an active this, // but we can just fall through // in that case. arGetContextClass(ec->getFP()), false /*raise*/); if (LIKELY(res == LookupResult::MethodFoundNoThis && !f->isAbstract() && f->isStatic())) { f->validate(); TRACE(1, "fill %s :: %s -> %p\n", clsName->data(), methName->data(), f); // Do the | here instead of on every call. thiz->m_cls = (Class*)(uintptr_t(cls) | 1); thiz->m_func = f; ar->setClass(const_cast<Class*>(cls)); return f; } assert(res != LookupResult::MethodFoundWithThis); // Not possible: no this. // We've already sync'ed regs; this is some hard case, we might as well // just let the interpreter handle this entirely. assert(toOp(*vmpc()) == OpFPushClsMethodD); Stats::inc(Stats::Instr_InterpOneFPushClsMethodD); Stats::inc(Stats::Instr_TC, -1); ec->opFPushClsMethodD(); // Return whatever func the instruction produced; if nothing was // possible we'll either have fataled or thrown. assert(ar->m_func); ar->m_func->validate(); // Don't update the cache; this case was too scary to memoize. TRACE(1, "unfillable miss %s :: %s -> %p\n", clsName->data(), methName->data(), ar->m_func); // Indicate to the caller that there is no work to do. return nullptr; }
const Func* StaticMethodCache::lookupIR(RDS::Handle handle, const NamedEntity *ne, const StringData* clsName, const StringData* methName, TypedValue* vmfp, TypedValue* vmsp) { StaticMethodCache* thiz = static_cast<StaticMethodCache*> (handleToPtr(handle)); Stats::inc(Stats::TgtCache_StaticMethodMiss); Stats::inc(Stats::TgtCache_StaticMethodHit, -1); TRACE(1, "miss %s :: %s caller %p\n", clsName->data(), methName->data(), __builtin_return_address(0)); ActRec* ar = reinterpret_cast<ActRec*>(vmsp - kNumActRecCells); const Func* f; VMExecutionContext* ec = g_vmContext; const Class* cls = Unit::loadClass(ne, clsName); if (UNLIKELY(!cls)) { raise_error(Strings::UNKNOWN_CLASS, clsName->data()); } LookupResult res = ec->lookupClsMethod(f, cls, methName, nullptr, // there may be an active this, // but we can just fall through // in that case. arGetContextClass((ActRec*)vmfp), false /*raise*/); if (LIKELY(res == LookupResult::MethodFoundNoThis && !f->isAbstract() && f->isStatic())) { f->validate(); TRACE(1, "fill %s :: %s -> %p\n", clsName->data(), methName->data(), f); // Do the | here instead of on every call. thiz->m_cls = (Class*)(uintptr_t(cls) | 1); thiz->m_func = f; ar->setClass(const_cast<Class*>(cls)); return f; } assert(res != LookupResult::MethodFoundWithThis); // Not possible: no this. // Indicate to the IR that it should take even slower path return nullptr; }
const Func* StaticMethodFCache::lookupIR(RDS::Handle handle, const Class* cls, const StringData* methName, TypedValue* vmfp) { assert(cls); StaticMethodFCache* thiz = static_cast<StaticMethodFCache*> (handleToPtr(handle)); Stats::inc(Stats::TgtCache_StaticMethodFMiss); Stats::inc(Stats::TgtCache_StaticMethodFHit, -1); const Func* f; VMExecutionContext* ec = g_vmContext; LookupResult res = ec->lookupClsMethod(f, cls, methName, nullptr, arGetContextClass((ActRec*)vmfp), false /*raise*/); assert(res != LookupResult::MethodFoundWithThis); // Not possible: no this. if (LIKELY(res == LookupResult::MethodFoundNoThis && !f->isAbstract())) { // We called lookupClsMethod with a NULL this and got back a // method that may or may not be static. This implies that // lookupClsMethod, given the same class and the same method name, // will never return MagicCall*Found or MethodNotFound. It will // always return the same f and if we do give it a this it will // return MethodFoundWithThis iff (this->instanceof(cls) && // !f->isStatic()). this->instanceof(cls) is always true for // FPushClsMethodF because it is only used for self:: and parent:: // calls. So, if we store f and its staticness we can handle calls // with and without this completely in assembly. f->validate(); thiz->m_func = f; thiz->m_static = f->isStatic(); TRACE(1, "fill staticfcache %s :: %s -> %p\n", cls->name()->data(), methName->data(), f); Stats::inc(Stats::TgtCache_StaticMethodFFill); return f; } return nullptr; }