void skip() { Class* cls; do { cls = *m_next->second.clsList(); if (!cls || !cls->getCached()) ++m_next; } while (!empty() && !cls); }
Class* front() { ASSERT(!empty()); Class* c = *m_next->second.clsList(); ASSERT(c); c = c->getCached(); ASSERT(c); return c; }
Class* Unit::loadClass(const NamedEntity* ne, const StringData* name) { Class *cls = *ne->clsList(); if (LIKELY(cls != NULL)) { cls = cls->getCached(); if (LIKELY(cls != NULL)) return cls; } VMRegAnchor _; AutoloadHandler::s_instance->invokeHandler(name->data()); return Unit::lookupClass(ne); }
Class* Unit::defClass(PreClass* preClass, bool failIsFatal /* = true */) { Class* const* clsList = preClass->namedEntity()->clsList(); Class* top = *clsList; if (top) { Class *cls = top->getCached(); if (cls) { // Raise a fatal unless the existing class definition is identical to the // one this invocation would create. if (cls->preClass() != preClass) { if (failIsFatal) { raise_error("Class already declared: %s", preClass->name()->data()); } return NULL; } return cls; } } // Get a compatible Class, and add it to the list of defined classes. Class* parent = NULL; for (;;) { // Search for a compatible extant class. Searching from most to least // recently created may have better locality than alternative search orders. // In addition, its the only simple way to make this work lock free... for (Class* class_ = top; class_ != NULL; class_ = class_->m_nextClass) { if (class_->preClass() != preClass) continue; Class::Avail avail = class_->avail(parent, failIsFatal /*tryAutoload*/); if (LIKELY(avail == Class::AvailTrue)) { class_->setCached(); DEBUGGER_ATTACHED_ONLY(phpDefClassHook(class_)); return class_; } if (avail == Class::AvailFail) { if (failIsFatal) { raise_error("unknown class %s", parent->name()->data()); } return NULL; } ASSERT(avail == Class::AvailFalse); } // Create a new class. if (!parent && preClass->parent()->size() != 0) { parent = Unit::getClass(preClass->parent(), failIsFatal); if (parent == NULL) { if (failIsFatal) { raise_error("unknown class %s", preClass->parent()->data()); } return NULL; } } VMExecutionContext* ec = g_vmContext; ActRec* fp = ec->getFP(); PC pc = ec->getPC(); bool needsFrame = ec->m_stack.top() && (!fp || fp->m_func->unit() != preClass->unit()); if (needsFrame) { /* we can be called from Unit::merge, which hasnt yet setup the frame (because often it doesnt need to). Set up a fake frame here, in case of errors. But note that mergeUnit is called for systemlib etc before the stack has been setup. So dont do anything if m_stack.top() is NULL */ ActRec &tmp = *ec->m_stack.allocA(); tmp.m_savedRbp = (uint64_t)fp; tmp.m_savedRip = 0; tmp.m_func = preClass->unit()->getMain(); tmp.m_soff = preClass->getOffset() - tmp.m_func->base(); tmp.setThis(NULL); tmp.m_varEnv = 0; tmp.initNumArgs(0); ec->m_fp = &tmp; ec->m_pc = preClass->unit()->at(preClass->getOffset()); ec->pushLocalsAndIterators(tmp.m_func); } ClassPtr newClass(Class::newClass(preClass, parent)); if (needsFrame) { ec->m_stack.top() = (Cell*)(ec->m_fp+1); ec->m_fp = fp; ec->m_pc = pc; } Lock l(Unit::s_classesMutex); /* We could re-enter via Unit::getClass() or class_->avail(), so no need for *clsList to be volatile */ if (UNLIKELY(top != *clsList)) { top = *clsList; continue; } if (top) { newClass->m_cachedOffset = top->m_cachedOffset; } else { newClass->m_cachedOffset = Transl::TargetCache::allocKnownClass(preClass->name()); } newClass->m_nextClass = top; Util::compiler_membar(); *const_cast<Class**>(clsList) = newClass.get(); newClass.get()->incAtomicCount(); newClass.get()->setCached(); DEBUGGER_ATTACHED_ONLY(phpDefClassHook(newClass.get())); return newClass.get(); } }