const Func* lookupImmutableMethod(const Class* cls, const StringData* name, bool& magicCall, bool staticLookup, const Func* ctxFunc, bool exactClass) { if (!cls) return nullptr; if (cls->attrs() & AttrInterface) return nullptr; auto ctx = ctxFunc->cls(); if (!(cls->attrs() & AttrUnique)) { if (!ctx || !ctx->classof(cls)) { return nullptr; } } const Func* func; LookupResult res = staticLookup ? g_context->lookupClsMethod(func, cls, name, nullptr, ctx, false) : g_context->lookupObjMethod(func, cls, name, ctx, false); if (res == LookupResult::MethodNotFound) return nullptr; if (func->isAbstract()) return nullptr; assertx(res == LookupResult::MethodFoundWithThis || res == LookupResult::MethodFoundNoThis || (staticLookup ? res == LookupResult::MagicCallStaticFound : res == LookupResult::MagicCallFound)); magicCall = res == LookupResult::MagicCallStaticFound || res == LookupResult::MagicCallFound; if (staticLookup) { if (magicCall) { if (ctx && !ctxFunc->isStatic() && (ctx->classof(cls) || cls->classof(ctx))) { // we might need to call __call instead return nullptr; } if (!exactClass && !(cls->attrs() & AttrNoOverride)) { // there might be a derived class which defines the method return nullptr; } } else if (!exactClass) { if (!func->isImmutableFrom(cls)) return nullptr; } } else if (!exactClass && !(func->attrs() & AttrPrivate)) { if (magicCall) { if (!(cls->attrs() & AttrNoOverride)) { return nullptr; } } else if (!func->isImmutableFrom(cls)) { return nullptr; } } return func; }
at *send_message(at *classname, at *obj, at *method, at *args) { class_t *cl = classof(obj); /* find superclass */ if (classname) { ifn (SYMBOLP(classname)) error(NIL, "not a class name", classname); while (cl && cl->classname != classname) cl = cl->super; ifn (cl) error(NIL, "cannot find class", classname); } /* send */ ifn (SYMBOLP(method)) error(NIL, "not a method name", method); struct hashelem *hx = _getmethod(cl, method); if (hx) return call_method(obj, hx, args); else if (method == at_pname) // special method? return NEW_STRING(cl->name(obj)); /* send -unknown */ hx = _getmethod(cl, at_unknown); if (hx) { at *arg = new_cons(method, new_cons(args, NIL)); return call_method(obj, hx, arg); } /* fail */ error(NIL, "method not found", method); }
void lush_delete(at *p) { if (!p || ZOMBIEP(p)) return; class_t *cl = classof(p); if (cl->dontdelete) error(NIL, "cannot delete this object", p); run_notifiers(p); if (cl->has_compiled_part) { assert(isa(p, object_class)); /* OO objects may have two parts */ /* lush_delete has to delete both of them */ object_t *obj = Mptr(p); struct CClass_object *cobj = obj->cptr; oostruct_dispose(obj); cobj->Vtbl->Cdestroy(cobj); } else { if (Class(p)->dispose) Mptr(p) = Class(p)->dispose(Mptr(p)); else Mptr(p) = NULL; } zombify(p); }
bool isa(at *p, const class_t *cl) { class_t *c = classof(p); while (c && c != cl) c = c->super; return c == cl; }
/* process_pending_events -- * Process currently pending events * by calling event-hook and event-idle * until no events are left. */ void process_pending_events(void) { MM_ENTER; int timer_fired = 0; call_spoll(); at *hndl = ev_peek(); for(;;) { while (hndl) { /* Call the handler method <handle> */ at *event = event_get(hndl, true); if (CONSP(event)) { class_t *cl = classof(hndl); at *m = getmethod(cl, at_handle); if (m) { at *args = new_cons(quote(event), NIL); send_message(NIL, hndl, at_handle, args); } } /* Check for more events */ call_spoll(); hndl = ev_peek(); } /* Check for timer events */ if (timer_fired) break; timer_fire(); timer_fired = 1; hndl = ev_peek(); } MM_EXIT; }
bool InliningDecider::canInlineAt(SrcKey callSK, const Func* callee) const { if (!callee || !RuntimeOption::EvalHHIREnableGenTimeInlining || RuntimeOption::EvalJitEnableRenameFunction || callee->attrs() & AttrInterceptable) { return false; } if (callee->cls()) { if (!classHasPersistentRDS(callee->cls())) { // if the callee's class is not persistent, its still ok // to use it if we're jitting into a method of a subclass auto ctx = callSK.func()->cls(); if (!ctx || !ctx->classof(callee->cls())) { return false; } } } else { auto const handle = callee->funcHandle(); if (handle == rds::kInvalidHandle || !rds::isPersistentHandle(handle)) { // if the callee isn't persistent, its still ok to // use it if its defined at the top level in the same // unit as the caller if (callee->unit() != callSK.unit() || !callee->top()) { return false; } } } // If inlining was disabled... don't inline. if (m_disabled) return false; // TODO(#3331014): We have this hack until more ARM codegen is working. if (arch() == Arch::ARM) return false; // We can only inline at normal FCalls. if (callSK.op() != Op::FCall && callSK.op() != Op::FCallD) { return false; } // Don't inline from resumed functions. The inlining mechanism doesn't have // support for these---it has no way to redefine stack pointers relative to // the frame pointer, because in a resumed function the frame pointer points // into the heap instead of into the eval stack. if (callSK.resumed()) return false; // TODO(#4238160): Inlining into pseudomain callsites is still buggy. if (callSK.func()->isPseudoMain()) return false; if (!isCalleeInlinable(callSK, callee) || !checkNumArgs(callSK, callee)) { return false; } return true; }
bool filename_t::equals (object_t* other) { ___CBTPUSH; bool result = this == other; if (!result) { result = other->getClass () == classof (filename_t) && strcmp (((filename_t*) other)->m_name, m_name) == 0; } ___CBTPOP; return result; }
icu::TimeZone* IntlTimeZone::ParseArg(CVarRef arg, const String& funcname, intl_error &err) { String tzstr; if (arg.isNull()) { tzstr = f_date_default_timezone_get(); } else if (arg.isObject()) { auto objarg = arg.toObject(); auto cls = objarg->getVMClass(); auto IntlTimeZone_Class = Unit::lookupClass(s_IntlTimeZone.get()); if (IntlTimeZone_Class && ((cls == IntlTimeZone_Class) || cls->classof(IntlTimeZone_Class))) { return IntlTimeZone::Get(objarg)->timezone()->clone(); } if (auto dtz = objarg.getTyped<c_DateTimeZone>(true, true)) { tzstr = dtz->t_getname(); } else { tzstr = arg.toString(); } } else { tzstr = arg.toString(); } UErrorCode error = U_ZERO_ERROR; icu::UnicodeString id; if (!Intl::ustring_from_char(id, tzstr, error)) { err.code = error; err.custom_error_message = funcname + String(": Time zone identifier given is not a " "valid UTF-8 string", CopyString); return nullptr; } auto ret = icu::TimeZone::createTimeZone(id); if (!ret) { err.code = U_MEMORY_ALLOCATION_ERROR; err.custom_error_message = funcname + String(": could not create time zone", CopyString); return nullptr; } icu::UnicodeString gottenId; if (ret->getID(gottenId) != id) { err.code = U_ILLEGAL_ARGUMENT_ERROR; err.custom_error_message = funcname + String(": no such time zone: '", CopyString) + arg.toString() + "'"; delete ret; return nullptr; } return ret; }
icu::TimeZone* IntlTimeZone::ParseArg(const Variant& arg, const String& funcname, IntlError* err) { String tzstr; if (arg.isNull()) { tzstr = f_date_default_timezone_get(); } else if (arg.isObject()) { auto objarg = arg.toObject(); auto cls = objarg->getVMClass(); auto IntlTimeZone_Class = Unit::lookupClass(s_IntlTimeZone.get()); if (IntlTimeZone_Class && ((cls == IntlTimeZone_Class) || cls->classof(IntlTimeZone_Class))) { return IntlTimeZone::Get(objarg.get())->timezone()->clone(); } if (objarg.instanceof(DateTimeZoneData::getClass())) { auto* dtz = Native::data<DateTimeZoneData>(objarg.get()); tzstr = dtz->getName(); } else { tzstr = arg.toString(); } } else { tzstr = arg.toString(); } UErrorCode error = U_ZERO_ERROR; icu::UnicodeString id; if (!ustring_from_char(id, tzstr, error)) { err->setError(error, "%s: Time zone identifier given is not a " "valid UTF-8 string", funcname.c_str()); return nullptr; } auto ret = icu::TimeZone::createTimeZone(id); if (!ret) { err->setError(U_MEMORY_ALLOCATION_ERROR, "%s: could not create time zone", funcname.c_str()); return nullptr; } icu::UnicodeString gottenId; if (ret->getID(gottenId) != id) { err->setError(U_ILLEGAL_ARGUMENT_ERROR, "%s: no such time zone: '%s'", funcname.c_str(), arg.toString().c_str()); delete ret; return nullptr; } return ret; }
/* process_pending_events -- Process currently pending events by calling event-hook and event-idle until no events are left. */ void process_pending_events(void) { at *hndl; at *event; int timer_fired = 0; call_spoll(); hndl = ev_peek(); for(;;) { while (hndl) { /* Call the handler method <handle> */ LOCK(hndl); event = event_get(hndl, TRUE); if (CONSP(event)) { at *cl = classof(hndl); if (EXTERNP(cl, &class_class)) { at *m = checksend(cl->Object, at_handle); if (m) { at *args = new_cons(event,NIL); UNLOCK(m); argeval_ptr = eval_nothing; m = send_message(NIL,hndl,at_handle,args); argeval_ptr = eval_std; UNLOCK(args); } UNLOCK(m); } UNLOCK(cl); } UNLOCK(event); UNLOCK(hndl); /* Check for more events */ call_spoll(); hndl = ev_peek(); } /* Check for timer events */ if (timer_fired) break; timer_fire(); timer_fired = 1; hndl = ev_peek(); } }
const Func* lookupImmutableCtor(const Class* cls, const Class* ctx) { if (!cls) return nullptr; auto const func = cls->getCtor(); if (func && !(func->attrs() & AttrPublic)) { auto fcls = func->cls(); if (fcls != ctx) { if (!ctx) return nullptr; if ((func->attrs() & AttrPrivate) || !(ctx->classof(fcls) || fcls->classof(ctx))) { return nullptr; } } } return func; }
static object_t *oostruct_dispose(object_t *obj) { /* oostruct gets called only once */ assert(!ZOMBIEP(obj->backptr)); obj->next_unreachable = NULL; int oldready = error_doc.ready_to_an_error; error_doc.ready_to_an_error = true; struct lush_context mycontext; context_push(&mycontext); int errflag = sigsetjmp(context->error_jump,1); if (errflag==0) { /* call all destructors for interpreted part */ /* destructors for compiled part are called */ /* by finalizer for compiled part */ at *f = NIL; class_t *cl = Class(obj->backptr); while (cl) { struct hashelem *hx = _getmethod(cl, at_destroy); cl = cl->super; if (! hx) break; else if (hx->function == f) continue; else if (classof(hx->function) == dh_class) break; call_method(obj->backptr, hx, NIL); f = hx->function; } } context_pop(); error_doc.ready_to_an_error = oldready; if (obj->cptr) obj->cptr->__lptr = NULL; zombify(obj->backptr); if (errflag) siglongjmp(context->error_jump, -1L); return obj; }
TypeConstraint applyConstraint(TypeConstraint tc, const TypeConstraint newTc) { tc.category = std::max(newTc.category, tc.category); if (newTc.wantArrayKind()) tc.setWantArrayKind(); if (newTc.wantClass()) { if (tc.wantClass()) { // It only makes sense to constrain tc with a class that's related to its // existing class, and we want to preserve the more derived of the two. auto cls1 = tc.desiredClass(); auto cls2 = newTc.desiredClass(); tc.setDesiredClass(cls1->classof(cls2) ? cls1 : cls2); } else { tc.setDesiredClass(newTc.desiredClass()); } } return tc; }
TEST(Type, SpecializedObjects) { auto const A = SystemLib::s_IteratorClass; auto const B = SystemLib::s_TraversableClass; EXPECT_TRUE(A->classof(B)); auto const obj = Type::Obj; auto const exactA = obj.specializeExact(A); auto const exactB = obj.specializeExact(B); auto const subA = obj.specialize(A); auto const subB = obj.specialize(B); EXPECT_EQ(exactA.getClass(), A); EXPECT_EQ(subA.getClass(), A); EXPECT_EQ(exactA.getExactClass(), A); EXPECT_EQ(subA.getExactClass(), nullptr); EXPECT_LE(exactA, exactA); EXPECT_LE(subA, subA); EXPECT_LT(exactA, obj); EXPECT_LT(subA, obj); EXPECT_LE(Type::Bottom, subA); EXPECT_LE(Type::Bottom, exactA); EXPECT_LT(exactA, subA); EXPECT_LT(exactA, subB); EXPECT_LT(subA, subB); EXPECT_FALSE(exactA <= exactB); EXPECT_FALSE(subA <= exactB); EXPECT_EQ(exactA & subA, exactA); EXPECT_EQ(subA & exactA, exactA); EXPECT_EQ(exactB & subB, exactB); EXPECT_EQ(subB & exactB, exactB); EXPECT_EQ(Type::Obj, Type::Obj - subA); // conservative EXPECT_EQ(subA, subA - exactA); // conservative }
GuardConstraint applyConstraint(GuardConstraint gc, GuardConstraint newGc) { gc.category = std::max(newGc.category, gc.category); if (newGc.wantArrayKind()) gc.setWantArrayKind(); if (newGc.wantClass()) { if (gc.wantClass()) { // It only makes sense to constrain gc with a class that's related to its // existing class, and we want to preserve the more derived of the two. auto cls1 = gc.desiredClass(); auto cls2 = newGc.desiredClass(); gc.setDesiredClass(cls1->classof(cls2) ? cls1 : cls2); } else { gc.setDesiredClass(newGc.desiredClass()); } } return gc; }
void oostruct_setslot(at *p, at *prop, at *val) { at *slot = Car(prop); ifn (SYMBOLP(slot)) error(NIL, "not a slot name", slot); prop = Cdr(prop); object_t *obj = Mptr(p); class_t *cl = Class(obj->backptr); for(int i=0; i<cl->num_slots; i++) if (slot == cl->slots[i]) { if (prop) setslot(&obj->slots[i], prop, val); else if (i<cl->num_cslots) { class_t *cl = classof(obj->slots[i]); cl->setslot(obj->slots[i], NIL, val); } else obj->slots[i] = val; return; } error(NIL, "not a slot", slot); }
const Func* lookupImmutableCtor(const Class* cls, const Class* ctx) { if (!cls || RuntimeOption::EvalJitEnableRenameFunction) return nullptr; if (!(cls->attrs() & AttrUnique)) { if (!ctx || !ctx->classof(cls)) { return nullptr; } } auto const func = cls->getCtor(); if (func && !(func->attrs() & AttrPublic)) { auto fcls = func->cls(); if (fcls != ctx) { if (!ctx) return nullptr; if ((func->attrs() & AttrPrivate) || !(ctx->classof(fcls) || fcls->classof(ctx))) { return nullptr; } } } return func; }
// Called by the proxy whenever its breakpoint list is updated. // Since this intended to be called when user input is received, it is not // performance critical. Also, in typical scenarios, the list is short. void phpSetBreakPoints(Eval::DebuggerProxy* proxy) { Eval::BreakPointInfoPtrVec bps; proxy->getBreakPoints(bps); for (unsigned int i = 0; i < bps.size(); i++) { Eval::BreakPointInfoPtr bp = bps[i]; bp->m_bindState = Eval::BreakPointInfo::Unknown; auto className = bp->getClass(); if (!className.empty()) { auto clsName = makeStaticString(className); auto cls = Unit::lookupClass(clsName); if (cls == nullptr) continue; bp->m_bindState = Eval::BreakPointInfo::KnownToBeInvalid; size_t numFuncs = cls->numMethods(); if (numFuncs == 0) continue; auto methodName = bp->getFunction(); Func* const* funcs = cls->methods(); for (size_t i = 0; i < numFuncs; ++i) { auto f = funcs[i]; if (!matchFunctionName(methodName, f)) continue; bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid; addBreakPointFuncEntry(f); break; } //TODO: what about superclass methods accessed via the derived class? //Task 2527229. continue; } auto funcName = bp->getFuncName(); if (!funcName.empty()) { auto fName = makeStaticString(funcName); Func* f = Unit::lookupFunc(fName); if (f == nullptr) continue; if (f->hasGeneratorAsBody() && !f->isAsync()) { // This function is a generator, and it's the original // function which has been turned into a stub which creates a // continuation. We want to set the breakpoint on the // continuation function instead. fName = makeStaticString(funcName + "$continuation"); f = Unit::lookupFunc(fName); if (f == nullptr) continue; } bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid; addBreakPointFuncEntry(f); continue; } auto fileName = bp->m_file; if (!fileName.empty()) { for (EvaledFilesMap::const_iterator it = g_vmContext->m_evaledFiles.begin(); it != g_vmContext->m_evaledFiles.end(); ++it) { auto efile = it->second; if (!Eval::BreakPointInfo::MatchFile(fileName, efile->getFileName())) { continue; } addBreakPointInUnit(bp, efile->unit()); break; } continue; } auto exceptionClassName = bp->getExceptionClass(); if (exceptionClassName == "@") { bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid; continue; } else if (!exceptionClassName.empty()) { auto expClsName = makeStaticString(exceptionClassName); auto cls = Unit::lookupClass(expClsName); if (cls != nullptr) { auto baseClsName = makeStaticString("Exception"); auto baseCls = Unit::lookupClass(baseClsName); if (baseCls != nullptr) { if (cls->classof(baseCls)) { bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid; } else { bp->m_bindState = Eval::BreakPointInfo::KnownToBeInvalid; } } } continue; } else { continue; } // If we get here, the break point is of a type that does // not need to be explicitly enabled in the VM. For example // a break point that get's triggered when the server starts // to process a page request. bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid; } }
void proxySetBreakPoints(DebuggerProxy* proxy) { std::vector<BreakPointInfoPtr> bps; proxy->getBreakPoints(bps); for (unsigned int i = 0; i < bps.size(); i++) { BreakPointInfoPtr bp = bps[i]; bp->m_bindState = BreakPointInfo::Unknown; auto className = bp->getClass(); if (!className.empty()) { auto clsName = makeStaticString(className); auto cls = Unit::lookupClass(clsName); if (cls == nullptr) continue; bp->m_bindState = BreakPointInfo::KnownToBeInvalid; size_t numFuncs = cls->numMethods(); if (numFuncs == 0) continue; auto methodName = bp->getFunction(); for (size_t i = 0; i < numFuncs; ++i) { auto f = cls->getMethod(i); if (!matchFunctionName(methodName, f)) continue; bp->m_bindState = BreakPointInfo::KnownToBeValid; phpAddBreakPointFuncEntry(f); break; } // TODO(#2527229): what about superclass methods accessed via the derived // class? continue; } auto funcName = bp->getFuncName(); if (!funcName.empty()) { auto fName = makeStaticString(funcName); Func* f = Unit::lookupFunc(fName); if (f == nullptr) continue; bp->m_bindState = BreakPointInfo::KnownToBeValid; phpAddBreakPointFuncEntry(f); continue; } auto fileName = bp->m_file; if (!fileName.empty()) { for (auto& kv : g_context->m_evaledFiles) { auto const unit = kv.second; if (!BreakPointInfo::MatchFile(fileName, unit->filepath()->toCppString())) { continue; } addBreakPointInUnit(bp, unit); break; } continue; } auto exceptionClassName = bp->getExceptionClass(); if (exceptionClassName == "@") { bp->m_bindState = BreakPointInfo::KnownToBeValid; continue; } else if (!exceptionClassName.empty()) { auto expClsName = makeStaticString(exceptionClassName); auto cls = Unit::lookupClass(expClsName); if (cls != nullptr) { auto baseClsName = makeStaticString("Exception"); auto baseCls = Unit::lookupClass(baseClsName); if (baseCls != nullptr) { if (cls->classof(baseCls)) { bp->m_bindState = BreakPointInfo::KnownToBeValid; } else { bp->m_bindState = BreakPointInfo::KnownToBeInvalid; } } } continue; } else { continue; } // If we get here, the break point is of a type that does // not need to be explicitly enabled in the VM. For example // a break point that get's triggered when the server starts // to process a page request. bp->m_bindState = BreakPointInfo::KnownToBeValid; } }