Example #1
0
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;
}
Example #3
0
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++);
  }
}
Example #4
0
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();
}
Example #5
0
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++);
  }
}
Example #6
0
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;
}
Example #7
0
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);
}
Example #8
0
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();
}
Example #11
0
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));
  }
}
Example #12
0
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");
}
Example #15
0
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;
}
Example #16
0
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();
  }
}
Example #17
0
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;
}
Example #18
0
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.
}
Example #19
0
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);
}
Example #22
0
void c_Continuation::call_send(Cell& v) {
  assert(SystemLib::s_continuationSendFunc ==
         getVMClass()->lookupMethod(s_send.get()));
  g_vmContext->invokeContFunc(SystemLib::s_continuationSendFunc, this, &v);
}
Example #23
0
void c_DateTime::t___wakeup() {
  t___construct(o_get(s__date_time));
  unsetProp(getVMClass(), s__date_time.get());
}