Example #1
0
ArrayData*
PackedArray::SetInt(ArrayData* adIn, int64_t k, const Variant& v, bool copy) {
  assert(checkInvariants(adIn));

  // Right now SetInt is used for the AddInt entry point also. This
  // first branch is the only thing we'd be able to omit if we were
  // doing AddInt.
  if (size_t(k) < adIn->m_size) {
    auto const ad = copy ? Copy(adIn) : adIn;
    auto& dst = *tvToCell(&packedData(ad)[k]);
    cellSet(*v.asCell(), dst);
    // TODO(#3888164): we should restructure things so we don't have to
    // check KindOfUninit here.
    if (UNLIKELY(dst.m_type == KindOfUninit)) {
      dst.m_type = KindOfNull;
    }
    return ad;
  }

  // Setting the int at the size of the array can keep it in packed
  // mode---it's the same as an append.
  if (size_t(k) == adIn->m_size) return Append(adIn, v, copy);

  // On the promote-to-mixed path, we can use addVal since we know the
  // key can't exist.
  auto const mixed = copy ? ToMixedCopy(adIn) : ToMixed(adIn);
  return mixed->addVal(k, v);
}
Object c_AwaitAllWaitHandle::FromMixedArray(const MixedArray* dependencies) {
  auto const start = dependencies->data();
  auto const stop = start + dependencies->iterLimit();

  return createAAWH<const MixedArray::Elm*>(start, stop, mixedArrayNext,
    [](const MixedArray::Elm* elm) { return tvToCell(&elm->data); });
}
Example #3
0
ArrayData* GlobalsArray::SetStr(ArrayData* ad,
                                         StringData* k,
                                         Cell v,
                                         bool copy) {
  auto a = asGlobals(ad);
  cellSet(v, *tvToCell(a->m_tab->lookupAdd(k)));
  return a;
}
ArrayData* NameValueTableWrapper::SetStr(ArrayData* ad,
                                         StringData* k,
                                         Cell v,
                                         bool copy) {
  auto a = asNVTW(ad);
  cellSet(v, *tvToCell(a->m_tab->lookupAdd(k)));
  return a;
}
Object c_AwaitAllWaitHandle::FromPackedArray(const ArrayData* dependencies) {
  auto const start = packedData(dependencies);
  auto const stop  = start + dependencies->getSize();

  return createAAWH<const TypedValue*>(start, stop,
    [](const TypedValue* tv, UNUSED const TypedValue* limit) { return tv + 1; },
    [](const TypedValue* tv) { return tvToCell(tv); });
}
Example #6
0
bool objOffsetEmpty(TypedValue& tvRef, ObjectData* base, const Variant& offset,
                    bool validate /* = true */) {
  if (objOffsetExists(base, offset) == OffsetExistsResult::DoesNotExist) {
    return true;
  }
  TypedValue* result = objOffsetGet(tvRef, base, offset, false);
  assert(result);
  return !cellToBool(*tvToCell(result));
}
Example #7
0
bool objOffsetEmpty(TypedValue& tvRef, ObjectData* base, CVarRef offset,
                    bool validate /* = true */) {
  if (!objOffsetExists(base, offset)) {
    return true;
  }
  TypedValue* result = objOffsetGet(tvRef, base, offset, false);
  assert(result);
  return !cellToBool(*tvToCell(result));
}
Example #8
0
void TypeConstraint::verifyFail(const Func* func, int paramNum,
                                TypedValue* tv) const {
  JIT::VMRegAnchor _;

  const StringData* tn = typeName();
  if (isSelf()) {
    selfToTypeName(func, &tn);
  } else if (isParent()) {
    parentToTypeName(func, &tn);
  }

  auto const givenType = describe_actual_type(tv);

  auto c = tvToCell(tv);
  if (isArray() && !isSoft() && !func->mustBeRef(paramNum) &&
      c->m_type == KindOfObject && c->m_data.pobj->isCollection()) {
    // To ease migration, the 'array' type constraint will implicitly cast
    // collections to arrays, provided the type constraint is not soft and
    // the parameter is not by reference. We raise a notice to let the user
    // know that there was a type mismatch and that an implicit conversion
    // was performed.
    raise_notice(
      folly::format(
        "Argument {} to {}() must be of type {}, {} given; argument {} was "
        "implicitly cast to array",
        paramNum + 1, func->fullName()->data(), fullName(), givenType,
        paramNum + 1
      ).str()
    );
    tvCastToArrayInPlace(tv);
    return;
  }

  if (isExtended() && isSoft()) {
    // Soft extended type hints raise warnings instead of recoverable
    // errors, to ease migration.
    raise_debugging(
      "Argument %d to %s() must be of type %s, %s given",
      paramNum + 1, func->fullName()->data(), fullName().c_str(), givenType);
  } else if (isExtended() && isNullable()) {
    raise_typehint_error(
      folly::format(
        "Argument {} to {}() must be of type {}, {} given",
        paramNum + 1, func->fullName()->data(), fullName(), givenType
      ).str()
    );
  } else {
    raise_typehint_error(
      folly::format(
        "Argument {} passed to {}() must be an instance of {}, {} given",
        paramNum + 1, func->fullName()->data(), tn->data(), givenType
      ).str()
    );
  }
}
ALWAYS_INLINE
TypedValue& getDefaultIfNullCell(TypedValue* tv, TypedValue& def) {
  if (UNLIKELY(nullptr == tv)) {
    // refcount is already correct since def was never decrefed
    return def;
  }
  tvRefcountedDecRef(&def);
  TypedValue* ret = tvToCell(tv);
  tvRefcountedIncRef(ret);
  return *ret;
}
Example #10
0
bool BaseVector::OffsetIsset(ObjectData* obj, TypedValue* key) {
  assert(key->m_type != KindOfRef);
  auto vec = static_cast<BaseVector*>(obj);
  TypedValue* result;
  if (key->m_type == KindOfInt64) {
    result = vec->get(key->m_data.num);
  } else {
    throwBadKeyType();
    result = nullptr;
  }
  return result ? !cellIsNull(tvToCell(result)) : false;
}
Example #11
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 (malloc_huge(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());
    auto const rval = it.secondRval();
    if (!isNullType(tvToCell(rval).type())) {
      auto val = APCHandle::Create(VarNR(rval.tv()), 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 #12
0
ArrayData* StructArray::SetStr(
  ArrayData* ad,
  StringData* k,
  Cell v,
  bool copy
) {
  auto structArray = asStructArray(ad);
  auto shape = structArray->shape();
  auto result = structArray;

  auto offset = shape->offsetFor(k);
  bool isNewProperty = offset == PropertyTable::kInvalidOffset;

  auto convertToMixedAndAdd = [&]() {
    auto mixed = copy ? ToMixedCopy(structArray) : ToMixed(structArray);
    return mixed->addValNoAsserts(k, v);
  };

  if (isNewProperty) {
    StringData* staticKey;
    // We don't support adding non-static strings yet.
    if (k->isStatic()) {
      staticKey = k;
    } else {
      staticKey = lookupStaticString(k);
      if (!staticKey) return convertToMixedAndAdd();
    }

    auto newShape = shape->transition(staticKey);
    if (!newShape) return convertToMixedAndAdd();
    result = copy ? CopyAndResizeIfNeeded(structArray, newShape)
                  : ResizeIfNeeded(structArray, newShape);
    offset = result->shape()->offsetFor(staticKey);
    assert(offset != PropertyTable::kInvalidOffset);
    TypedValue* dst = &result->data()[offset];
    // TODO(#3888164): we should restructure things so we don't have to
    // check KindOfUninit here.
    if (UNLIKELY(v.m_type == KindOfUninit)) v = make_tv<KindOfNull>();
    cellDup(v, *dst);
    return result;
  }

  if (copy) {
    result = asStructArray(Copy(structArray));
  }

  assert(offset != PropertyTable::kInvalidOffset);
  TypedValue* dst = &result->data()[offset];
  if (UNLIKELY(v.m_type == KindOfUninit)) v = make_tv<KindOfNull>();
  cellSet(v, *tvToCell(dst));
  return result;
}
Example #13
0
ALWAYS_INLINE
TypedValue& getDefaultIfNullCell(TypedValue* tv, TypedValue& def) {
  if (UNLIKELY(nullptr == tv)) {
    // DecRef of def is done unconditionally by the IR, since there's
    // a good chance it will be paired with an IncRef and optimized
    // away.  So we need to IncRef here if it is being returned.
    tvRefcountedIncRef(&def);
    return def;
  }
  TypedValue* ret = tvToCell(tv);
  tvRefcountedIncRef(ret);
  return *ret;
}
Example #14
0
void objOffsetSet(ObjectData* base, const Variant& offset, TypedValue* val,
                  bool validate /* = true */) {
  if (validate) {
    objArrayAccess(base);
  }
  assert(!base->isCollection());
  const Func* method = base->methodNamed(s_offsetSet.get());
  assert(method != nullptr);
  TypedValue tvResult;
  tvWriteUninit(&tvResult);
  TypedValue args[2] = { *offset.asCell(), *tvToCell(val) };
  g_context->invokeFuncFew(&tvResult, method, base, nullptr, 2, args);
  tvRefcountedDecRef(&tvResult);
}
Object c_AwaitAllWaitHandle::FromPackedArray(const ArrayData* dependencies) {
  auto const start = reinterpret_cast<const TypedValue*>(dependencies + 1);
  auto const stop = start + dependencies->getSize();
  auto ctx_idx = std::numeric_limits<context_idx_t>::max();
  int32_t cnt = 0;

  for (auto iter = start; iter < stop; ++iter) {
    prepareChild(tvToCell(iter), ctx_idx, cnt);
  }

  if (!cnt) return returnEmpty();

  auto result = Alloc(cnt);
  auto next = &result->m_children[cnt];

  for (auto iter = start; iter < stop; ++iter) {
    addChild(tvToCell(iter), next);
  }

  assert(next == &result->m_children[0]);
  result->initialize(ctx_idx);
  return Object{std::move(result)};
}
Example #16
0
bool objOffsetEmpty(
  ObjectData* base,
  TypedValue offset,
  bool validate /* = true */
) {
  if (objOffsetExists(base, offset) == OffsetExistsResult::DoesNotExist) {
    return true;
  }

  auto value = objOffsetGet(base, offset, false);
  auto result = !cellToBool(*tvToCell(&value));
  tvRefcountedDecRef(value);
  return result;
}
Example #17
0
static const char* describe_actual_type(const TypedValue* tv, bool isHHType) {
  tv = tvToCell(tv);
  switch (tv->m_type) {
    case KindOfUninit:        return "undefined variable";
    case KindOfNull:          return "null";
    case KindOfBoolean:       return "bool";
    case KindOfInt64:         return "int";
    case KindOfDouble:        return isHHType ? "float" : "double";
    case KindOfStaticString:
    case KindOfString:        return "string";
    case KindOfArray:         return "array";
    case KindOfObject:        return tv->m_data.pobj->o_getClassName().c_str();
    case KindOfResource:      return tv->m_data.pres->o_getClassName().c_str();
    default:
      assert(false);
  }
  not_reached();
}
Example #18
0
void objOffsetSet(
  ObjectData* base,
  TypedValue offset,
  TypedValue* val,
  bool validate /* = true */
) {
  if (validate) {
    objArrayAccess(base);
  }

  assertx(!base->isCollection());
  assertx(offset.m_type != KindOfRef);

  auto const method = base->methodNamed(s_offsetSet.get());
  assert(method != nullptr);

  TypedValue args[2] = { offset, *tvToCell(val) };
  g_context->invokeMethodV(base, method, folly::range(args));
}
Object c_AwaitAllWaitHandle::Create(Iter iter) {
  auto ctx_idx = std::numeric_limits<context_idx_t>::max();
  uint32_t cnt = 0;

  auto toCell = convert
    ? [](TypedValue tv) { return tvToCell(tv); }
    : [](TypedValue tv) { return tvAssertCell(tv); };

  iter([&](TypedValue v) { prepareChild(toCell(v), ctx_idx, cnt); });

  if (!cnt) {
    return Object{returnEmpty()};
  }

  auto result = Alloc(cnt);
  auto next = &result->m_children[cnt];
  uint32_t idx = cnt - 1;

  iter([&](TypedValue v) { addChild(toCell(v), next, idx); });

  assert(next == &result->m_children[0]);
  result->initialize(ctx_idx);
  return Object{std::move(result)};
}
Example #20
0
void Generator::done(TypedValue tv) {
  assert(getState() == State::Running);
  cellSetNull(m_key);
  cellSet(*tvToCell(&tv), m_value);
  setState(State::Done);
}
Example #21
0
bool tvSame(TypedValue tv1, TypedValue tv2) {
    assert(tvIsPlausible(tv1));
    assert(tvIsPlausible(tv2));
    return cellSame(*tvToCell(&tv1), *tvToCell(&tv2));
}
Example #22
0
TypedValue* ArrayData::nvGetCell(int64 k) const {
  TypedValue* tv = (TypedValue*)&get(k, false);
  return LIKELY(tv != (TypedValue*)&null_variant) ? tvToCell(tv) :
         nvGetNotFound(k);
}
Example #23
0
TypedValue* ArrayData::nvGetCell(const StringData* key) const {
  TypedValue* tv = (TypedValue*)&get(key, false);
  return LIKELY(tv != (TypedValue*)&null_variant) ? tvToCell(tv) :
         nvGetNotFound(key);
}
Example #24
0
TypedValue* NameValueTable::set(const StringData* name, const TypedValue* val) {
  TypedValue* target = findTypedValue(name);
  tvSet(*tvToCell(val), *target);
  return target;
}
Example #25
0
void TypeConstraint::verifyFail(const Func* func, TypedValue* tv,
                                int id) const {
  VMRegAnchor _;
  std::string name = displayName(func);
  auto const givenType = describe_actual_type(tv, isHHType());
  // Handle return type constraint failures
  if (id == ReturnId) {
    std::string msg;
    if (func->isClosureBody()) {
      msg =
        folly::format(
          "Value returned from {}closure must be of type {}, {} given",
          func->isAsync() ? "async " : "",
          name,
          givenType
        ).str();
    } else {
      msg =
        folly::format(
          "Value returned from {}{} {}() must be of type {}, {} given",
          func->isAsync() ? "async " : "",
          func->preClass() ? "method" : "function",
          func->fullName()->data(),
          name,
          givenType
        ).str();
    }
    if (RuntimeOption::EvalCheckReturnTypeHints >= 2 && !isSoft() &&
        (!func->isClosureBody() ||
         !RuntimeOption::EvalSoftClosureReturnTypeHints)) {
      raise_return_typehint_error(msg);
    } else {
      raise_debugging(msg);
    }
    return;
  }
  // Handle implicit collection->array conversion for array parameter type
  // constraints
  auto c = tvToCell(tv);
  if (isArray() && !isSoft() && !func->mustBeRef(id) &&
      c->m_type == KindOfObject && c->m_data.pobj->isCollection()) {
    // To ease migration, the 'array' type constraint will implicitly cast
    // collections to arrays, provided the type constraint is not soft and
    // the parameter is not by reference. We raise a notice to let the user
    // know that there was a type mismatch and that an implicit conversion
    // was performed.
    raise_notice(
      folly::format(
        "Argument {} to {}() must be of type {}, {} given; argument {} was "
        "implicitly cast to array",
        id + 1, func->fullName()->data(), name, givenType, id + 1
      ).str()
    );
    tvCastToArrayInPlace(tv);
    return;
  }
  // Handle parameter type constraint failures
  if (isExtended() && isSoft()) {
    // Soft extended type hints raise warnings instead of recoverable
    // errors, to ease migration.
    raise_debugging(
      folly::format(
        "Argument {} to {}() must be of type {}, {} given",
        id + 1, func->fullName()->data(), name, givenType
      ).str()
    );
  } else if (isExtended() && isNullable()) {
    raise_typehint_error(
      folly::format(
        "Argument {} to {}() must be of type {}, {} given",
        id + 1, func->fullName()->data(), name, givenType
      ).str()
    );
  } else {
    auto cls = Unit::lookupClass(m_typeName);
    if (cls && isInterface(cls)) {
      raise_typehint_error(
        folly::format(
          "Argument {} passed to {}() must implement interface {}, {} given",
          id + 1, func->fullName()->data(), name, givenType
        ).str()
      );
    } else {
      raise_typehint_error(
        folly::format(
          "Argument {} passed to {}() must be an instance of {}, {} given",
          id + 1, func->fullName()->data(), name, givenType
        ).str()
      );
    }
  }
}
Example #26
0
void TypeConstraint::verifyFail(const Func* func, TypedValue* tv,
                                int id, bool useStrictTypes) const {
  VMRegAnchor _;
  std::string name = displayName(func);
  auto const givenType = describe_actual_type(tv, isHHType());

  if (UNLIKELY(!useStrictTypes)) {
    if (auto dt = underlyingDataType()) {
      // In non-strict mode we may be able to coerce a type failure. For object
      // typehints there is no possible coercion in the failure case, but HNI
      // builtins currently only guard on kind not class so the following wil
      // generate false positives for objects.
      if (*dt != KindOfObject) {
        // HNI conversions implicitly unbox references, this behavior is wrong,
        // in particular it breaks the way type conversion works for PHP 7
        // scalar type hints
        if (tv->m_type == KindOfRef) {
          auto inner = tv->m_data.pref->var()->asTypedValue();
          if (tvCoerceParamInPlace(inner, *dt)) {
            tvAsVariant(tv) = tvAsVariant(inner);
            return;
          }
        } else {
          if (tvCoerceParamInPlace(tv, *dt)) return;
        }
      }
    }
  } else if (UNLIKELY(!func->unit()->isHHFile() &&
                      !RuntimeOption::EnableHipHopSyntax)) {
    // PHP 7 allows for a widening conversion from Int to Float. We still ban
    // this in HH files.
    if (auto dt = underlyingDataType()) {
      if (*dt == KindOfDouble && tv->m_type == KindOfInt64 &&
          tvCoerceParamToDoubleInPlace(tv)) {
        return;
      }
    }
  }

  // Handle return type constraint failures
  if (id == ReturnId) {
    std::string msg;
    if (func->isClosureBody()) {
      msg =
        folly::format(
          "Value returned from {}closure must be of type {}, {} given",
          func->isAsync() ? "async " : "",
          name,
          givenType
        ).str();
    } else {
      msg =
        folly::format(
          "Value returned from {}{} {}() must be of type {}, {} given",
          func->isAsync() ? "async " : "",
          func->preClass() ? "method" : "function",
          func->fullName(),
          name,
          givenType
        ).str();
    }
    if (RuntimeOption::EvalCheckReturnTypeHints >= 2 && !isSoft()) {
      raise_return_typehint_error(msg);
    } else {
      raise_warning_unsampled(msg);
    }
    return;
  }

  // Handle implicit collection->array conversion for array parameter type
  // constraints
  auto c = tvToCell(tv);
  if (isArray() && !isSoft() && !func->mustBeRef(id) &&
      c->m_type == KindOfObject && c->m_data.pobj->isCollection()) {
    // To ease migration, the 'array' type constraint will implicitly cast
    // collections to arrays, provided the type constraint is not soft and
    // the parameter is not by reference. We raise a notice to let the user
    // know that there was a type mismatch and that an implicit conversion
    // was performed.
    raise_notice(
      folly::format(
        "Argument {} to {}() must be of type {}, {} given; argument {} was "
        "implicitly cast to array",
        id + 1, func->fullName(), name, givenType, id + 1
      ).str()
    );
    tvCastToArrayInPlace(tv);
    return;
  }

  // Handle parameter type constraint failures
  if (isExtended() && isSoft()) {
    // Soft extended type hints raise warnings instead of recoverable
    // errors, to ease migration.
    raise_warning_unsampled(
      folly::format(
        "Argument {} to {}() must be of type {}, {} given",
        id + 1, func->fullName(), name, givenType
      ).str()
    );
  } else if (isExtended() && isNullable()) {
    raise_typehint_error(
      folly::format(
        "Argument {} to {}() must be of type {}, {} given",
        id + 1, func->fullName(), name, givenType
      ).str()
    );
  } else {
    auto cls = Unit::lookupClass(m_typeName);
    if (cls && isInterface(cls)) {
      raise_typehint_error(
        folly::format(
          "Argument {} passed to {}() must implement interface {}, {} given",
          id + 1, func->fullName(), name, givenType
        ).str()
      );
    } else {
      raise_typehint_error(
        folly::format(
          "Argument {} passed to {}() must be an instance of {}, {} given",
          id + 1, func->fullName(), name, givenType
        ).str()
      );
    }
  }
}
Example #27
0
bool tvSame(const TypedValue* tv1, const TypedValue* tv2) {
  assert(tvIsPlausible(tv1));
  assert(tvIsPlausible(tv2));
  return cellSame(tvToCell(tv1), tvToCell(tv2));
}