Object c_GenArrayWaitHandle::ti_create(const char* cls, CArrRef dependencies) {
    Array deps = dependencies->copy();
    for (ssize_t iter_pos = deps->iter_begin();
            iter_pos != ArrayData::invalid_index;
            iter_pos = deps->iter_advance(iter_pos)) {

        TypedValue* current = deps->nvGetValueRef(iter_pos);
        if (UNLIKELY(current->m_type == KindOfRef)) {
            tvUnbox(current);
        }

        if (!c_WaitHandle::fromTypedValue(current) &&
                !IS_NULL_TYPE(current->m_type)) {
            Object e(SystemLib::AllocInvalidArgumentExceptionObject(
                         "Expected dependencies to be an array of WaitHandle instances"));
            throw e;
        }
    }

    Object exception;
    for (ssize_t iter_pos = deps->iter_begin();
            iter_pos != ArrayData::invalid_index;
            iter_pos = deps->iter_advance(iter_pos)) {

        TypedValue* current = deps->nvGetValueRef(iter_pos);
        if (IS_NULL_TYPE(current->m_type)) {
            // {uninit,null} yields null
            tvWriteNull(current);
            continue;
        }

        assert(current->m_type == KindOfObject);
        assert(dynamic_cast<c_WaitHandle*>(current->m_data.pobj));
        auto child = static_cast<c_WaitHandle*>(current->m_data.pobj);

        if (child->isSucceeded()) {
            tvSetIgnoreRef(child->getResult(), current);
        } else if (child->isFailed()) {
            putException(exception, child->getException());
        } else {
            assert(dynamic_cast<c_WaitableWaitHandle*>(child));
            auto child_wh = static_cast<c_WaitableWaitHandle*>(child);

            c_GenArrayWaitHandle* my_wh = NEWOBJ(c_GenArrayWaitHandle)();
            my_wh->initialize(exception, deps, iter_pos, child_wh);
            return my_wh;
        }
    }

    if (exception.isNull()) {
        TypedValue tv;
        tv.m_type = KindOfArray;
        tv.m_data.parr = deps.get();
        return c_StaticResultWaitHandle::Create(&tv);
    } else {
        return c_StaticExceptionWaitHandle::Create(exception.get());
    }
}
Example #2
0
bool objOffsetIsset(TypedValue& tvRef, ObjectData* base, const Variant& offset,
                    bool validate /* = true */) {
  auto exists = objOffsetExists(base, offset);

  // Unless we called ArrayObject::offsetExists, there's nothing more to do
  if (exists != OffsetExistsResult::IssetIfNonNull) {
    return (int)exists;
  }

  // For ArrayObject::offsetExists, we need to check the value at `offset`.
  // If it's null, then we return false.
  TypedValue tvResult;
  tvWriteUninit(&tvResult);

  // We can't call the offsetGet method on `base` because users aren't
  // expecting offsetGet to be called for `isset(...)` expressions, so call
  // the method on the base ArrayObject class.
  auto const method =
    SystemLib::s_ArrayObjectClass->lookupMethod(s_offsetGet.get());
  assert(method != nullptr);
  g_context->invokeFuncFew(&tvResult, method, base, nullptr, 1,
                           offset.asCell());
  auto const result = !IS_NULL_TYPE(tvResult.m_type);
  tvRefcountedDecRef(&tvResult);
  return result;
}
Example #3
0
bool
TypeConstraint::checkPrimitive(DataType dt) const {
  assert(m_type.m_dt != KindOfObject);
  assert(dt != KindOfRef);
  if (nullable() && IS_NULL_TYPE(dt)) return true;
  return equivDataTypes(m_type.m_dt, dt);
}
Example #4
0
bool TypeConstraint::checkTypedefNonObj(const TypedValue* tv) const {
  assert(tv->m_type != KindOfObject); // this checks when tv is not an object
  assert(!isSelf() && !isParent());

  auto const td = getTypedefWithAutoload(m_namedEntity, m_typeName);
  if (!td) return false;
  if (td->nullable && IS_NULL_TYPE(tv->m_type)) return true;
  return td->kind == KindOfAny || equivDataTypes(td->kind, tv->m_type);
}
Example #5
0
bool cellSame(Cell c1, Cell c2) {
  assert(cellIsPlausible(c1));
  assert(cellIsPlausible(c2));

  bool const null1 = IS_NULL_TYPE(c1.m_type);
  bool const null2 = IS_NULL_TYPE(c2.m_type);
  if (null1 && null2) return true;
  if (null1 || null2) return false;

  switch (c1.m_type) {
    case KindOfBoolean:
    case KindOfInt64:
      if (c2.m_type != c1.m_type) return false;
      return c1.m_data.num == c2.m_data.num;

    case KindOfDouble:
      if (c2.m_type != c1.m_type) return false;
      return c1.m_data.dbl == c2.m_data.dbl;

    case KindOfStaticString:
    case KindOfString:
      if (!IS_STRING_TYPE(c2.m_type)) return false;
      return c1.m_data.pstr->same(c2.m_data.pstr);

    case KindOfArray:
      if (c2.m_type != KindOfArray) return false;
      return c1.m_data.parr->equal(c2.m_data.parr, true);

    case KindOfObject:
      return c2.m_type == KindOfObject &&
        c1.m_data.pobj == c2.m_data.pobj;

    case KindOfResource:
      return c2.m_type == KindOfResource &&
        c1.m_data.pres == c2.m_data.pres;

    case KindOfUninit:
    case KindOfNull:
    case KindOfRef:
    case KindOfClass:
      break;
  }
  not_reached();
}
void c_GenArrayWaitHandle::enterContext(context_idx_t ctx_idx) {
  assert(AsioSession::Get()->getContext(ctx_idx));

  // stop before corrupting unioned data
  if (isFinished()) {
    return;
  }

  // already in the more specific context?
  if (LIKELY(getContextIdx() >= ctx_idx)) {
    return;
  }

  assert(getState() == STATE_BLOCKED);

  // recursively import current child
  {
    assert(m_iterPos != ArrayData::invalid_index);
    TypedValue* current = m_deps->nvGetValueRef(m_iterPos);

    assert(current->m_type == KindOfObject);
    assert(dynamic_cast<c_WaitableWaitHandle*>(current->m_data.pobj));
    auto child_wh = static_cast<c_WaitableWaitHandle*>(current->m_data.pobj);
    child_wh->enterContext(ctx_idx);
  }

  // import ourselves
  setContextIdx(ctx_idx);

  // try to import other children
  try {
    for (ssize_t iter_pos = m_deps->iter_advance(m_iterPos);
         iter_pos != ArrayData::invalid_index;
         iter_pos = m_deps->iter_advance(iter_pos)) {

      TypedValue* current = m_deps->nvGetValueRef(iter_pos);
      if (IS_NULL_TYPE(current->m_type)) {
        continue;
      }

      assert(current->m_type == KindOfObject);
      assert(dynamic_cast<c_WaitHandle*>(current->m_data.pobj));
      auto child = static_cast<c_WaitHandle*>(current->m_data.pobj);

      if (child->isFinished()) {
        continue;
      }

      assert(dynamic_cast<c_WaitableWaitHandle*>(child));
      auto child_wh = static_cast<c_WaitableWaitHandle*>(child);
      child_wh->enterContext(ctx_idx);
    }
  } catch (const Object& cycle_exception) {
    // exception will be eventually processed by onUnblocked()
  }
}
Example #7
0
bool tvCoerceParamToNullableObjectInPlace(TypedValue* tv) {
  assert(tvIsPlausible(*tv));
  tvUnboxIfNeeded(tv);
  if (IS_NULL_TYPE(tv->m_type)) {
    // See comment in tvCastToNullableObjectInPlace
    tv->m_data.pobj = nullptr;
    return true;
  }
  return tv->m_type == KindOfObject;
}
Example #8
0
bool TypeConstraint::checkTypedefObj(const TypedValue* tv) const {
  assert(tv->m_type == KindOfObject); // this checks when tv is an object
  assert(!isSelf() && !isParent() && !isCallable());

  auto const td = getTypedefWithAutoload(m_namedEntity, m_typeName);
  if (!td) return false;
  if (td->nullable && IS_NULL_TYPE(tv->m_type)) return true;
  if (td->kind != KindOfObject) return td->kind == KindOfAny;
  return td->klass && tv->m_data.pobj->instanceof(td->klass);
}
void c_ContinuationWaitHandle::run() {
  // may happen if scheduled in multiple contexts
  if (getState() != STATE_SCHEDULED) {
    return;
  }

  try {
    setState(STATE_RUNNING);

    do {
      // iterate continuation
      if (m_child.isNull()) {
        // first iteration or null dependency
        m_continuation->call_next();
      } else if (m_child->isSucceeded()) {
        // child succeeded, pass the result to the continuation
        m_continuation->call_send(m_child->getResult());
      } else if (m_child->isFailed()) {
        // child failed, raise the exception inside continuation
        m_continuation->call_raise(m_child->getException());
      } else {
        throw FatalErrorException(
            "Invariant violation: child neither succeeded nor failed");
      }

      // continuation finished, retrieve result from its m_value
      if (m_continuation->m_done) {
        markAsSucceeded(m_continuation->m_value.asTypedValue());
        return;
      }

      // set up dependency
      TypedValue* value = m_continuation->m_value.asTypedValue();
      if (IS_NULL_TYPE(value->m_type)) {
        // null dependency
        m_child = nullptr;
      } else {
        c_WaitHandle* child = c_WaitHandle::fromTypedValue(value);
        if (UNLIKELY(!child)) {
          Object e(SystemLib::AllocInvalidArgumentExceptionObject(
              "Expected yield argument to be an instance of WaitHandle"));
          throw e;
        }
        m_child = child;
      }
    } while (m_child.isNull() || m_child->isFinished());

    // we are blocked on m_child so it must be WaitableWaitHandle
    assert(dynamic_cast<c_WaitableWaitHandle*>(m_child.get()));
    blockOn(static_cast<c_WaitableWaitHandle*>(m_child.get()));
  } catch (Object exception) {
    // process exception thrown by generator or blockOn cycle detection
    markAsFailed(exception);
  }
}
Example #10
0
void tvCastToNullableObjectInPlace(TypedValue* tv) {
  if (IS_NULL_TYPE(tv->m_type)) {
    // XXX(t3879280) This happens immediately before calling an extension
    // function that takes an optional Object argument. We want to end up
    // passing const Object& holding nullptr, so by clearing out m_data.pobj we
    // can unconditionally treat &tv->m_data.pobj as a const Object& in the
    // function being called. This violates the invariant that the value of
    // m_data doesn't matter in a KindOfNull TypedValue.
    tv->m_data.pobj = nullptr;
  } else {
    tvCastToObjectInPlace(tv);
  }
}
Example #11
0
bool cellSame(const Cell* c1, const Cell* c2) {
  assert(cellIsPlausible(c1));
  assert(cellIsPlausible(c2));

  bool const null1 = IS_NULL_TYPE(c1->m_type);
  bool const null2 = IS_NULL_TYPE(c2->m_type);
  if (null1 && null2) return true;
  if (null1 || null2) return false;

  switch (c1->m_type) {
  case KindOfInt64:
  case KindOfBoolean:
    if (c2->m_type != c1->m_type) return false;
    return c1->m_data.num == c2->m_data.num;
  case KindOfDouble:
    if (c2->m_type != c1->m_type) return false;
    return c1->m_data.dbl == c2->m_data.dbl;

  case KindOfStaticString:
  case KindOfString:
    if (!IS_STRING_TYPE(c2->m_type)) return false;
    return c1->m_data.pstr->same(c2->m_data.pstr);

  case KindOfArray:
    if (c2->m_type != KindOfArray) return false;
    return c1->m_data.parr->equal(c2->m_data.parr, true);

  case KindOfObject:
    return c2->m_type == KindOfObject &&
      c1->m_data.pobj == c2->m_data.pobj;

  default:
    break;
  }
  not_reached();
}
bool tvSame(const TypedValue* tv1, const TypedValue* tv2) {
  bool const null1 = IS_NULL_TYPE(tv1->m_type);
  bool const null2 = IS_NULL_TYPE(tv2->m_type);
  if (null1 && null2) return true;
  if (null1 || null2) return false;

  if (tv1->m_type == KindOfRef) tv1 = tv1->m_data.pref->tv();
  if (tv2->m_type == KindOfRef) tv2 = tv2->m_data.pref->tv();

  switch (tv1->m_type) {
  case KindOfInt64:
  case KindOfBoolean:
    if (tv2->m_type != tv1->m_type) return false;
    return tv1->m_data.num == tv2->m_data.num;
  case KindOfDouble:
    if (tv2->m_type != tv1->m_type) return false;
    return tv1->m_data.dbl == tv2->m_data.dbl;

  case KindOfStaticString:
  case KindOfString:
    if (!IS_STRING_TYPE(tv2->m_type)) return false;
    return tv1->m_data.pstr->same(tv2->m_data.pstr);

  case KindOfArray:
    if (tv2->m_type != KindOfArray) return false;
    return tv1->m_data.parr->equal(tv2->m_data.parr, true);

  case KindOfObject:
    return tv2->m_type == KindOfObject &&
      tv1->m_data.pobj == tv2->m_data.pobj;

  default:
    break;
  }
  not_reached();
}
void c_GenArrayWaitHandle::onUnblocked() {
  for (;
       m_iterPos != ArrayData::invalid_index;
       m_iterPos = m_deps->iter_advance(m_iterPos)) {

    TypedValue* current = m_deps->nvGetValueRef(m_iterPos);
    if (IS_NULL_TYPE(current->m_type)) {
      // {uninit,null} yields null
      tvWriteNull(current);
      continue;
    }

    assert(current->m_type == KindOfObject);
    assert(dynamic_cast<c_WaitHandle*>(current->m_data.pobj));
    auto child = static_cast<c_WaitHandle*>(current->m_data.pobj);

    if (child->isSucceeded()) {
      tvSetIgnoreRef(child->getResult(), current);
    } else if (child->isFailed()) {
      putException(m_exception, child->getException());
    } else {
      assert(dynamic_cast<c_WaitableWaitHandle*>(child));
      auto child_wh = static_cast<c_WaitableWaitHandle*>(child);

      try {
        blockOn(child_wh);
        return;
      } catch (const Object& cycle_exception) {
        putException(m_exception, cycle_exception.get());
      }
    }
  }

  if (m_exception.isNull()) {
    TypedValue result;
    result.m_type = KindOfArray;
    result.m_data.parr = m_deps.get();
    setResult(&result);
    m_deps = nullptr;
  } else {
    setException(m_exception.get());
    m_exception = nullptr;
    m_deps = nullptr;
  }
}
Example #14
0
bool
TypeConstraint::check(const TypedValue* tv, const Func* func) const {
  assert(hasConstraint());

  // This is part of the interpreter runtime; perf matters.
  if (tv->m_type == KindOfRef) {
    tv = tv->m_data.pref->tv();
  }
  if (nullable() && IS_NULL_TYPE(tv->m_type)) return true;

  if (tv->m_type == KindOfObject) {
    if (!isObjectOrTypedef()) return false;
    // Perfect match seems common enough to be worth skipping the hash
    // table lookup.
    if (m_typeName->isame(tv->m_data.pobj->getVMClass()->name())) {
      if (shouldProfile()) Class::profileInstanceOf(m_typeName);
      return true;
    }
    const Class *c = nullptr;
    const bool selfOrParentOrCallable = isSelf() || isParent() || isCallable();
    if (selfOrParentOrCallable) {
      if (isSelf()) {
        selfToClass(func, &c);
      } else if (isParent()) {
        parentToClass(func, &c);
      } else {
        assert(isCallable());
        return f_is_callable(tvAsCVarRef(tv));
      }
    } else {
      // We can't save the Class* since it moves around from request
      // to request.
      assert(m_namedEntity);
      c = Unit::lookupClass(m_namedEntity);
    }
    if (shouldProfile() && c) {
      Class::profileInstanceOf(c->preClass()->name());
    }
    if (c && tv->m_data.pobj->instanceof(c)) {
      return true;
    }
    return !selfOrParentOrCallable && checkTypedefObj(tv);
  }

  if (isObjectOrTypedef()) {
    switch (tv->m_type) {
      case KindOfArray:
        if (interface_supports_array(m_typeName)) {
          return true;
        }
        break;
      case KindOfString:
      case KindOfStaticString:
        if (interface_supports_string(m_typeName)) {
          return true;
        }
        break;
      case KindOfInt64:
        if (interface_supports_int(m_typeName)) {
          return true;
        }
        break;
      case KindOfDouble:
        if (interface_supports_double(m_typeName)) {
          return true;
        }
        break;
      default:
        break;
    }

    if (isCallable()) {
      return f_is_callable(tvAsCVarRef(tv));
    }
    return isPrecise() && checkTypedefNonObj(tv);
  }

  return equivDataTypes(m_type.m_dt, tv->m_type);
}