ALWAYS_INLINE bool DataWalker::visitTypedValue(TypedValue rval, DataFeature& features, PointerSet& visited, PointerMap* seenArrs) const { if (isRefType(rval.m_type)) { if (rval.m_data.pref->isReferenced()) { if (markVisited(rval.m_data.pref, features, visited)) { // Don't recurse forever; we already went down this path, and // stop the walk if we've already got everything we need. return canStopWalk(features); } // Right now consider it circular even if the referenced variant // only showed up in one spot. This could be revisted later. features.isCircular = true; if (canStopWalk(features)) return true; } rval = *rval.m_data.pref->cell(); } if (rval.m_type == KindOfObject) { features.hasObjectOrResource = true; traverseData(rval.m_data.pobj, features, visited); } else if (isArrayLikeType(rval.m_type)) { traverseData(rval.m_data.parr, features, visited, seenArrs); } else if (rval.m_type == KindOfResource) { features.hasObjectOrResource = true; } return canStopWalk(features); }
void cgCallBuiltin(IRLS& env, const IRInstruction* inst) { auto const extra = inst->extra<CallBuiltin>(); auto const callee = extra->callee; auto const returnType = inst->typeParam(); auto const funcReturnType = callee->returnType(); auto const returnByValue = callee->isReturnByValue(); auto const dstData = dstLoc(env, inst, 0).reg(0); auto const dstType = dstLoc(env, inst, 0).reg(1); auto& v = vmain(env); // Whether `t' is passed in/out of C++ as String&/Array&/Object&. auto const isReqPtrRef = [] (MaybeDataType t) { return isStringType(t) || isArrayLikeType(t) || t == KindOfObject || t == KindOfResource; }; if (FixupMap::eagerRecord(callee)) { auto const sp = srcLoc(env, inst, 1).reg(); auto const spOffset = cellsToBytes(extra->spOffset.offset); auto const& marker = inst->marker(); auto const pc = marker.fixupSk().unit()->entry() + marker.fixupBcOff(); auto const synced_sp = v.makeReg(); v << lea{sp[spOffset], synced_sp}; emitEagerSyncPoint(v, pc, rvmtl(), srcLoc(env, inst, 0).reg(), synced_sp); } int returnOffset = rds::kVmMInstrStateOff + offsetof(MInstrState, tvBuiltinReturn); auto args = argGroup(env, inst); if (!returnByValue) { if (isBuiltinByRef(funcReturnType)) { if (isReqPtrRef(funcReturnType)) { returnOffset += TVOFF(m_data); } // Pass the address of tvBuiltinReturn to the native function as the // location where it can construct the return Array, String, Object, or // Variant. args.addr(rvmtl(), returnOffset); args.indirect(); } } // The srcs past the first two (sp and fp) are the arguments to the callee. auto srcNum = uint32_t{2}; // Add the this_ or self_ argument for HNI builtins. if (callee->isMethod()) { if (callee->isStatic()) { args.ssa(srcNum); ++srcNum; } else { // Note that we don't support objects with vtables here (if they may need // a $this pointer adjustment). This should be filtered out during irgen // or before. args.ssa(srcNum); ++srcNum; } } // Add the func_num_args() value if needed. if (callee->attrs() & AttrNumArgs) { // If `numNonDefault' is negative, this is passed as an src. if (extra->numNonDefault >= 0) { args.imm((int64_t)extra->numNonDefault); } else { args.ssa(srcNum); ++srcNum; } } // Add the positional arguments. for (uint32_t i = 0; i < callee->numParams(); ++i, ++srcNum) { auto const& pi = callee->params()[i]; // Non-pointer and NativeArg args are passed by value. String, Array, // Object, and Variant are passed by const&, i.e. a pointer to stack memory // holding the value, so we expect PtrToT types for these. Pointers to // req::ptr types (String, Array, Object) need adjusting to point to // &ptr->m_data. if (TVOFF(m_data) && !pi.nativeArg && isReqPtrRef(pi.builtinType)) { assertx(inst->src(srcNum)->type() <= TPtrToGen); args.addr(srcLoc(env, inst, srcNum).reg(), TVOFF(m_data)); } else if (pi.nativeArg && !pi.builtinType && !callee->byRef(i)) { // This condition indicates a MixedTV (i.e., TypedValue-by-value) arg. args.typedValue(srcNum); } else { args.ssa(srcNum, pi.builtinType == KindOfDouble); } } auto dest = [&] () -> CallDest { if (isBuiltinByRef(funcReturnType)) { if (!returnByValue) return kVoidDest; // indirect return return funcReturnType ? callDest(dstData) // String, Array, or Object : callDest(dstData, dstType); // Variant } return funcReturnType == KindOfDouble ? callDestDbl(env, inst) : callDest(env, inst); }(); cgCallHelper(v, env, CallSpec::direct(callee->nativeFuncPtr()), dest, SyncOptions::Sync, args); // For primitive return types (int, bool, double) and returnByValue, the // return value is already in dstData/dstType. if (returnType.isSimpleType() || returnByValue) return; // For return by reference (String, Object, Array, Variant), the builtin // writes the return value into MInstrState::tvBuiltinReturn, from where it // has to be tested and copied. if (returnType.isReferenceType()) { // The return type is String, Array, or Object; fold nullptr to KindOfNull. assertx(isBuiltinByRef(funcReturnType) && isReqPtrRef(funcReturnType)); v << load{rvmtl()[returnOffset], dstData}; if (dstType.isValid()) { auto const sf = v.makeReg(); auto const rtype = v.cns(returnType.toDataType()); auto const nulltype = v.cns(KindOfNull); v << testq{dstData, dstData, sf}; v << cmovb{CC_Z, sf, rtype, nulltype, dstType}; } return; } if (returnType <= TCell || returnType <= TBoxedCell) { // The return type is Variant; fold KindOfUninit to KindOfNull. assertx(isBuiltinByRef(funcReturnType) && !isReqPtrRef(funcReturnType)); static_assert(KindOfUninit == 0, "KindOfUninit must be 0 for test"); v << load{rvmtl()[returnOffset + TVOFF(m_data)], dstData}; if (dstType.isValid()) { auto const rtype = v.makeReg(); v << loadb{rvmtl()[returnOffset + TVOFF(m_type)], rtype}; auto const sf = v.makeReg(); auto const nulltype = v.cns(KindOfNull); v << testb{rtype, rtype, sf}; v << cmovb{CC_Z, sf, rtype, nulltype, dstType}; } return; } not_reached(); }
bool tvMatchesRepoAuthType(TypedValue tv, RepoAuthType ty) { assert(tvIsPlausible(tv)); bool const initNull = tv.m_type == KindOfNull; using T = RepoAuthType::Tag; switch (ty.tag()) { case T::Uninit: return tv.m_type == KindOfUninit; case T::InitNull: return initNull; case T::OptBool: if (initNull) return true; // fallthrough case T::Bool: return tv.m_type == KindOfBoolean; case T::OptInt: if (initNull) return true; // fallthrough case T::Int: return tv.m_type == KindOfInt64; case T::OptDbl: if (initNull) return true; // fallthrough case T::Dbl: return tv.m_type == KindOfDouble; case T::OptRes: if (initNull) return true; // fallthrough case T::Res: return tv.m_type == KindOfResource; case T::OptObj: if (initNull) return true; // fallthrough case T::Obj: return tv.m_type == KindOfObject; case T::OptSStr: if (initNull) return true; // fallthrough case T::SStr: return isStringType(tv.m_type) && tv.m_data.pstr->isStatic(); case T::OptStr: if (initNull) return true; // fallthrough case T::Str: return isStringType(tv.m_type); case T::OptSArr: if (initNull) return true; // fallthrough case T::SArr: if (!isArrayType(tv.m_type) || !tv.m_data.parr->isStatic()) { return false; } if (auto const arr = ty.array()) { if (!tvMatchesArrayType(tv, arr)) return false; } return true; case T::OptArr: if (initNull) return true; // fallthrough case T::Arr: if (!isArrayType(tv.m_type)) return false; if (auto const arr = ty.array()) { if (!tvMatchesArrayType(tv, arr)) return false; } return true; case T::OptSVArr: if (initNull) return true; // fallthrough case T::SVArr: if (!isArrayType(tv.m_type) || !tv.m_data.parr->isStatic() || !tv.m_data.parr->isVArray()) { return false; } if (auto const arr = ty.array()) { if (!tvMatchesArrayType(tv, arr)) return false; } return true; case T::OptVArr: if (initNull) return true; // fallthrough case T::VArr: if (!isArrayType(tv.m_type) || !tv.m_data.parr->isVArray()) return false; if (auto const arr = ty.array()) { if (!tvMatchesArrayType(tv, arr)) return false; } return true; case T::OptSDArr: if (initNull) return true; // fallthrough case T::SDArr: if (!isArrayType(tv.m_type) || !tv.m_data.parr->isStatic() || !tv.m_data.parr->isDArray()) { return false; } if (auto const arr = ty.array()) { if (!tvMatchesArrayType(tv, arr)) return false; } return true; case T::OptDArr: if (initNull) return true; // fallthrough case T::DArr: if (!isArrayType(tv.m_type) || !tv.m_data.parr->isDArray()) return false; if (auto const arr = ty.array()) { if (!tvMatchesArrayType(tv, arr)) return false; } return true; case T::OptSVec: if (initNull) return true; // fallthrough case T::SVec: return isVecType(tv.m_type) && tv.m_data.parr->isStatic(); case T::OptVec: if (initNull) return true; // fallthrough case T::Vec: return isVecType(tv.m_type); case T::OptSDict: if (initNull) return true; // fallthrough case T::SDict: return isDictType(tv.m_type) && tv.m_data.parr->isStatic(); case T::OptDict: if (initNull) return true; // fallthrough case T::Dict: return isDictType(tv.m_type); case T::OptSKeyset: if (initNull) return true; // fallthrough case T::SKeyset: return isKeysetType(tv.m_type) && tv.m_data.parr->isStatic(); case T::OptKeyset: if (initNull) return true; // fallthrough case T::Keyset: return isKeysetType(tv.m_type); case T::Null: return initNull || tv.m_type == KindOfUninit; case T::OptSubObj: if (initNull) return true; // fallthrough case T::SubObj: { auto const cls = Unit::lookupClass(ty.clsName()); if (!cls) return false; return tv.m_type == KindOfObject && tv.m_data.pobj->getVMClass()->classof(cls); } case T::OptExactObj: if (initNull) return true; // fallthrough case T::ExactObj: { auto const cls = Unit::lookupClass(ty.clsName()); if (!cls) return false; return tv.m_type == KindOfObject && tv.m_data.pobj->getVMClass() == cls; } case T::InitUnc: if (tv.m_type == KindOfUninit) return false; // fallthrough case T::Unc: return !isRefcountedType(tv.m_type) || (tv.m_type == KindOfString && tv.m_data.pstr->isStatic()) || (isArrayLikeType(tv.m_type) && tv.m_data.parr->isStatic()); case T::OptArrKey: if (initNull) return true; // fallthrough case T::ArrKey: return isStringType(tv.m_type) || tv.m_type == KindOfInt64; case T::OptUncArrKey: if (initNull) return true; // fallthrough case T::UncArrKey: return (isStringType(tv.m_type) && !tv.m_data.pstr->isRefCounted()) || tv.m_type == KindOfInt64; case T::InitCell: if (tv.m_type == KindOfUninit) return false; // fallthrough case T::Cell: return tv.m_type != KindOfRef; case T::Ref: return tv.m_type == KindOfRef; case T::InitGen: if (tv.m_type == KindOfUninit) return false; // fallthrough case T::Gen: return true; } not_reached(); }