Variant HHVM_FUNCTION(property_exists, const Variant& class_or_object,
                                       const String& property) {
  Class* cls = nullptr;
  ObjectData* obj = nullptr;
  if (class_or_object.isObject()) {
    obj = class_or_object.getObjectData();
    cls = obj->getVMClass();
    assert(cls);
  } else if (class_or_object.isString()) {
    cls = Unit::loadClass(class_or_object.toString().get());
    if (!cls) return false;
  } else {
    raise_warning(
      "First parameter must either be an object"
      " or the name of an existing class"
    );
    return Variant(Variant::NullInit());
  }

  auto const lookup = cls->getDeclPropIndex(cls, property.get());
  if (lookup.prop != kInvalidSlot) return true;

  if (obj &&
      UNLIKELY(obj->getAttribute(ObjectData::HasDynPropArr)) &&
      obj->dynPropArray()->nvGet(property.get())) {
    return true;
  }
  auto const propInd = cls->lookupSProp(property.get());
  return propInd != kInvalidSlot;
}
static void unserializeProp(VariableUnserializer* uns,
                            ObjectData* obj,
                            const String& key,
                            Class* ctx,
                            const String& realKey,
                            int nProp) {
  // Do a two-step look up
  auto const lookup = obj->getProp(ctx, key.get());
  Variant* t;

  if (!lookup.prop || !lookup.accessible) {
    // Dynamic property. If this is the first, and we're using MixedArray,
    // we need to pre-allocate space in the array to ensure the elements
    // dont move during unserialization.
    //
    // TODO(#2881866): this assumption means we can't do reallocations
    // when promoting kPackedKind -> kMixedKind.
    t = &obj->reserveProperties(nProp).lvalAt(realKey, AccessFlags::Key);
  } else {
    t = &tvAsVariant(lookup.prop);
  }

  if (UNLIKELY(IS_REFCOUNTED_TYPE(t->getRawType()))) {
    uns->putInOverwrittenList(*t);
  }

  unserializeVariant(*t, uns);
  if (!RuntimeOption::RepoAuthoritative) return;
  if (!Repo::get().global().HardPrivatePropInference) return;

  /*
   * We assume for performance reasons in repo authoriative mode that
   * we can see all the sets to private properties in a class.
   *
   * It's a hole in this if we don't check unserialization doesn't
   * violate what we've seen, which we handle by throwing if the repo
   * was built with this option.
   */
  auto const cls  = obj->getVMClass();
  auto const slot = cls->lookupDeclProp(key.get());
  if (UNLIKELY(slot == kInvalidSlot)) return;
  auto const repoTy = obj->getVMClass()->declPropRepoAuthType(slot);
  if (LIKELY(tvMatchesRepoAuthType(*t->asTypedValue(), repoTy))) {
    return;
  }

  auto msg = folly::format(
    "Property {} for class {} was deserialized with type ({}) that "
    "didn't match what we inferred in static analysis",
    key.data(),
    obj->getVMClass()->name()->data(),
    tname(t->asTypedValue()->m_type)
  ).str();
  throw Exception(msg);
}
Example #3
0
APCHandle::Pair APCObject::Construct(ObjectData* objectData) {
    // This function assumes the object and object/array down the tree
    // have no internal references and do not implement the serializable
    // interface.
    assert(!objectData->instanceof(SystemLib::s_SerializableClass));

    Array odProps;
    objectData->o_getArray(odProps);
    auto const propCount = odProps.size();

    auto size = sizeof(APCObject) + sizeof(Prop) * propCount;
    auto const apcObj = new (std::malloc(size)) APCObject(objectData, propCount);
    if (!propCount) return {apcObj->getHandle(), size};

    auto prop = apcObj->props();
    for (ArrayIter it(odProps); !it.end(); it.next(), ++prop) {
        Variant key(it.first());
        assert(key.isString());
        const Variant& value = it.secondRef();
        if (!value.isNull()) {
            auto val = APCHandle::Create(value, false, true, true);
            prop->val = val.handle;
            size += val.size;
        } else {
            prop->val = nullptr;
        }

        const String& keySD = key.asCStrRef();

        if (!keySD.empty() && *keySD.data() == '\0') {
            int32_t subLen = keySD.find('\0', 1) + 1;
            String cls = keySD.substr(1, subLen - 2);
            if (cls.size() == 1 && cls[0] == '*') {
                // Protected.
                prop->ctx = nullptr;
            } else {
                // Private.
                auto* ctx = Unit::lookupClass(cls.get());
                if (ctx && ctx->attrs() & AttrUnique) {
                    prop->ctx = ctx;
                } else {
                    prop->ctx = makeStaticString(cls.get());
                }
            }
            prop->name = makeStaticString(keySD.substr(subLen));
        } else {
            prop->ctx = nullptr;
            prop->name = makeStaticString(keySD.get());
        }
    }
    assert(prop == apcObj->props() + propCount);

    return {apcObj->getHandle(), size};
}
Example #4
0
static void compact(HPHP::VM::VarEnv* v, Array &ret, CVarRef var) {
  if (var.isArray()) {
    for (ArrayIter iter(var.getArrayData()); iter; ++iter) {
      compact(v, ret, iter.second());
    }
  } else {
    String varname = var.toString();
    if (!varname.empty() && v->lookup(varname.get()) != NULL) {
      ret.set(varname, *reinterpret_cast<Variant*>(v->lookup(varname.get())));
    }
  }
}
bool HHVM_FUNCTION(method_exists, const Variant& class_or_object,
                                  const String& method_name) {
  const Class* cls = get_cls(class_or_object);
  if (!cls) return false;
  if (cls->lookupMethod(method_name.get()) != NULL) return true;
  if (cls->attrs() & (AttrAbstract | AttrInterface)) {
    const Class::InterfaceMap& ifaces = cls->allInterfaces();
    for (int i = 0, size = ifaces.size(); i < size; i++) {
      if (ifaces[i]->lookupMethod(method_name.get())) return true;
    }
  }
  return false;
}
Example #6
0
NEVER_INLINE
APCHandle::Pair APCObject::ConstructSlow(ObjectData* objectData,
                                         ClassOrName name) {
  Array odProps;
  objectData->o_getArray(odProps);
  auto const propCount = odProps.size();

  auto size = sizeof(APCObject) + sizeof(Prop) * propCount;
  auto const apcObj = new (std::malloc(size)) APCObject(name, propCount);
  if (!propCount) return {apcObj->getHandle(), size};

  auto prop = apcObj->props();
  for (ArrayIter it(odProps); !it.end(); it.next(), ++prop) {
    Variant key(it.first());
    assert(key.isString());
    const Variant& value = it.secondRef();
    if (!value.isNull()) {
      auto val = APCHandle::Create(value, false, APCHandleLevel::Inner, true);
      prop->val = val.handle;
      size += val.size;
    } else {
      prop->val = nullptr;
    }

    const String& keySD = key.asCStrRef();

    if (!keySD.empty() && *keySD.data() == '\0') {
      int32_t subLen = keySD.find('\0', 1) + 1;
      String cls = keySD.substr(1, subLen - 2);
      if (cls.size() == 1 && cls[0] == '*') {
        // Protected.
        prop->ctx = nullptr;
      } else {
        // Private.
        auto* ctx = Unit::lookupClass(cls.get());
        if (ctx && ctx->attrs() & AttrUnique) {
          prop->ctx = ctx;
        } else {
          prop->ctx = makeStaticString(cls.get());
        }
      }
      prop->name = makeStaticString(keySD.substr(subLen));
    } else {
      prop->ctx = nullptr;
      prop->name = makeStaticString(keySD.get());
    }
  }
  assert(prop == apcObj->props() + propCount);

  return {apcObj->getHandle(), size};
}
Example #7
0
int FuncEmitter::parseNativeAttributes(Attr& attrs_) const {
  int ret = Native::AttrNone;

  auto it = userAttributes.find(s_native.get());
  assertx(it != userAttributes.end());
  const TypedValue userAttr = it->second;
  assertx(isArrayType(userAttr.m_type));
  for (ArrayIter it(userAttr.m_data.parr); it; ++it) {
    Variant userAttrVal = it.second();
    if (userAttrVal.isString()) {
      String userAttrStrVal = userAttrVal.toString();
      if (userAttrStrVal.get()->isame(s_actrec.get())) {
        ret |= Native::AttrActRec;
        attrs_ |= AttrMayUseVV;
      } else if (userAttrStrVal.get()->isame(s_nofcallbuiltin.get())) {
        attrs_ |= AttrNoFCallBuiltin;
      } else if (userAttrStrVal.get()->isame(s_variadicbyref.get())) {
        attrs_ |= AttrVariadicByRef;
      } else if (userAttrStrVal.get()->isame(s_noinjection.get())) {
        attrs_ |= AttrNoInjection;
      } else if (userAttrStrVal.get()->isame(s_numargs.get())) {
        ret |= Native::AttrTakesNumArgs;
      } else if (userAttrStrVal.get()->isame(s_opcodeimpl.get())) {
        ret |= Native::AttrOpCodeImpl;
      } else if (userAttrStrVal.get()->isame(s_readsCallerFrame.get())) {
        attrs_ |= AttrReadsCallerFrame;
      } else if (userAttrStrVal.get()->isame(s_writesCallerFrame.get())) {
        attrs_ |= AttrWritesCallerFrame;
      }
    }
  }
  return ret;
}
static bool findFileWrapper(const String& file, void* ctx) {
  ResolveIncludeContext* context = (ResolveIncludeContext*)ctx;
  assert(context->path.isNull());

  Stream::Wrapper* w = Stream::getWrapperFromURI(file);
  if (!dynamic_cast<FileStreamWrapper*>(w)) {
    if (w->stat(file, context->s) == 0) {
      context->path = file;
      return true;
    }
  }

  // handle file://
  if (file.substr(0, 7) == s_file_url) {
    return findFileWrapper(file.substr(7), ctx);
  }

  // TranslatePath() will canonicalize the path and also check
  // whether the file is in an allowed directory.
  String translatedPath = File::TranslatePathKeepRelative(file);
  if (file[0] != '/') {
    if (HPHP::Eval::FileRepository::findFile(translatedPath.get(),
                                             context->s)) {
      context->path = translatedPath;
      return true;
    }
    return false;
  }
  if (RuntimeOption::SandboxMode || !RuntimeOption::AlwaysUseRelativePath) {
    if (HPHP::Eval::FileRepository::findFile(translatedPath.get(),
                                             context->s)) {
      context->path = translatedPath;
      return true;
    }
  }
  string server_root(SourceRootInfo::GetCurrentSourceRoot());
  if (server_root.empty()) {
    server_root = string(g_vmContext->getCwd()->data());
    if (server_root.empty() || server_root[server_root.size() - 1] != '/') {
      server_root += "/";
    }
  }
  String rel_path(Util::relativePath(server_root, translatedPath.data()));
  if (HPHP::Eval::FileRepository::findFile(rel_path.get(),
                                           context->s)) {
    context->path = rel_path;
    return true;
  }
  return false;
}
Example #9
0
bool TestDebugger::getResponse(const string& path, string& result,
                               int port /* = -1 */,
                               const string& host /* = "" */) {
  String server = "http://";
  if (host.empty()) {
    server += f_php_uname("n");
  } else {
    server += host;
  }
  server += ":" + boost::lexical_cast<string>(port > 0 ? port : m_serverPort);
  server += "/" + path;
  printf("\n  Getting URL '%s'...\n", server.get()->data());
  Variant c = f_curl_init();
  f_curl_setopt(c, k_CURLOPT_URL, server);
  f_curl_setopt(c, k_CURLOPT_RETURNTRANSFER, true);
  f_curl_setopt(c, CURLOPT_TIMEOUT, 120);
  Variant res = f_curl_exec(c);
  if (same(res, false)) {
    printf("  Request failed\n");
    return false;
  }
  result = (std::string) res.toString();
  printf("  Request succeeded, returning '%s'\n", result.c_str());
  return true;
}
Array HHVM_FUNCTION(get_class_constants, const String& className) {
  auto const cls = Unit::loadClass(className.get());
  if (cls == NULL) {
    return Array::attach(MixedArray::MakeReserve(0));
  }

  auto const numConstants = cls->numConstants();
  ArrayInit arrayInit(numConstants, ArrayInit::Map{});

  auto const consts = cls->constants();
  for (size_t i = 0; i < numConstants; i++) {
    // Note: hphpc doesn't include inherited constants in
    // get_class_constants(), so mimic that behavior
    if (consts[i].cls == cls && !consts[i].isAbstract() &&
        !consts[i].isType()) {
      auto const name  = const_cast<StringData*>(consts[i].name.get());
      Cell value = consts[i].val;
      // Handle dynamically set constants
      if (value.m_type == KindOfUninit) {
        value = cls->clsCnsGet(consts[i].name);
      }
      assert(value.m_type != KindOfUninit);
      arrayInit.set(name, cellAsCVarRef(value));
    }
  }

  return arrayInit.toArray();
}
Example #11
0
Variant f_eval(CStrRef code_str) {
  String prefixedCode = concat("<?php ", code_str);
  Unit* unit = g_vmContext->compileEvalString(prefixedCode.get());
  TypedValue retVal;
  g_vmContext->invokeUnit(&retVal, unit);
  return tvAsVariant(&retVal);
}
Example #12
0
 inline std::u16string MakeAnything(ThingToMake<std::u16string>, JNIEnv& env, const String& string)
    {
     NullCheck(env, string.get());
     std::u16string result(jni::GetStringLength(env, *string), char16_t());
     jni::GetStringRegion(env, *string, 0, result);
     return result;
    }
bool String::operator==(const InternalType& it)
{
    if (const_cast<InternalType&>(it).isString() == false)
    {
        return false;
    }

    String* pS = const_cast<InternalType&>(it).getAs<types::String>();

    if (pS->getRows() != getRows() || pS->getCols() != getCols())
    {
        return false;
    }

    wchar_t **p1 = get();
    wchar_t **p2 = pS->get();

    for (int i = 0 ; i < getSize() ; i++)
    {
        if (wcscmp(p1[i], p2[i]) != 0)
        {
            return false;
        }
    }
    return true;
}
bool HHVM_FUNCTION(class_alias, const String& original, const String& alias,
                                bool autoload /* = true */) {
  auto const origClass =
    autoload ? Unit::loadClass(original.get())
             : Unit::lookupClass(original.get());
  if (!origClass) {
    raise_warning("Class %s not found", original.data());
    return false;
  }
  if (origClass->isBuiltin()) {
    raise_warning(
      "First argument of class_alias() must be a name of user defined class");
    return false;
  }

  return Unit::aliasClass(origClass, alias.get());
}
Example #15
0
Variant f_stream_resolve_include_path(const String& filename,
                                     CResRef context /* = null_object */) {
  struct stat s;
  String ret = Eval::resolveVmInclude(filename.get(), "", &s);
  if (ret.isNull()) {
    return false;
  }
  return ret;
}
Example #16
0
VarNR::VarNR(const String& v) {
  init(KindOfString);
  StringData *s = v.get();
  if (s) {
    m_data.pstr = s;
  } else {
    m_type = KindOfNull;
  }
}
Example #17
0
			/**
			 * Writes an utf-8 encoded string. The size is stored
			 * as dynamic uint.
			 * @param str The string to write.
			 */
			inline void write_dynamic_utf8_string(
				const String &str)
			{
				Uint64 size;

				size = str.get().size();

				write_dynamic_uint(size);
				write_utf8_string(str, size);
			}
Example #18
0
static String memcache_prepare_key(const String& var) {
  auto data = var.get()->mutableData();
  for (int i = 0; i < var.length(); i++) {
    // This is a stupid encoding since it causes collisions but it matches php5
    if (data[i] <= ' ') {
      data[i] = '_';
    }
  }
  return data;
}
Variant HHVM_FUNCTION(get_class_vars, const String& className) {
  const Class* cls = Unit::loadClass(className.get());
  if (!cls) {
    return false;
  }
  cls->initialize();


  auto const propInfo = cls->declProperties();

  auto const numDeclProps = cls->numDeclProperties();
  auto const numSProps    = cls->numStaticProperties();

  // The class' instance property initialization template is in different
  // places, depending on whether it has any request-dependent initializers
  // (i.e. constants)
  auto const& declPropInitVec = cls->declPropInit();
  auto const propVals = !cls->pinitVec().empty()
    ? cls->getPropData()
    : &declPropInitVec;

  assert(propVals != nullptr);
  assert(propVals->size() == numDeclProps);

  // For visibility checks
  CallerFrame cf;
  auto ctx = arGetContextClass(cf());

  ArrayInit arr(numDeclProps + numSProps, ArrayInit::Map{});

  for (size_t i = 0; i < numDeclProps; ++i) {
    auto const name = const_cast<StringData*>(propInfo[i].name.get());
    // Empty names are used for invisible/private parent properties; skip them.
    assert(name->size() != 0);
    if (Class::IsPropAccessible(propInfo[i], ctx)) {
      auto const value = &((*propVals)[i]);
      arr.set(name, tvAsCVarRef(value));
    }
  }

  for (auto const& sprop : cls->staticProperties()) {
    auto const lookup = cls->getSProp(ctx, sprop.name);
    if (lookup.accessible) {
      arr.set(
        const_cast<StringData*>(sprop.name.get()),
        tvAsCVarRef(lookup.prop)
      );
    }
  }

  return arr.toArray();
}
Example #20
0
void rename_function(const String& old_name, const String& new_name) {
  auto const old = old_name.get();
  auto const n3w = new_name.get();
  auto const oldNe = const_cast<NamedEntity*>(NamedEntity::get(old));
  auto const newNe = const_cast<NamedEntity*>(NamedEntity::get(n3w));

  Func* func = Unit::lookupFunc(oldNe);
  if (!func) {
    // It's the caller's responsibility to ensure that the old function
    // exists.
    not_reached();
  }

  // Interceptable functions can be renamed even when
  // JitEnableRenameFunction is false.
  if (!(func->attrs() & AttrInterceptable)) {
    if (!RuntimeOption::EvalJitEnableRenameFunction) {
      // When EvalJitEnableRenameFunction is false, the translator may
      // wire non-AttrInterceptable Func*'s into the TC. Don't rename
      // functions.
      raise_error("fb_rename_function must be explicitly enabled"
                  "(-v Eval.JitEnableRenameFunction=true)");
    }
  }

  auto const fnew = Unit::lookupFunc(newNe);
  if (fnew && fnew != func) {
    raise_error("Function already defined: %s", n3w->data());
  }

  always_assert(!rds::isPersistentHandle(oldNe->getFuncHandle()));
  oldNe->setCachedFunc(nullptr);
  newNe->m_cachedFunc.bind(rds::Mode::Normal);
  newNe->setCachedFunc(func);

  if (RuntimeOption::EvalJit) {
    jit::invalidateForRenameFunction(old);
  }
}
Example #21
0
ALWAYS_INLINE static
int64_t extract_impl(VRefParam vref_array,
                     int extract_type /* = EXTR_OVERWRITE */,
                     const String& prefix /* = "" */) {
  bool reference = extract_type & EXTR_REFS;
  extract_type &= ~EXTR_REFS;

  if (!vref_array.wrapped().isArray()) {
    raise_warning("extract() expects parameter 1 to be array");
    return 0;
  }

  VMRegAnchor _;
  auto const varEnv = g_context->getVarEnv();
  if (!varEnv) return 0;

  if (UNLIKELY(reference)) {
    auto& arr = vref_array.wrapped().toArrRef();
    int count = 0;
    for (ArrayIter iter(arr); iter; ++iter) {
      String name = iter.first();
      if (!modify_extract_name(varEnv, name, extract_type, prefix)) continue;
      g_context->bindVar(name.get(), arr.lvalAt(name).asTypedValue());
      ++count;
    }
    return count;
  }

  auto const var_array = vref_array.wrapped().toArray();
  int count = 0;
  for (ArrayIter iter(var_array); iter; ++iter) {
    String name = iter.first();
    if (!modify_extract_name(varEnv, name, extract_type, prefix)) continue;
    g_context->setVar(name.get(), iter.secondRef().asTypedValue());
    ++count;
  }
  return count;
}
Example #22
0
Unit* compile_systemlib_string(const char* s, size_t sz,
                               const char* fname) {
  if (RuntimeOption::RepoAuthoritative) {
    FileRepository::FileInfo fi;
    String systemName = String("/:") + String(fname);
    if (FileRepository::readRepoMd5(systemName.get(), fi)) {
      MD5 md5(fi.m_unitMd5.c_str());
      if (Unit* u = Repo::get().loadUnit(fname, md5)) {
        return u;
      }
    }
  }
  return compile_string(s, sz, fname);
}
Example #23
0
Variant::Variant(const String& v) {
  m_type = KindOfString;
  StringData *s = v.get();
  if (s) {
    m_data.pstr = s;
    if (s->isStatic()) {
      m_type = KindOfStaticString;
    } else {
      s->incRefCount();
    }
  } else {
    m_type = KindOfNull;
  }
}
Example #24
0
void ReplayTransport::recordInput(Transport* transport, const char *filename) {
  assert(transport);

  Hdf hdf;

  char buf[32];
  snprintf(buf, sizeof(buf), "%u", Process::GetProcessId());
  hdf["pid"] = std::string(buf);
#ifdef _MSC_VER
  snprintf(buf, sizeof(buf), "%" PRIx64,
    (int64_t)pthread_getw32threadid_np(Process::GetThreadId()));
#else
  snprintf(buf, sizeof(buf), "%" PRIx64, (int64_t)Process::GetThreadId());
#endif
  hdf["tid"] = std::string(buf);
  snprintf(buf, sizeof(buf), "%u", Process::GetThreadPid());
  hdf["tpid"] = std::string(buf);

  hdf["cmd"] = static_cast<int>(transport->getMethod());
  hdf["url"] = transport->getUrl();
  hdf["remote_host"] = transport->getRemoteHost();
  hdf["remote_port"] = transport->getRemotePort();

  transport->getHeaders(m_requestHeaders);
  int index = 0;
  for (HeaderMap::const_iterator iter = m_requestHeaders.begin();
       iter != m_requestHeaders.end(); ++iter) {
    for (unsigned int i = 0; i < iter->second.size(); i++) {
      Hdf header = hdf["headers"][index++];
      header["name"] = iter->first;
      header["value"] = iter->second[i];
    }
  }

  int size;
  const void *data = transport->getPostData(size);
  if (size) {
    String encoded = string_uuencode((const char *)data, size);
    hdf["post"] = encoded.get()->data();
  } else {
    hdf["post"] = "";
  }

  hdf.write(filename);
}
static bool is_a_impl(const Variant& class_or_object, const String& class_name,
                      bool allow_string, bool subclass_only) {
  if (class_or_object.isString() && !allow_string) {
    return false;
  }
  if (!(class_or_object.isString() || class_or_object.isObject())) {
    return false;
  }

  const Class* cls = get_cls(class_or_object);
  if (!cls) return false;
  if (cls->attrs() & AttrTrait) return false;
  const Class* other = Unit::lookupClass(class_name.get());
  if (!other) return false;
  if (other->attrs() & AttrTrait) return false;
  if (other == cls) return !subclass_only;
  return cls->classof(other);
}
Example #26
0
void unserializeVariant(Variant& self, VariableUnserializer *uns,
                        UnserializeMode mode /* = UnserializeMode::Value */) {

  // NOTE: If you make changes to how serialization and unserialization work,
  // make sure to update the reserialize() method in "runtime/ext/ext_apc.cpp"
  // and to update test_apc_reserialize() in "test/ext/test_ext_apc.cpp".

  char type = uns->readChar();
  char sep = uns->readChar();

  if (type != 'R') {
    uns->add(&self, mode);
  }

  if (type == 'N') {
    if (sep != ';') throw Exception("Expected ';' but got '%c'", sep);
    self.setNull(); // NULL *IS* the value, without we get undefined warnings
    return;
  }
  if (sep != ':') {
    throw Exception("Expected ':' but got '%c'", sep);
  }

  switch (type) {
  case 'r':
    {
      int64_t id = uns->readInt();
      Variant *v = uns->getByVal(id);
      if (v == nullptr) {
        throw Exception("Id %" PRId64 " out of range", id);
      }
      self = *v;
    }
    break;
  case 'R':
    {
      int64_t id = uns->readInt();
      Variant *v = uns->getByRef(id);
      if (v == nullptr) {
        throw Exception("Id %" PRId64 " out of range", id);
      }
      self.assignRef(*v);
    }
    break;
  case 'b': { int64_t v = uns->readInt(); self = (bool)v; } break;
  case 'i': { int64_t v = uns->readInt(); self = v;       } break;
  case 'd':
    {
      double v;
      char ch = uns->peek();
      bool negative = false;
      char buf[4];
      if (ch == '-') {
        negative = true;
        ch = uns->readChar();
        ch = uns->peek();
      }
      if (ch == 'I') {
        uns->read(buf, 3); buf[3] = '\0';
        if (strcmp(buf, "INF")) {
          throw Exception("Expected 'INF' but got '%s'", buf);
        }
        v = atof("inf");
      } else if (ch == 'N') {
        uns->read(buf, 3); buf[3] = '\0';
        if (strcmp(buf, "NAN")) {
          throw Exception("Expected 'NAN' but got '%s'", buf);
        }
        v = atof("nan");
      } else {
        v = uns->readDouble();
      }
      self = negative ? -v : v;
    }
    break;
  case 's':
    {
      String v;
      v.unserialize(uns);
      self = std::move(v);
      if (!uns->endOfBuffer()) {
        // Semicolon *should* always be required,
        // but PHP's implementation allows omitting it
        // and still functioning.
        // Worse, it throws it away without any check.
        // So we'll do the same.  Sigh.
        uns->readChar();
      }
    }
    return;
  case 'S':
    if (uns->type() == VariableUnserializer::Type::APCSerialize) {
      union {
        char buf[8];
        StringData *sd;
      } u;
      uns->read(u.buf, 8);
      self = u.sd;
    } else {
      throw Exception("Unknown type '%c'", type);
    }
    break;
  case 'a':
    {
      // Check stack depth to avoid overflow.
      check_recursion_throw();
      auto v = Array::Create();
      v.unserialize(uns);
      self = std::move(v);
    }
    return; // array has '}' terminating
  case 'L':
    {
      int64_t id = uns->readInt();
      uns->expectChar(':');
      String rsrcName;
      rsrcName.unserialize(uns);
      uns->expectChar('{');
      uns->expectChar('}');
      auto rsrc = makeSmartPtr<DummyResource>();
      rsrc->o_setResourceId(id);
      rsrc->m_class_name = rsrcName;
      self = std::move(rsrc);
    }
    return; // resource has '}' terminating
  case 'O':
  case 'V':
  case 'K':
    {
      String clsName;
      clsName.unserialize(uns);

      uns->expectChar(':');
      int64_t size = uns->readInt();
      uns->expectChar(':');
      uns->expectChar('{');

      const bool allowObjectFormatForCollections = true;

      Class* cls;
      // If we are potentially dealing with a collection, we need to try to
      // load the collection class under an alternate name so that we can
      // deserialize data that was serialized before the migration of
      // collections to the HH namespace.

      if (type != 'O') {
        // Collections are CPP builtins; don't attempt to autoload
        cls = Unit::getClass(clsName.get(), /* autoload */ false);
        if (!cls) {
          cls = tryAlternateCollectionClass(clsName.get());
        }
      } else if (allowObjectFormatForCollections) {
        // In order to support the legacy {O|V}:{Set|Vector|Map}
        // serialization, we defer autoloading until we know that there's
        // no alternate (builtin) collection class.
        cls = Unit::getClass(clsName.get(), /* autoload */ false);
        if (!cls) {
          cls = tryAlternateCollectionClass(clsName.get());
        }
        if (!cls) {
          cls = Unit::loadClass(clsName.get()); // with autoloading
        }
      } else {
        cls = Unit::loadClass(clsName.get()); // with autoloading
      }

      Object obj;
      if (RuntimeOption::UnserializationWhitelistCheck &&
          (type == 'O') &&
          !uns->isWhitelistedClass(clsName)) {
        const char* err_msg =
          "The object being unserialized with class name '%s' "
          "is not in the given whitelist. "
          "See http://fburl.com/SafeSerializable for more detail";
        if (RuntimeOption::UnserializationWhitelistCheckWarningOnly) {
          raise_warning(err_msg, clsName.c_str());
        } else {
          raise_error(err_msg, clsName.c_str());
        }
      }
      if (cls) {
        // Only unserialize CPP extension types which can actually
        // support it. Otherwise, we risk creating a CPP object
        // without having it initialized completely.
        if (cls->instanceCtor() && !cls->isCppSerializable()) {
          assert(obj.isNull());
          throw_null_pointer_exception();
        } else {
          obj = Object{cls};
          if (UNLIKELY(collections::isType(cls, CollectionType::Pair) &&
                       (size != 2))) {
            throw Exception("Pair objects must have exactly 2 elements");
          }
        }
      } else {
        obj = Object{SystemLib::s___PHP_Incomplete_ClassClass};
        obj->o_set(s_PHP_Incomplete_Class_Name, clsName);
      }
      assert(!obj.isNull());
      self = obj;

      if (size > 0) {
        // Check stack depth to avoid overflow.
        check_recursion_throw();

        if (type == 'O') {
          // Collections are not allowed
          if (obj->isCollection()) {
            throw Exception("%s does not support the 'O' serialization "
                            "format", clsName.data());
          }

          Variant serializedNativeData = init_null();
          bool hasSerializedNativeData = false;

          /*
            Count backwards so that i is the number of properties
            remaining (to be used as an estimate for the total number
            of dynamic properties when we see the first dynamic prop).
            see getVariantPtr
          */
          for (int64_t i = size; i--; ) {
            Variant v;
            unserializeVariant(v, uns, UnserializeMode::Key);
            String key = v.toString();
            int ksize = key.size();
            const char *kdata = key.data();
            int subLen = 0;
            if (key == ObjectData::s_serializedNativeDataKey) {
              unserializeVariant(serializedNativeData, uns);
              hasSerializedNativeData = true;
            } else if (kdata[0] == '\0') {
              if (UNLIKELY(!ksize)) {
                raise_error("Cannot access empty property");
              }
              // private or protected
              subLen = strlen(kdata + 1) + 2;
              if (UNLIKELY(subLen >= ksize)) {
                if (subLen == ksize) {
                  raise_error("Cannot access empty property");
                } else {
                  throw Exception("Mangled private object property");
                }
              }
              String k(kdata + subLen, ksize - subLen, CopyString);
              Class* ctx = (Class*)-1;
              if (kdata[1] != '*') {
                ctx = Unit::lookupClass(
                  String(kdata + 1, subLen - 2, CopyString).get());
              }
              unserializeProp(uns, obj.get(), k, ctx, key, i + 1);
            } else {
              unserializeProp(uns, obj.get(), key, nullptr, key, i + 1);
            }

            if (i > 0) {
              auto lastChar = uns->peekBack();
              if ((lastChar != ';') && (lastChar != '}')) {
                throw Exception("Object property not terminated properly");
              }
            }
          }

          // nativeDataWakeup is called last to ensure that all properties are
          // already unserialized. We also ensure that nativeDataWakeup is
          // invoked regardless of whether or not serialized native data exists
          // within the serialized content.
          if (obj->getAttribute(ObjectData::HasNativeData) &&
              obj->getVMClass()->getNativeDataInfo()->isSerializable()) {
            Native::nativeDataWakeup(obj.get(), serializedNativeData);
          } else if (hasSerializedNativeData) {
            raise_warning("%s does not expect any serialized native data.",
                          clsName.data());
          }
        } else {
          assert(type == 'V' || type == 'K');
          if (!obj->isCollection()) {
            throw Exception("%s is not a collection class", clsName.data());
          }
          collections::unserialize(obj.get(), uns, size, type);
        }
      }
      uns->expectChar('}');

      if (uns->type() != VariableUnserializer::Type::DebuggerSerialize ||
          (cls && cls->instanceCtor() && cls->isCppSerializable())) {
        // Don't call wakeup when unserializing for the debugger, except for
        // natively implemented classes.
        obj->invokeWakeup();
      }

      check_request_surprise_unlikely();
    }
    return; // object has '}' terminating
  case 'C':
    {
      if (uns->type() == VariableUnserializer::Type::DebuggerSerialize) {
        raise_error("Debugger shouldn't call custom unserialize method");
      }
      String clsName;
      clsName.unserialize(uns);

      uns->expectChar(':');
      String serialized;
      serialized.unserialize(uns, '{', '}');

      auto const obj = [&]() -> Object {
        if (auto const cls = Unit::loadClass(clsName.get())) {
          return Object::attach(g_context->createObject(cls, init_null_variant,
                                                        false /* init */));
        }
        if (!uns->allowUnknownSerializableClass()) {
          raise_error("unknown class %s", clsName.data());
        }
        Object ret = create_object_only(s_PHP_Incomplete_Class);
        ret->o_set(s_PHP_Incomplete_Class_Name, clsName);
        ret->o_set("serialized", serialized);
        return ret;
      }();

      if (!obj->instanceof(SystemLib::s_SerializableClass)) {
        raise_warning("Class %s has no unserializer",
                      obj->getClassName().data());
      } else {
        obj->o_invoke_few_args(s_unserialize, 1, serialized);
        obj.get()->clearNoDestruct();
      }

      self = std::move(obj);
    }
    return; // object has '}' terminating
  default:
    throw Exception("Unknown type '%c'", type);
  }
  uns->expectChar(';');
}
bool HHVM_FUNCTION(class_exists, const String& class_name,
                                 bool autoload /* = true */) {
  return Unit::classExists(class_name.get(), autoload, ClassKind::Class);
}
bool HHVM_FUNCTION(enum_exists, const String& enum_name,
                   bool autoload /* = true */) {
  Class* cls = Unit::getClass(enum_name.get(), autoload);
  return cls && isEnum(cls);
}
bool HHVM_FUNCTION(trait_exists, const String& trait_name,
                                 bool autoload /* = true */) {
  return Unit::classExists(trait_name.get(), autoload, ClassKind::Trait);
}
bool HHVM_FUNCTION(interface_exists, const String& interface_name,
                                     bool autoload /* = true */) {
  return
    Unit::classExists(interface_name.get(), autoload, ClassKind::Interface);
}