void c_Closure::init(int numArgs, ActRec* ar, TypedValue* sp) { auto const cls = getVMClass(); auto const invokeFunc = cls->lookupMethod(s_uuinvoke.get()); if (ar->hasThis()) { if (invokeFunc->isStatic()) { // Only set the class for static closures. setClass(ar->getThis()->getVMClass()); } else { setThis(ar->m_this); ar->getThis()->incRefCount(); } } else if (ar->hasClass()) { setClass(ar->getClass()); } else { m_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 Variant HHVM_METHOD(Closure, bindto, const Variant& newthis, const Variant& scope) { if (RuntimeOption::RepoAuthoritative && RuntimeOption::EvalAllowScopeBinding) { raise_warning("Closure binding is not supported in RepoAuthoritative mode"); return init_null_variant; } auto const cls = this_->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 init_null_variant; } 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 init_null_variant; } } } else if (scope.isNull()) { newscope = nullptr; } else { raise_warning("Closure::bindto() expects parameter 2 " "to be string or object"); return init_null_variant; } 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 init_null_variant; } if (thisNotOfCtx) { raise_warning("Binding to objects not subclassed from closure " "context is disabled"); return init_null_variant; } } auto cloneObj = this_->clone(); auto clone = c_Closure::fromObject(cloneObj); 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); } else { clone->setThis(nullptr); } // 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) { assertx(newattrs != AttrNone); auto newcls = cls->rescope(newscope, newattrs); cloneObj->setVMClass(newcls); } return Object{cloneObj}; }