Esempio n. 1
0
APCHandle* APCArray::MakeShared(ArrayData* arr,
                                size_t& size,
                                bool inner,
                                bool unserializeObj) {
  if (!inner) {
    // only need to call traverseData() on the toplevel array
    DataWalker walker(DataWalker::LookupFeature::HasObjectOrResource);
    DataWalker::DataFeature features = walker.traverseData(arr);
    if (features.isCircular() || features.hasCollection()) {
      String s = apc_serialize(arr);
      APCHandle* handle = APCString::MakeShared(KindOfArray, s.get(), size);
      handle->setSerializedArray();
      return handle;
    }

    if (apcExtension::UseUncounted &&
        !features.hasObjectOrResource() &&
        !arr->empty()) {
      size = getMemSize(arr) + sizeof(APCTypedValue);
      return APCTypedValue::MakeSharedArray(arr);
    }
  }

  if (arr->isVectorData()) {
    return APCArray::MakePackedShared(arr, size, unserializeObj);
  }

  return APCArray::MakeShared(arr, size, unserializeObj);
}
Esempio n. 2
0
bool ConcurrentTableSharedStore::constructPrime(const String& v,
                                                KeyValuePair& item,
                                                bool serialized) {
  if (s_apc_file_storage.getState() !=
      APCFileStorage::StorageState::Invalid &&
      (!v.get()->isStatic() || serialized)) {
    // StaticString for non-object should consume limited amount of space,
    // not worth going through the file storage

    // TODO: currently we double serialize string for uniform handling later,
    // hopefully the unserialize won't be called often. We could further
    // optimize by storing more type info.
    String s = apc_serialize(v);
    char *sAddr = s_apc_file_storage.put(s.data(), s.size());
    if (sAddr) {
      item.sAddr = sAddr;
      item.sSize = serialized ? 0 - s.size() : s.size();
      return false;
    }
  }
  auto pair = APCHandle::Create(v, serialized, APCHandleLevel::Outer, false);
  item.value = pair.handle;
  item.sSize = pair.size;
  return true;
}
Esempio n. 3
0
APCHandle* APCObject::MakeShared(
    ObjectData* objectData, size_t& size, bool inner, bool createFromSer) {
  if (!canOptimize(objectData)) {
    return createFromSer ? MakeShared(apc_serialize(objectData), size) :
        nullptr;
  }

  if (objectData->isCollection()) {
    return APCCollection::MakeShared(objectData, size, inner);
  }

  bool serialize = false;
  if (!inner) {
    PointerSet visited;
    visited.insert(reinterpret_cast<void*>(objectData));
    traverseDataRecursive(objectData,
        [&](const Variant& var) {
          if (var.isReferenced()) {
            Variant *pvar = var.getRefData();
            if (chekVisited(visited, pvar)) {
              serialize = true;
              return true;
            }
          }

          DataType type = var.getType();
          if (type == KindOfObject) {
            auto data = var.getObjectData();
            if (chekVisited(visited, reinterpret_cast<void*>(data))) {
              serialize = true;
              return true;
            }
          } else if (type == KindOfResource) {
            serialize = true;
            return true;
          }
          return false;
        }
    );
  }

  if (serialize) {
    return createFromSer ? MakeShared(apc_serialize(objectData), size) :
                           nullptr;
  }
  return Construct(objectData, size);
}
bool ConcurrentTableSharedStore::constructPrime(CVarRef v,
                                                KeyValuePair& item) {
  if (s_apc_file_storage.getState() != SharedStoreFileStorage::StateInvalid &&
      (IS_REFCOUNTED_TYPE(v.getType()))) {
    // Only do the storage for ref-counted type
    String s = apc_serialize(v);
    char *sAddr = s_apc_file_storage.put(s.data(), s.size());
    if (sAddr) {
      item.sAddr = sAddr;
      item.sSize = s.size();
      return false;
    }
  }
  item.value = SharedVariant::Create(v, false);
  return true;
}
Esempio n. 5
0
bool ConcurrentTableSharedStore::constructPrime(const Variant& v,
                                                KeyValuePair& item) {
  if (s_apc_file_storage.getState() !=
      APCFileStorage::StorageState::Invalid &&
      (isRefcountedType(v.getType()))) {
    // Only do the storage for ref-counted type
    String s = apc_serialize(v);
    char *sAddr = s_apc_file_storage.put(s.data(), s.size());
    if (sAddr) {
      item.sAddr = sAddr;
      item.sSize = s.size();
      return false;
    }
  }
  auto pair = APCHandle::Create(v, false, APCHandleLevel::Outer, false);
  item.value = pair.handle;
  item.sSize = pair.size;
  return true;
}
Esempio n. 6
0
APCHandle::Pair APCCollection::Make(const ObjectData* obj,
                                    APCHandleLevel level,
                                    bool unserializeObj) {
  auto bail = [&] {
    return APCString::MakeSerializedObject(
      apc_serialize(Variant(const_cast<ObjectData*>(obj)))
    );
  };

  auto const array = collections::asArray(obj);
  if (!array) return bail();

  /*
   * Create an uncounted array if we can.
   *
   * If this collection is an OuterHandle, then we need to do a full check on
   * this array for things like circularity.  If we're an InnerHandle, someone
   * already checked that, but we want to check for whether it's uncounted to
   * use a better representation.  For the OuterHandle case, we just delegate
   * to APCArray below (which will do the full DataWalker pass).
   */
  if (level == APCHandleLevel::Inner && apcExtension::UseUncounted &&
      !array->empty()) {
    DataWalker walker(DataWalker::LookupFeature::HasObjectOrResource);
    auto const features = walker.traverseData(const_cast<ArrayData*>(array));
    assert(!features.isCircular);
    if (!features.hasObjectOrResource) {
      return WrapArray(
        { APCArray::MakeUncountedArray(const_cast<ArrayData*>(array)),
          getMemSize(array) + sizeof(APCTypedValue) },
        obj->collectionType()
      );
    }
  }

  return WrapArray(
    APCArray::MakeSharedArray(const_cast<ArrayData*>(array),
                              level,
                              unserializeObj),
    obj->collectionType()
  );
}
Esempio n. 7
0
APCHandle::Pair
APCArray::MakeSharedArray(ArrayData* arr, APCHandleLevel level,
                          bool unserializeObj) {
  if (level == APCHandleLevel::Outer) {
    // only need to call traverseData() on the toplevel array
    DataWalker walker(DataWalker::LookupFeature::HasObjectOrResource);
    DataWalker::DataFeature features = walker.traverseData(arr);
    if (features.isCircular) {
      String s = apc_serialize(Variant{arr});
      return APCString::MakeSerializedArray(s.get());
    }

    if (apcExtension::UseUncounted && !features.hasObjectOrResource &&
        !arr->empty()) {
      return {MakeUncountedArray(arr),
              getMemSize(arr) + sizeof(APCTypedValue)};
    }
  }

  return arr->isVectorData() ? MakePacked(arr, unserializeObj) :
         MakeHash(arr, unserializeObj);
}
Esempio n. 8
0
APCHandle::Pair APCHandle::Create(const Variant& source,
                                  bool serialized,
                                  APCHandleLevel level,
                                  bool unserializeObj) {

  auto type = source.getType(); // this gets rid of the ref, if it was one
  switch (type) {
    case KindOfUninit: {
      auto value = APCTypedValue::tvUninit();
      return {value->getHandle(), sizeof(APCTypedValue)};
    }
    case KindOfNull: {
      auto value = APCTypedValue::tvNull();
      return {value->getHandle(), sizeof(APCTypedValue)};
    }
    case KindOfBoolean: {
      auto value = source.getBoolean() ? APCTypedValue::tvTrue()
                                       : APCTypedValue::tvFalse();
      return {value->getHandle(), sizeof(APCTypedValue)};
    }
    case KindOfInt64: {
      auto value = new APCTypedValue(source.getInt64());
      return {value->getHandle(), sizeof(APCTypedValue)};
    }
    case KindOfDouble: {
      auto value = new APCTypedValue(source.getDouble());
      return {value->getHandle(), sizeof(APCTypedValue)};
    }
    case KindOfPersistentString:
    case KindOfString: {
      StringData* s = source.getStringData();
      if (serialized) {
        // It is priming, and there might not be the right class definitions
        // for unserialization.
        return APCString::MakeSerializedObject(apc_reserialize(String{s}));
      }
      if (s->isStatic()) {
        auto value = new APCTypedValue(APCTypedValue::StaticStr{}, s);
        return APCHandle::Pair{value->getHandle(), sizeof(APCTypedValue)};
      }
      auto const st = lookupStaticString(s);
      if (st) {
        auto value = new APCTypedValue(APCTypedValue::StaticStr{}, st);
        return {value->getHandle(), sizeof(APCTypedValue)};
      }
      if (apcExtension::UseUncounted) {
        auto st = StringData::MakeUncounted(s->slice());
        auto value = new APCTypedValue(APCTypedValue::UncountedStr{}, st);
        return {value->getHandle(), st->size() + sizeof(APCTypedValue)};
      }
      return APCString::MakeSharedString(s);
    }

    case KindOfPersistentVec:
    case KindOfVec: {
      auto ad = source.getArrayData();
      assert(ad->isVecArray());
      if (ad->isStatic()) {
        auto value = new APCTypedValue(APCTypedValue::StaticVec{}, ad);
        return {value->getHandle(), sizeof(APCTypedValue)};
      }

      return APCArray::MakeSharedVec(ad, level, unserializeObj);
    }

    case KindOfPersistentDict:
    case KindOfDict: {
      auto ad = source.getArrayData();
      assert(ad->isDict());
      if (ad->isStatic()) {
        auto value = new APCTypedValue(APCTypedValue::StaticDict{}, ad);
        return {value->getHandle(), sizeof(APCTypedValue)};
      }

      return APCArray::MakeSharedDict(ad, level, unserializeObj);
    }

    case KindOfPersistentKeyset:
    case KindOfKeyset: {
      auto ad = source.getArrayData();
      assert(ad->isKeyset());
      if (ad->isStatic()) {
        auto value = new APCTypedValue(APCTypedValue::StaticKeyset{}, ad);
        return {value->getHandle(), sizeof(APCTypedValue)};
      }

      return APCArray::MakeSharedKeyset(ad, level, unserializeObj);
    }

    case KindOfPersistentArray:
    case KindOfArray: {
      auto ad = source.getArrayData();
      assert(ad->isPHPArray());
      if (ad->isStatic()) {
        auto value = new APCTypedValue(APCTypedValue::StaticArr{}, ad);
        return {value->getHandle(), sizeof(APCTypedValue)};
      }

      return APCArray::MakeSharedArray(ad, level, unserializeObj);
    }

    case KindOfObject:
      if (source.getObjectData()->isCollection()) {
        return APCCollection::Make(source.getObjectData(),
                                   level,
                                   unserializeObj);
      }
      return unserializeObj ? APCObject::Construct(source.getObjectData()) :
             APCString::MakeSerializedObject(apc_serialize(source));

    case KindOfResource:
      // TODO Task #2661075: Here and elsewhere in the runtime, we convert
      // Resources to the empty array during various serialization operations,
      // which does not match Zend behavior. We should fix this.
      return APCArray::MakeSharedEmptyArray();

    case KindOfRef:
    case KindOfClass:
      return {nullptr, 0};
  }
  not_reached();
}
SharedVariant::SharedVariant(CVarRef source, bool serialized,
                             bool inner /* = false */,
                             bool unserializeObj /* = false */)
  : m_count (1), m_shouldCache(false), m_flags(0){
  ASSERT(!serialized || source.isString());

  m_type = source.getType();

  switch (m_type) {
  case KindOfBoolean:
    {
      m_data.num = source.toBoolean();
      break;
    }
  case KindOfByte:
  case KindOfInt16:
  case KindOfInt32:
  case KindOfInt64:
    {
      m_type = KindOfInt64;
      m_data.num = source.toInt64();
      break;
    }
  case KindOfDouble:
    {
      m_data.dbl = source.toDouble();
      break;
    }
  case KindOfStaticString:
  case KindOfString:
    {
      String s = source.toString();
      if (serialized) {
        m_type = KindOfObject;
        // It is priming, and there might not be the right class definitions
        // for unserialization.
        s = apc_reserialize(s);
      }
      m_data.str = s->copy(true);
      break;
    }
  case KindOfArray:
    {
      ArrayData *arr = source.getArrayData();

      if (!inner) {
        // only need to call hasInternalReference() on the toplevel array
        PointerSet seen;
        if (arr->hasInternalReference(seen)) {
          setSerializedArray();
          m_shouldCache = true;
          String s = apc_serialize(source);
          m_data.str = new StringData(s.data(), s.size(), CopyString);
          break;
        }
      }

      size_t size = arr->size();
      if (arr->isVectorData()) {
        setIsVector();
        m_data.vec = new VectorData(size);
        uint i = 0;
        for (ArrayIter it(arr); !it.end(); it.next(), i++) {
          SharedVariant* val = Create(it.secondRef(), false, true,
                                      unserializeObj);
          if (val->m_shouldCache) m_shouldCache = true;
          m_data.vec->vals[i] = val;
        }
      } else {
        m_data.map = new ImmutableMap(size);
        for (ArrayIter it(arr); !it.end(); it.next()) {
          SharedVariant* key = Create(it.first(), false, true,
                                      unserializeObj);
          SharedVariant* val = Create(it.secondRef(), false, true,
                                      unserializeObj);
          if (val->m_shouldCache) m_shouldCache = true;
          m_data.map->add(key, val);
        }
      }
      break;
    }
  case KindOfNull:
    {
      m_data.num = 0;
      break;
    }
  default:
    {
      ASSERT(source.isObject());
      m_shouldCache = true;
      if (unserializeObj) {
        // This assumes hasInternalReference(seen, true) is false
        ImmutableObj* obj = new ImmutableObj(source.getObjectData());
        m_data.obj = obj;
        setIsObj();
      } else {
        String s = apc_serialize(source);
        m_data.str = new StringData(s.data(), s.size(), CopyString);
      }
      break;
    }
  }
}
Esempio n. 10
0
APCHandle* APCHandle::Create(const Variant& source,
                             size_t& size,
                             bool serialized,
                             bool inner /* = false */,
                             bool unserializeObj /* = false */) {
  auto type = source.getType(); // this gets rid of the ref, if it was one
  switch (type) {
    case KindOfBoolean: {
      auto value = new APCTypedValue(type,
          static_cast<int64_t>(source.getBoolean()));
      size = sizeof(APCTypedValue);
      return value->getHandle();
    }
    case KindOfInt64: {
      auto value = new APCTypedValue(type, source.getInt64());
      size = sizeof(APCTypedValue);
      return value->getHandle();
    }
    case KindOfDouble: {
      auto value = new APCTypedValue(type, source.getDouble());
      size = sizeof(APCTypedValue);
      return value->getHandle();
    }
    case KindOfUninit:
    case KindOfNull: {
      auto value = new APCTypedValue(type);
      size = sizeof(APCTypedValue);
      return value->getHandle();
    }

    case KindOfStaticString: {
      if (serialized) goto StringCase;

      auto value = new APCTypedValue(type, source.getStringData());
      size = sizeof(APCTypedValue);
      return value->getHandle();
    }
StringCase:
    case KindOfString: {
      StringData* s = source.getStringData();
      if (serialized) {
        // It is priming, and there might not be the right class definitions
        // for unserialization.
        return APCObject::MakeShared(apc_reserialize(s), size);
      }

      auto const st = lookupStaticString(s);
      if (st) {
        APCTypedValue* value = new APCTypedValue(KindOfStaticString, st);
        size = sizeof(APCTypedValue);
        return value->getHandle();
      }

      assert(!s->isStatic()); // would've been handled above
      if (!inner && apcExtension::UseUncounted) {
        StringData* st = StringData::MakeUncounted(s->slice());
        APCTypedValue* value = new APCTypedValue(st);
        size = sizeof(APCTypedValue) + st->size();
        return value->getHandle();
      }
      return APCString::MakeShared(type, s, size);
    }

    case KindOfArray:
      return APCArray::MakeShared(source.getArrayData(),
                                  size,
                                  inner,
                                  unserializeObj);

    case KindOfResource:
      // TODO Task #2661075: Here and elsewhere in the runtime, we convert
      // Resources to the empty array during various serialization operations,
      // which does not match Zend behavior. We should fix this.
      size = sizeof(APCArray);
      return APCArray::MakeShared();

    case KindOfObject:
      return unserializeObj ?
          APCObject::Construct(source.getObjectData(), size) :
          APCObject::MakeShared(apc_serialize(source), size);

    default:
      return nullptr;
  }
}
ThreadSharedVariant::ThreadSharedVariant(CVarRef source, bool serialized,
                                         bool inner /* = false */) {
  ASSERT(!serialized || source.isString());

  setOwner();
  m_ref = 1;

  switch (source.getType()) {
  case KindOfBoolean:
    {
      m_type = KindOfBoolean;
      m_data.num = source.toBoolean();
      break;
    }
  case KindOfByte:
  case KindOfInt16:
  case KindOfInt32:
  case KindOfInt64:
    {
      m_type = KindOfInt64;
      m_data.num = source.toInt64();
      break;
    }
  case KindOfDouble:
    {
      m_type = KindOfDouble;
      m_data.dbl = source.toDouble();
      break;
    }
  case KindOfStaticString:
  case KindOfString:
    {
      String s = source.toString();
      m_type = serialized ? KindOfObject : KindOfString;
      if (serialized) {
        // It is priming, and there might not be the right class definitions
        // for unserialization.
        s = apc_reserialize(s);
      }
      m_data.str = s->copy(true);
      break;
    }
  case KindOfArray:
    {
      m_type = KindOfArray;

      ArrayData *arr = source.getArrayData();

      if (!inner) {
        // only need to call hasInternalReference() on the toplevel array
        PointerSet seen;
        if (arr->hasInternalReference(seen)) {
          setSerializedArray();
          setShouldCache();
          String s = apc_serialize(source);
          m_data.str = new StringData(s.data(), s.size(), CopyString);
          break;
        }
      }

      size_t size = arr->size();
      if (arr->isVectorData()) {
        setIsVector();
        m_data.vec = new VectorData(size);
        uint i = 0;
        for (ArrayIter it(arr); !it.end(); it.next(), i++) {
          ThreadSharedVariant* val = createAnother(it.second(), false, true);
          if (val->shouldCache()) setShouldCache();
          m_data.vec->vals[i] = val;
        }
      } else {
        m_data.map = new ImmutableMap(size);
        uint i = 0;
        for (ArrayIter it(arr); !it.end(); it.next(), i++) {
          ThreadSharedVariant* key = createAnother(it.first(), false);
          ThreadSharedVariant* val = createAnother(it.second(), false, true);
          if (val->shouldCache()) setShouldCache();
          m_data.map->add(key, val);
        }
      }
      break;
    }
  default:
    {
      m_type = KindOfObject;
      setShouldCache();
      String s = apc_serialize(source);
      m_data.str = new StringData(s.data(), s.size(), CopyString);
      break;
    }
  }
}
Esempio n. 12
0
SharedVariant::SharedVariant(CVarRef source, bool serialized,
                             bool inner /* = false */,
                             bool unserializeObj /* = false */)
  : m_shouldCache(false), m_flags(0) {
  assert(!serialized || source.isString());
  m_count = 1;
  m_type = source.getType();
  switch (m_type) {
  case KindOfBoolean:
    {
      m_data.num = source.toBoolean();
      break;
    }
  case KindOfInt64:
    {
      m_type = KindOfInt64;
      m_data.num = source.toInt64();
      break;
    }
  case KindOfDouble:
    {
      m_data.dbl = source.toDouble();
      break;
    }
  case KindOfStaticString:
    {
      if (serialized) goto StringCase;
      m_data.str = source.getStringData();
      break;
    }
StringCase:
  case KindOfString:
    {
      String s = source.toString();
      if (serialized) {
        m_type = KindOfObject;
        // It is priming, and there might not be the right class definitions
        // for unserialization.
        s = apc_reserialize(s);
      }
      StringData* st = StringData::LookupStaticString(s.get());
      if (st) {
        m_data.str = st;
        m_type = KindOfStaticString;
        break;
      }
      m_data.str = s->copy(true);
      break;
    }
  case KindOfArray:
    {
      ArrayData *arr = source.getArrayData();

      if (!inner) {
        // only need to call hasInternalReference() on the toplevel array
        PointerSet seen;
        if (arr->hasInternalReference(seen)) {
          setSerializedArray();
          m_shouldCache = true;
          String s = apc_serialize(source);
          m_data.str = StringData::MakeMalloced(s.data(), s.size());
          break;
        }
      }

      if (arr->isVectorData()) {
        setIsVector();
        m_data.vec = new (arr->size()) VectorData();
        for (ArrayIter it(arr); !it.end(); it.next()) {
          SharedVariant* val = Create(it.secondRef(), false, true,
                                      unserializeObj);
          if (val->m_shouldCache) m_shouldCache = true;
          m_data.vec->vals()[m_data.vec->m_size++] = val;
        }
      } else {
        m_data.map = ImmutableMap::Create(arr, unserializeObj, m_shouldCache);
      }
      break;
    }
  case KindOfUninit:
  case KindOfNull:
    {
      break;
    }
  case KindOfResource:
    {
      // TODO Task #2661075: Here and elsewhere in the runtime, we convert
      // Resources to the empty array during various serialization operations,
      // which does not match Zend behavior. We should fix this.
      m_type = KindOfArray;
      setIsVector();
      m_data.vec = new (0) VectorData();
      break;
    }
  default:
    {
      assert(source.isObject());
      m_shouldCache = true;
      if (unserializeObj) {
        // This assumes hasInternalReference(seen, true) is false
        ImmutableObj* obj = new ImmutableObj(source.getObjectData());
        m_data.obj = obj;
        setIsObj();
      } else {
        String s = apc_serialize(source);
        m_data.str = StringData::MakeMalloced(s.data(), s.size());
      }
      break;
    }
  }
  assert(m_type != KindOfResource);
}