void emitContEnter(IRGS& env) { auto const returnOffset = nextBcOff(env); assertx(curClass(env)); assertx(curClass(env)->classof(c_AsyncGenerator::classof()) || curClass(env)->classof(c_Generator::classof())); assertx(curFunc(env)->contains(returnOffset)); auto isAsync = curClass(env)->classof(c_AsyncGenerator::classof()); // Load generator's FP and resume address. auto const genObj = ldThis(env); auto const genFp = gen(env, LdContActRec, IsAsyncData(isAsync), genObj); auto resumeAddr = gen(env, LdContResumeAddr, IsAsyncData(isAsync), genObj); // Make sure function enter hook is called if needed. auto const exitSlow = makeExitSlow(env); gen(env, CheckSurpriseFlags, exitSlow, fp(env)); // Exit to interpreter if resume address is not known. resumeAddr = gen(env, CheckNonNull, exitSlow, resumeAddr); spillStack(env); env.irb->exceptionStackBoundary(); auto returnBcOffset = returnOffset - curFunc(env)->base(); gen( env, ContEnter, ContEnterData { offsetFromIRSP(env, BCSPOffset{0}), returnBcOffset }, sp(env), fp(env), genFp, resumeAddr ); }
void emitContEnter(HTS& env) { auto const returnOffset = nextBcOff(env); assert(curClass(env)); assert(curClass(env)->classof(c_AsyncGenerator::classof()) || curClass(env)->classof(c_Generator::classof())); assert(curFunc(env)->contains(returnOffset)); // Load generator's FP and resume address. auto const genObj = ldThis(env); auto const genFp = gen(env, LdContActRec, genObj); auto resumeAddr = gen(env, LdContResumeAddr, genObj); // Make sure function enter hook is called if needed. auto const exitSlow = makeExitSlow(env); gen(env, CheckSurpriseFlags, exitSlow); // Exit to interpreter if resume address is not known. resumeAddr = gen(env, CheckNonNull, exitSlow, resumeAddr); // Sync stack. auto const stack = spillStack(env); // Enter generator. auto returnBcOffset = returnOffset - curFunc(env)->base(); gen(env, ContEnter, stack, fp(env), genFp, resumeAddr, cns(env, returnBcOffset)); }
SSATmp* ldClsPropAddr(IRGS& env, SSATmp* ssaCls, SSATmp* ssaName, bool raise) { /* * We can use ldClsPropAddrKnown if either we know which property it is and * that it is visible && accessible, or we know it is a property on this * class itself. */ bool const sPropKnown = [&] { if (!ssaName->hasConstVal()) return false; auto const propName = ssaName->strVal(); if (!ssaCls->hasConstVal()) return false; auto const cls = ssaCls->clsVal(); if (!classIsPersistentOrCtxParent(env, cls)) return false; auto const lookup = cls->findSProp(curClass(env), propName); return lookup.prop != kInvalidSlot && lookup.accessible; }(); if (sPropKnown) { return ldClsPropAddrKnown(env, ssaCls->clsVal(), ssaName->strVal()); } return gen( env, raise ? LdClsPropAddrOrRaise : LdClsPropAddrOrNull, ssaCls, ssaName, cns(env, curClass(env)) ); }
void emitContValid(IRGS& env) { assertx(curClass(env)); assertx(curClass(env)->classof(c_AsyncGenerator::classof()) || curClass(env)->classof(c_Generator::classof())); auto const cont = ldThis(env); push(env, gen(env, ContValid, IsAsyncData(curClass(env)->classof(c_AsyncGenerator::classof())), cont)); }
void emitContCheck(HTS& env, int32_t checkStarted) { assert(curClass(env)); assert(curClass(env)->classof(c_AsyncGenerator::classof()) || curClass(env)->classof(c_Generator::classof())); auto const cont = ldThis(env); gen(env, ContPreNext, makeExitSlow(env), cont, cns(env, static_cast<bool>(checkStarted))); }
/* * The CreateCl opcode is specified as not being allowed before the * class it creates exists, and closure classes are always unique. * * This means even if we're not in RepoAuthoritative mode, as long as * this code is reachable it will always use the same closure Class*, * so we can just burn it into the TC without using RDS. */ void emitCreateCl(HTS& env, int32_t numParams, const StringData* clsName) { auto const cls = Unit::lookupClassOrUniqueClass(clsName); auto const invokeFunc = cls->lookupMethod(s_uuinvoke.get()); auto const clonedFunc = invokeFunc->cloneAndSetClass( const_cast<Class*>(curClass(env)) ); assert(cls && (cls->attrs() & AttrUnique)); auto const closure = allocObjFast(env, cls); gen(env, IncRef, closure); auto const ctx = [&]{ if (!curClass(env)) return cns(env, nullptr); auto const ldctx = gen(env, LdCtx, fp(env)); if (invokeFunc->attrs() & AttrStatic) { return gen(env, ConvClsToCctx, gen(env, LdClsCtx, ldctx)); } gen(env, IncRefCtx, ldctx); return ldctx; }(); gen(env, StClosureCtx, closure, ctx); gen(env, StClosureFunc, FuncData(clonedFunc), closure); SSATmp* args[numParams]; for (int32_t i = 0; i < numParams; ++i) { args[numParams - i - 1] = popF(env); } int32_t propId = 0; for (; propId < numParams; ++propId) { gen( env, StClosureArg, PropByteOffset(cls->declPropOffset(propId)), closure, args[propId] ); } // Closure static variables are per instance, and need to start // uninitialized. After numParams use vars, the remaining instance // properties hold any static locals. assert(cls->numDeclProperties() == clonedFunc->numStaticLocals() + numParams); for (int32_t numDeclProperties = cls->numDeclProperties(); propId < numDeclProperties; ++propId) { gen( env, StClosureArg, PropByteOffset(cls->declPropOffset(propId)), closure, cns(env, Type::Uninit) ); } push(env, closure); }
void Injection::execute() const { if (m_builtin) { assert(m_callback); // Execute function in runtime m_callback(m_arg); return; } // Execute php code piece TypedValue retval; VarEnv *varEnv = nullptr; ActRec *cfpSave = nullptr; ObjectData *this_ = nullptr; Class *cls = nullptr; ActRec *fp = g_vmContext->getFP(); if (fp) { if (!fp->hasVarEnv()) { fp->m_varEnv = VarEnv::createLazyAttach(fp); } varEnv = fp->m_varEnv; cfpSave = varEnv->getCfp(); if (fp->hasThis()) { this_ = fp->getThis(); } else if (fp->hasClass()) { cls = fp->getClass(); } } // Note: For now we don't merge analysis code's class and function. // Later we might decide to do so g_vmContext->invokeFunc(&retval, m_unit->getMain(curClass()), null_array, this_, cls, varEnv); if (varEnv) { varEnv->setCfp(cfpSave); } }
void emitInitProp(IRGS& env, const StringData* propName, InitPropOp op) { auto const val = popC(env); auto const ctx = curClass(env); SSATmp* base; Slot idx = 0; switch (op) { case InitPropOp::Static: { // For sinit, the context class is always the same as the late-bound // class, so we can just use curClass(). auto const handle = ctx->sPropHandle(ctx->lookupSProp(propName)); base = gen( env, LdRDSAddr, RDSHandleData { handle }, TPtrToSPropCell ); } break; case InitPropOp::NonStatic: { // The above is not the case for pinit, so we need to load. auto const cctx = gen(env, LdCctx, fp(env)); auto const cls = gen(env, LdClsCtx, cctx); base = gen(env, LdClsInitData, cls); idx = ctx->lookupDeclProp(propName); } break; } gen(env, StElem, base, cns(env, idx * sizeof(TypedValue)), val); }
void emitContCurrent(HTS& env) { assert(curClass(env)); auto const cont = ldThis(env); gen(env, ContStartedCheck, makeExitSlow(env), cont); auto const offset = cns(env, offsetof(c_Generator, m_value)); auto const value = gen(env, LdContField, Type::Cell, cont, offset); pushIncRef(env, value); }
void emitParent(HTS& env) { auto const clss = curClass(env); if (clss == nullptr || clss->parent() == nullptr) { interpOne(env, Type::Cls, 0); } else { push(env, cns(env, clss->parent())); } }
void emitSelf(HTS& env) { auto const clss = curClass(env); if (clss == nullptr) { interpOne(env, Type::Cls, 0); } else { push(env, cns(env, clss)); } }
void emitContCurrent(IRGS& env) { assertx(curClass(env)); auto const cont = ldThis(env); gen(env, ContStartedCheck, IsAsyncData(false), makeExitSlow(env), cont); auto const offset = cns(env, offsetof(Generator, m_value) - Generator::objectOff()); auto const value = gen(env, LdContField, TCell, cont, offset); pushIncRef(env, value); }
void emitLateBoundCls(HTS& env) { auto const clss = curClass(env); if (!clss) { // no static context class, so this will raise an error interpOne(env, Type::Cls, 0); return; } auto const ctx = ldCtx(env); push(env, gen(env, LdClsCtx, ctx)); }
void emitCheckProp(IRGS& env, const StringData* propName) { auto const cctx = gen(env, LdCctx, fp(env)); auto const cls = gen(env, LdClsCtx, cctx); auto const propInitVec = gen(env, LdClsInitData, cls); auto const ctx = curClass(env); auto const idx = ctx->lookupDeclProp(propName); auto const curVal = gen(env, LdElem, propInitVec, cns(env, idx * sizeof(TypedValue))); push(env, gen(env, IsNType, TUninit, curVal)); }
void emitBareThis(HTS& env, BareThisOp subop) { if (!curClass(env)) { interpOne(env, Type::InitNull, 0); // will raise notice and push null return; } auto const ctx = gen(env, LdCtx, fp(env)); if (subop == BareThisOp::NeverNull) { env.irb->setThisAvailable(); } else { gen(env, CheckCtxThis, makeExitSlow(env), ctx); } pushIncRef(env, gen(env, CastCtxThis, ctx)); }
void emitInitThisLoc(HTS& env, int32_t id) { if (!curClass(env)) { // Do nothing if this is null return; } auto const ldrefExit = makeExit(env); auto const oldLoc = ldLoc(env, id, ldrefExit, DataTypeCountness); auto const ctx = gen(env, LdCtx, fp(env)); gen(env, CheckCtxThis, makeExitSlow(env), ctx); auto const this_ = gen(env, CastCtxThis, ctx); gen(env, IncRef, this_); stLocRaw(env, id, fp(env), this_); gen(env, DecRef, oldLoc); }
void emitContValid(HTS& env) { assert(curClass(env)); auto const cont = ldThis(env); push(env, gen(env, ContValid, cont)); }