/* * 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 c_Closure::init(int numArgs, ActRec* ar, TypedValue* sp) { auto const cls = getVMClass(); auto const invokeFunc = getInvokeFunc(); if (invokeFunc->cls()) { setThisOrClass(ar->getThisOrClass()); if (invokeFunc->isStatic()) { if (!hasClass()) { setClass(getThisUnchecked()->getVMClass()); } } else if (!hasClass()) { getThisUnchecked()->incRefCount(); } } else { hdr()->ctx = nullptr; } /* * Copy the use vars to instance variables, and initialize any * instance properties that are for static locals to KindOfUninit. */ auto const numDeclProperties = cls->numDeclProperties(); assertx(numDeclProperties - numArgs == getInvokeFunc()->numStaticLocals()); auto beforeCurUseVar = sp + numArgs; auto curProperty = getUseVars(); int i = 0; assertx(numArgs <= numDeclProperties); for (; i < numArgs; i++) { // teleport the references in here so we don't incref tvCopy(*--beforeCurUseVar, *curProperty++); } for (; i < numDeclProperties; ++i) { tvWriteUninit(*curProperty++); } }
static Array HHVM_METHOD(Closure, __debugInfo) { auto closure = c_Closure::fromObject(this_); Array ret = Array::Create(); // Serialize 'use' parameters. if (auto useVars = closure->getUseVars()) { Array use; auto cls = this_->getVMClass(); auto propsInfo = cls->declProperties(); auto nProps = cls->numDeclProperties(); for (size_t i = 0; i < nProps; ++i) { auto value = &useVars[i]; use.setWithRef(Variant(StrNR(propsInfo[i].name)), tvAsCVarRef(value)); } if (!use.empty()) { ret.set(s_static, use); } } auto const func = closure->getInvokeFunc(); // Serialize function parameters. if (auto nParams = func->numParams()) { Array params; auto lNames = func->localNames(); for (int i = 0; i < nParams; ++i) { auto str = String::attach( StringData::Make(s_varprefix.get(), lNames[i]) ); bool optional = func->params()[i].phpCode; if (auto mi = func->methInfo()) { optional = optional || mi->parameters[i]->valueText; } params.set(str, optional ? s_optional : s_required); } ret.set(s_parameter, params); } // Serialize 'this' object. if (closure->hasThis()) { ret.set(s_this, Object(closure->getThis())); } return ret; }
void cgNewInstanceRaw(IRLS& env, const IRInstruction* inst) { auto const dst = dstLoc(env, inst, 0).reg(); auto const cls = inst->extra<NewInstanceRaw>()->cls; auto const size = ObjectData::sizeForNProps(cls->numDeclProperties()); auto const callSpec = size <= kMaxSmallSize ? CallSpec::direct(ObjectData::newInstanceRaw) : CallSpec::direct(ObjectData::newInstanceRawBig); auto const args = argGroup(env, inst) .imm(reinterpret_cast<uintptr_t>(cls)) .imm(size); cgCallHelper(vmain(env), env, callSpec, callDest(dst), SyncOptions::Sync, args); }
void cgInitObjProps(IRLS& env, const IRInstruction* inst) { auto const cls = inst->extra<InitObjProps>()->cls; auto const obj = srcLoc(env, inst, 0).reg(); auto& v = vmain(env); // Set the attributes, if any. auto const odAttrs = cls->getODAttrs(); if (odAttrs) { static_assert(sizeof(ObjectData::Attribute) == 2, "Codegen expects 2-byte ObjectData attributes"); assertx(!(odAttrs & 0xffff0000)); v << orwim{odAttrs, obj[ObjectData::attributeOff()], v.makeReg()}; } // Initialize the properties. auto const nprops = cls->numDeclProperties(); if (nprops > 0) { if (cls->pinitVec().size() == 0) { // If the Class has no 86pinit property-initializer functions, we can // just copy the initial values from a data member on the Class. implInitObjPropsFast(v, env, inst, obj, cls, nprops); } else { // Load the Class's propInitVec from the target cache. We know it's // already been initialized as a pre-condition on this op. auto const propHandle = cls->propHandle(); assertx(rds::isNormalHandle(propHandle)); auto const propInitVec = v.makeReg(); auto const propData = v.makeReg(); v << load{Vreg(rvmtl())[propHandle], propInitVec}; v << load{propInitVec[Class::PropInitVec::dataOff()], propData}; auto const propsOff = sizeof(ObjectData) + cls->builtinODTailSize(); auto args = argGroup(env, inst) .addr(obj, safe_cast<int32_t>(propsOff)) .reg(propData); if (!cls->hasDeepInitProps()) { cgCallHelper(v, env, CallSpec::direct(memcpy), kVoidDest, SyncOptions::None, args.imm(cellsToBytes(nprops))); } else { cgCallHelper(v, env, CallSpec::direct(deepInitHelper), kVoidDest, SyncOptions::None, args.imm(nprops)); } } } }
Object APCObject::createObject() const { auto cls = m_cls.left(); assert(cls != nullptr); auto obj = Object::attach( m_fast_init ? ObjectData::newInstanceNoPropInit(const_cast<Class*>(cls)) : ObjectData::newInstance(const_cast<Class*>(cls)) ); auto const numProps = cls->numDeclProperties(); auto const objProp = obj->propVec(); auto const apcProp = persistentProps(); if (m_fast_init) { // re-entry is possible while we're executing toLocal() on each // property, so heap inspectors may see partially initid objects // not yet exposed to PHP. unsigned i = 0; try { for (; i < numProps; ++i) { new (objProp + i) Variant(apcProp[i]->toLocal()); } } catch (...) { for (; i < numProps; ++i) { new (objProp + i) Variant(); } throw; } } else { for (unsigned i = 0; i < numProps; ++i) { tvAsVariant(&objProp[i]) = apcProp[i]->toLocal(); } } if (UNLIKELY(numProps < m_propCount)) { auto dynProps = apcProp[numProps]; assert(dynProps->kind() == APCKind::StaticArray || dynProps->kind() == APCKind::UncountedArray || dynProps->kind() == APCKind::SharedArray); obj->setDynPropArray(dynProps->toLocal().asCArrRef()); } if (!m_no_wakeup) obj->invokeWakeup(); return obj; }
Object APCObject::createObject() const { auto cls = m_cls.left(); assert(cls != nullptr); auto obj = Object::attach( m_fast_init ? ObjectData::newInstanceNoPropInit(const_cast<Class*>(cls)) : ObjectData::newInstance(const_cast<Class*>(cls)) ); auto const numProps = cls->numDeclProperties(); auto const objProp = obj->propVec(); auto const apcProp = persistentProps(); if (m_fast_init) { unsigned i = 0; try { while (i < numProps) { new (objProp + i) Variant(apcProp[i]->toLocal()); ++i; } } catch (...) { while (i < numProps) { new (objProp + i) Variant(); ++i; } throw; } } else { for (unsigned i = 0; i < numProps; ++i) { tvAsVariant(&objProp[i]) = apcProp[i]->toLocal(); } } if (UNLIKELY(numProps < m_propCount)) { auto dynProps = apcProp[numProps]; assert(dynProps->kind() == APCKind::StaticArray || dynProps->kind() == APCKind::UncountedArray || dynProps->kind() == APCKind::SharedArray); obj->setDynPropArray(dynProps->toLocal().asCArrRef()); } if (!m_no_wakeup) obj->invokeWakeup(); return obj; }
ObjectData* c_Closure::clone() { auto const cls = getVMClass(); auto ret = c_Closure::fromObject(closureInstanceCtorRepoAuth(cls)); ret->hdr()->ctx = hdr()->ctx; if (auto t = getThis()) { t->incRefCount(); } auto src = getUseVars(); auto dest = ret->getUseVars(); auto const nProps = cls->numDeclProperties(); auto const stop = src + nProps; for (; src != stop; ++src, ++dest) { tvDup(*src, *dest); } return ret; }