void c_Closure::init(int numArgs, ActRec* ar, TypedValue* sp) { auto const invokeFunc = getVMClass()->lookupMethod(s_uuinvoke.get()); m_ctx = ar->m_this; if (ar->hasThis()) { if (invokeFunc->isStatic()) { // Only set the class for static closures. setClass(ar->getThis()->getVMClass()); } else { ar->getThis()->incRefCount(); } } /* * Copy the use vars to instance variables, and initialize any * instance properties that are for static locals to KindOfUninit. */ auto const numDeclProperties = getVMClass()->numDeclProperties(); assert(numDeclProperties - numArgs == getInvokeFunc()->numStaticLocals()); TypedValue* beforeCurUseVar = sp + numArgs; TypedValue* curProperty = propVec(); int i = 0; assert(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++); } }
/** * sp points to the last use variable on the stack. * returns the closure so that translator-x64 can just return "rax". */ c_Closure* c_Closure::init(int numArgs, ActRec* ar, TypedValue* sp) { static StringData* invokeName = StringData::GetStaticString("__invoke"); Func* invokeFunc = getVMClass()->lookupMethod(invokeName); if (invokeFunc->attrs() & AttrStatic) { // Only set the class for static closures m_thisOrClass = (ObjectData*)(intptr_t(ar->m_func->cls()) | 1LL); } else { // I don't care if it is a $this or a late bound class because we will just // put it back in the same place on an ActRec. m_thisOrClass = ar->m_this; if (ar->hasThis()) { ar->getThis()->incRefCount(); } } // Change my __invoke's m_cls to be the same as my creator's Class* scope = ar->m_func->cls(); m_func = invokeFunc->cloneAndSetClass(scope); // copy the props to instance variables assert(m_cls->numDeclProperties() == numArgs); TypedValue* beforeCurUseVar = sp + numArgs; TypedValue* curProperty = propVec(); for (int i = 0; i < numArgs; i++) { // teleport the references in here so we don't incref *curProperty++ = *--beforeCurUseVar; } return this; }
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++); } }
void MemoryManager::sweep() { assert(!sweeping()); if (debug) checkHeap(); if (debug) traceHeap(); m_sweeping = true; SCOPE_EXIT { m_sweeping = false; }; DEBUG_ONLY size_t num_sweepables = 0, num_natives = 0; // iterate until both sweep lists are empty. Entries can be added or // removed from either list during sweeping. do { while (!m_sweepables.empty()) { num_sweepables++; auto obj = m_sweepables.next(); obj->unregister(); obj->sweep(); } while (!m_natives.empty()) { num_natives++; assert(m_natives.back()->sweep_index == m_natives.size() - 1); auto node = m_natives.back(); m_natives.pop_back(); auto obj = Native::obj(node); auto ndi = obj->getVMClass()->getNativeDataInfo(); ndi->sweep(obj); // trash the native data but leave the header and object parsable assert(memset(node+1, kSmartFreeFill, node->obj_offset - sizeof(*node))); } } while (!m_sweepables.empty()); TRACE(1, "sweep: sweepable %lu native %lu\n", num_sweepables, num_natives); if (debug) checkHeap(); }
void c_Closure::init(int numArgs, ActRec* ar, TypedValue* sp) { auto const invokeFunc = getVMClass()->lookupMethod(s_uuinvoke.get()); m_thisOrClass = ar->m_this; if (ar->hasThis()) { if (invokeFunc->attrs() & AttrStatic) { // Only set the class for static closures. m_thisOrClass = reinterpret_cast<ObjectData*>( reinterpret_cast<intptr_t>(ar->getThis()->getVMClass()) | 1LL ); } else { ar->getThis()->incRefCount(); } } // Change my __invoke's m_cls to be the same as my creator's Class* scope = ar->m_func->cls(); m_func = invokeFunc->cloneAndSetClass(scope); // copy the props to instance variables assert(m_cls->numDeclProperties() == numArgs); TypedValue* beforeCurUseVar = sp + numArgs; TypedValue* curProperty = propVec(); for (int i = 0; i < numArgs; i++) { // teleport the references in here so we don't incref tvCopy(*--beforeCurUseVar, *curProperty++); } }
Array c_Closure::t___debuginfo() { Array ret = Array::Create(); // Serialize 'use' parameters. if (auto propValues = propVec()) { Array use; auto propsInfo = getVMClass()->declProperties(); for (size_t i = 0; i < getVMClass()->numDeclProperties(); ++i) { auto value = &propValues[i]; use.setWithRef(Variant(StrNR(propsInfo[i].name)), tvAsCVarRef(value)); } if (!use.empty()) { ret.set(s_static, use); } } auto const func = getInvokeFunc(); // Serialize function parameters. if (func->numParams()) { Array params; for (int i = 0; i < func->numParams(); ++i) { auto str = String::attach( StringData::Make(s_varprefix.get(), func->localNames()[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 (hasThis()) { ret.set(s_this, Object(getThis())); } return ret; }
void c_Continuation::call_raise(ObjectData* e) { assert(SystemLib::s_continuationRaiseFunc == getVMClass()->lookupMethod(s_raise.get())); assert(e); assert(e->instanceof(SystemLib::s_ExceptionClass)); Cell arg; arg.m_type = KindOfObject; arg.m_data.pobj = e; g_vmContext->invokeContFunc(SystemLib::s_continuationRaiseFunc, this, &arg); }
void sweepNativeData() { for (auto node = s_sweep; node;) { auto obj = reinterpret_cast<ObjectData*>(node + 1); auto ndi = obj->getVMClass()->getNativeDataInfo(); assert(ndi->sweep); ndi->sweep(obj); node = node->next; assert(invalidateNativeData(obj, ndi)); } s_sweep = nullptr; }
Object c_AwaitAllWaitHandle::ti_frommap(const Variant& dependencies) { if (LIKELY(dependencies.isObject())) { auto obj = dependencies.getObjectData(); if (LIKELY(obj->isCollection() && isMapCollection(obj->collectionType()))) { assertx(collections::isType(obj->getVMClass(), CollectionType::Map, CollectionType::ImmMap)); return FromMap(static_cast<BaseMap*>(obj)); } } failMap(); }
Object c_AwaitAllWaitHandle::ti_fromvector(const Variant& dependencies) { if (LIKELY(dependencies.isObject())) { auto obj = dependencies.getObjectData(); if (LIKELY(obj->isCollection() && isVectorCollection(obj->collectionType()))) { assertx(collections::isType(obj->getVMClass(), CollectionType::Vector, CollectionType::ImmVector)); return FromVector(static_cast<BaseVector*>(obj)); } } failVector(); }
void sweepNativeData(std::vector<NativeNode*>& natives) { while (!natives.empty()) { assert(natives.back()->sweep_index == natives.size() - 1); auto node = natives.back(); natives.pop_back(); auto obj = Native::obj(node); auto ndi = obj->getVMClass()->getNativeDataInfo(); assert(ndi->sweep); assert(node->obj_offset == ndsize(ndi)); ndi->sweep(obj); assert(invalidateNativeData(obj, ndi)); } }
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; }
Object HHVM_STATIC_METHOD(AwaitAllWaitHandle, fromMap, const Variant& dependencies) { if (LIKELY(dependencies.isObject())) { auto obj = dependencies.getObjectData(); if (LIKELY(obj->isCollection() && isMapCollection(obj->collectionType()))) { assertx(collections::isType(obj->getVMClass(), CollectionType::Map, CollectionType::ImmMap)); return c_AwaitAllWaitHandle::Create<false>([=](auto fn) { MixedArray::IterateV(static_cast<BaseMap*>(obj)->arrayData(), fn); }); } } failMap(); }
void MemoryManager::sweep() { // running a gc-cycle at end of request exposes bugs, but otherwise is // somewhat pointless since we're about to free the heap en-masse. if (debug) collect("before MM::sweep"); assert(!sweeping()); m_sweeping = true; DEBUG_ONLY size_t num_sweepables = 0, num_natives = 0; // iterate until both sweep lists are empty. Entries can be added or // removed from either list during sweeping. do { while (!m_sweepables.empty()) { num_sweepables++; auto obj = m_sweepables.next(); obj->unregister(); obj->sweep(); } while (!m_natives.empty()) { num_natives++; assert(m_natives.back()->sweep_index == m_natives.size() - 1); auto node = m_natives.back(); m_natives.pop_back(); auto obj = Native::obj(node); auto ndi = obj->getVMClass()->getNativeDataInfo(); ndi->sweep(obj); // trash the native data but leave the header and object parsable assert(memset(node+1, kSmallFreeFill, node->obj_offset - sizeof(*node))); } } while (!m_sweepables.empty()); DEBUG_ONLY auto napcs = m_apc_arrays.size(); FTRACE(1, "sweep: sweepable {} native {} apc array {}\n", num_sweepables, num_natives, napcs); // decref apc arrays referenced by this request. This must happen here // (instead of in resetAllocator), because the sweep routine may use // g_context. while (!m_apc_arrays.empty()) { auto a = m_apc_arrays.back(); m_apc_arrays.pop_back(); a->sweep(); if (debug) a->m_sweep_index = kInvalidSweepIndex; } if (debug) checkHeap("after MM::sweep"); }
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; }
void MemoryManager::sweep() { assert(!sweeping()); if (debug) checkHeap(); collect(); m_sweeping = true; SCOPE_EXIT { m_sweeping = false; }; DEBUG_ONLY size_t num_sweepables = 0, num_natives = 0; // iterate until both sweep lists are empty. Entries can be added or // removed from either list during sweeping. do { while (!m_sweepables.empty()) { num_sweepables++; auto obj = m_sweepables.next(); obj->unregister(); obj->sweep(); } while (!m_natives.empty()) { num_natives++; assert(m_natives.back()->sweep_index == m_natives.size() - 1); auto node = m_natives.back(); m_natives.pop_back(); auto obj = Native::obj(node); auto ndi = obj->getVMClass()->getNativeDataInfo(); ndi->sweep(obj); // trash the native data but leave the header and object parsable assert(memset(node+1, kSmallFreeFill, node->obj_offset - sizeof(*node))); } } while (!m_sweepables.empty()); DEBUG_ONLY auto napcs = m_apc_arrays.size(); FTRACE(1, "sweep: sweepable {} native {} apc array {}\n", num_sweepables, num_natives, napcs); if (debug) checkHeap(); // decref apc arrays referenced by this request. This must happen here // (instead of in resetAllocator), because the sweep routine may use // g_context. while (!m_apc_arrays.empty()) { auto a = m_apc_arrays.back(); m_apc_arrays.pop_back(); a->sweep(); } }
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; }
NEVER_INLINE void HashCollection::warnOnStrIntDup() const { req::hash_set<int64_t> seenVals; auto* eLimit = elmLimit(); for (auto* e = firstElm(); e != eLimit; e = nextElm(e, eLimit)) { int64_t newVal = 0; if (e->hasIntKey()) { newVal = e->ikey; } else { assert(e->hasStrKey()); // isStriclyInteger() puts the int value in newVal as a side effect. if (!e->skey->isStrictlyInteger(newVal)) continue; } if (seenVals.find(newVal) != seenVals.end()) { auto cls = getVMClass()->name()->toCppString(); auto pos = cls.rfind('\\'); if (pos != std::string::npos) { cls = cls.substr(pos + 1); } raise_warning( "%s::toArray() for a %s containing both int(%" PRId64 ") " "and string('%" PRId64 "')", cls.c_str(), toLower(cls).c_str(), newVal, newVal ); return; } seenVals.insert(newVal); } // Do nothing if no 'duplicates' were found. }
Object c_Closure::t_bindto(const Variant& newthis, const Variant& scope) { if (RuntimeOption::RepoAuthoritative && RuntimeOption::EvalAllowScopeBinding) { raise_warning("Closure binding is not supported in RepoAuthoritative mode"); return Object{}; } auto const cls = getVMClass(); auto const invoke = cls->getCachedInvoke(); ObjectData* od = nullptr; if (newthis.isObject()) { if (invoke->isStatic()) { raise_warning("Cannot bind an instance to a static closure"); } else { od = newthis.getObjectData(); } } else if (!newthis.isNull()) { raise_warning("Closure::bindto() expects parameter 1 to be object"); return Object{}; } auto const curscope = invoke->cls(); auto newscope = curscope; if (scope.isObject()) { newscope = scope.getObjectData()->getVMClass(); } else if (scope.isString()) { auto const className = scope.getStringData(); if (!className->equal(s_static.get())) { newscope = Unit::loadClass(className); if (!newscope) { raise_warning("Class '%s' not found", className->data()); return Object{}; } } } else if (scope.isNull()) { newscope = nullptr; } else { raise_warning("Closure::bindto() expects parameter 2 " "to be string or object"); return Object{}; } if (od && !newscope) { // Bound closures should be scoped. If no scope is specified, scope it to // the Closure class. newscope = static_cast<Class*>(c_Closure::classof()); } bool thisNotOfCtx = od && !od->getVMClass()->classof(newscope); if (!RuntimeOption::EvalAllowScopeBinding) { if (newscope != curscope) { raise_warning("Re-binding closure scopes is disabled"); return Object{}; } if (thisNotOfCtx) { raise_warning("Binding to objects not subclassed from closure " "context is disabled"); return Object{}; } } c_Closure* clone = Clone(this); clone->setClass(nullptr); Attr curattrs = invoke->attrs(); Attr newattrs = static_cast<Attr>(curattrs & ~AttrHasForeignThis); if (od) { od->incRefCount(); clone->setThis(od); if (thisNotOfCtx) { // If the bound $this is not a subclass of the context class, then we // have to pessimize translation. newattrs |= AttrHasForeignThis; } } else if (newscope) { // If we attach a scope to a function with no bound $this we need to make // the function static. newattrs |= AttrStatic; clone->setClass(newscope); } // If we are changing either the scope or the attributes of the closure, we // need to re-scope its Closure subclass. if (newscope != curscope || newattrs != curattrs) { assert(newattrs != AttrNone); auto newcls = cls->rescope(newscope, newattrs); clone->setVMClass(newcls); } return Object(clone); }
void c_Continuation::call_next() { const HPHP::Func* func = getVMClass()->lookupMethod(s_next.get()); g_vmContext->invokeContFunc(func, this); }
void c_Continuation::call_send(Cell& v) { const HPHP::Func* func = getVMClass()->lookupMethod(s_send.get()); g_vmContext->invokeContFunc(func, this, &v); }
void c_Continuation::call_send(Cell& v) { assert(SystemLib::s_continuationSendFunc == getVMClass()->lookupMethod(s_send.get())); g_vmContext->invokeContFunc(SystemLib::s_continuationSendFunc, this, &v); }
void c_DateTime::t___wakeup() { t___construct(o_get(s__date_time)); unsetProp(getVMClass(), s__date_time.get()); }