bool Call::matches(CallSignature sig) const { if (arguments_.size() != sig.second.size() || sig.first != function_) //TODO: Make case-insensitive check here? return false; //assert: lengths are now equal std::vector<Type>::iterator itr = sig.second.begin(); for (auto& i : arguments_) { if (i->getType() != *itr && !(isEvaluatable(*itr) && isEvaluatable(i->getType())) && !(isCallable(*itr) && isCallable(i->getType())) ) return false; itr++; } return true; }
void TypeConstraint::init() { if (UNLIKELY(s_typeNamesToTypes.empty())) { const struct Pair { const StringData* name; Type type; } pairs[] = { { makeStaticString("HH\\bool"), { KindOfBoolean, MetaType::Precise }}, { makeStaticString("HH\\int"), { KindOfInt64, MetaType::Precise }}, { makeStaticString("HH\\float"), { KindOfDouble, MetaType::Precise }}, { makeStaticString("HH\\string"), { KindOfString, MetaType::Precise }}, { makeStaticString("array"), { KindOfArray, MetaType::Precise }}, { makeStaticString("HH\\resource"), { KindOfResource, MetaType::Precise }}, { makeStaticString("HH\\num"), { KindOfDouble, MetaType::Number }}, { makeStaticString("self"), { KindOfObject, MetaType::Self }}, { makeStaticString("parent"), { KindOfObject, MetaType::Parent }}, { makeStaticString("callable"), { KindOfObject, MetaType::Callable }}, }; for (unsigned i = 0; i < sizeof(pairs) / sizeof(Pair); ++i) { s_typeNamesToTypes[pairs[i].name] = pairs[i].type; } } if (isTypeVar()) { // We kept the type variable type constraint to correctly check child // classes implementing abstract methods or interfaces. m_type.dt = KindOfInvalid; m_type.metatype = MetaType::Precise; return; } if (m_typeName == nullptr) { m_type.dt = KindOfInvalid; m_type.metatype = MetaType::Precise; return; } Type dtype; TRACE(5, "TypeConstraint: this %p type %s, nullable %d\n", this, m_typeName->data(), isNullable()); auto const mptr = folly::get_ptr(s_typeNamesToTypes, m_typeName); if (mptr) dtype = *mptr; if (!mptr || !(isHHType() || dtype.dt == KindOfArray || dtype.metatype == MetaType::Parent || dtype.metatype == MetaType::Self || dtype.metatype == MetaType::Callable)) { TRACE(5, "TypeConstraint: this %p no such type %s, treating as object\n", this, m_typeName->data()); m_type = { KindOfObject, MetaType::Precise }; m_namedEntity = Unit::GetNamedEntity(m_typeName); TRACE(5, "TypeConstraint: NamedEntity: %p\n", m_namedEntity); return; } m_type = dtype; assert(m_type.dt != KindOfStaticString); assert(IMPLIES(isParent(), m_type.dt == KindOfObject)); assert(IMPLIES(isSelf(), m_type.dt == KindOfObject)); assert(IMPLIES(isCallable(), m_type.dt == KindOfObject)); }
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 TypeConstraint::init() { if (isTypeVar()) { // We kept the type variable type constraint to correctly check child // classes implementing abstract methods or interfaces. m_type.dt = folly::none; m_type.metatype = MetaType::Precise; return; } if (m_typeName == nullptr) { m_type.dt = folly::none; m_type.metatype = MetaType::Precise; return; } Type dtype; TRACE(5, "TypeConstraint: this %p type %s, nullable %d\n", this, m_typeName->data(), isNullable()); auto const mptr = typeNameToType(m_typeName); if (mptr) dtype = *mptr; if (!mptr || !(isHHType() || dtype.dt == KindOfArray || dtype.dt == KindOfBoolean || dtype.dt == KindOfString || dtype.dt == KindOfInt64 || dtype.dt == KindOfDouble || dtype.dt == KindOfResource || dtype.metatype == MetaType::ArrayKey || dtype.metatype == MetaType::Number || dtype.metatype == MetaType::Parent || dtype.metatype == MetaType::Self || dtype.metatype == MetaType::Callable)) { TRACE(5, "TypeConstraint: this %p no such type %s, treating as object\n", this, m_typeName->data()); m_type = { KindOfObject, MetaType::Precise }; m_namedEntity = NamedEntity::get(m_typeName); TRACE(5, "TypeConstraint: NamedEntity: %p\n", m_namedEntity); return; } m_type = dtype; assert(m_type.dt != KindOfStaticString); assert(IMPLIES(isParent(), m_type.dt == KindOfObject)); assert(IMPLIES(isSelf(), m_type.dt == KindOfObject)); assert(IMPLIES(isCallable(), m_type.dt == KindOfObject)); }
MaybeDataType TypeConstraint::underlyingDataTypeResolved() const { assert(!isSelf() && !isParent() && !isCallable()); assert(IMPLIES( !hasConstraint() || isTypeVar() || isTypeConstant(), isMixed())); if (!isPrecise()) return folly::none; auto t = underlyingDataType(); assert(t); // If we aren't a class or type alias, nothing special to do. if (!isObject()) return t; assert(t == KindOfObject); auto p = getTypeAliasOrClassWithAutoload(m_namedEntity, m_typeName); auto td = p.first; auto c = p.second; // See if this is a type alias. if (td) { if (td->type != Type::Object) { t = (getAnnotMetaType(td->type) != MetaType::Precise) ? folly::none : MaybeDataType(getAnnotDataType(td->type)); } else { c = td->klass; } } // If the underlying type is a class, see if it is an enum and get that. if (c && isEnum(c)) { t = c->enumBaseTy(); } return t; }
void TypeConstraint::init() { if (UNLIKELY(s_typeNamesToTypes.empty())) { const struct Pair { const StringData* name; Type type; } pairs[] = { { StringData::GetStaticString("bool"), { KindOfBoolean, MetaType::Precise }}, { StringData::GetStaticString("boolean"), { KindOfBoolean, MetaType::Precise }}, { StringData::GetStaticString("int"), { KindOfInt64, MetaType::Precise }}, { StringData::GetStaticString("integer"), { KindOfInt64, MetaType::Precise }}, { StringData::GetStaticString("real"), { KindOfDouble, MetaType::Precise }}, { StringData::GetStaticString("double"), { KindOfDouble, MetaType::Precise }}, { StringData::GetStaticString("float"), { KindOfDouble, MetaType::Precise }}, { StringData::GetStaticString("string"), { KindOfString, MetaType::Precise }}, { StringData::GetStaticString("array"), { KindOfArray, MetaType::Precise }}, { StringData::GetStaticString("resource"), { KindOfResource, MetaType::Precise }}, { StringData::GetStaticString("self"), { KindOfObject, MetaType::Self }}, { StringData::GetStaticString("parent"), { KindOfObject, MetaType::Parent }}, { StringData::GetStaticString("callable"), { KindOfObject, MetaType::Callable }}, }; for (unsigned i = 0; i < sizeof(pairs) / sizeof(Pair); ++i) { s_typeNamesToTypes[pairs[i].name] = pairs[i].type; } } if (m_typeName && isExtended()) { assert(nullable() && "Only nullable extended type hints are implemented"); } if (blacklistedName(m_typeName)) { m_typeName = nullptr; } if (m_typeName == nullptr) { m_type.m_dt = KindOfInvalid; m_type.m_metatype = MetaType::Precise; return; } Type dtype; TRACE(5, "TypeConstraint: this %p type %s, nullable %d\n", this, m_typeName->data(), nullable()); if (!mapGet(s_typeNamesToTypes, m_typeName, &dtype) || !(hhType() || dtype.m_dt == KindOfArray || dtype.isParent() || dtype.isSelf())) { TRACE(5, "TypeConstraint: this %p no such type %s, treating as object\n", this, m_typeName->data()); m_type = { KindOfObject, MetaType::Precise }; m_namedEntity = Unit::GetNamedEntity(m_typeName); TRACE(5, "TypeConstraint: NamedEntity: %p\n", m_namedEntity); return; } m_type = dtype; assert(m_type.m_dt != KindOfStaticString); assert(IMPLIES(isParent(), m_type.m_dt == KindOfObject)); assert(IMPLIES(isSelf(), m_type.m_dt == KindOfObject)); assert(IMPLIES(isCallable(), m_type.m_dt == KindOfObject)); }
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); }
bool TypeConstraint::check(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 (isNullable() && tv->m_type == KindOfNull) return true; if (isNumber()) { return IS_INT_TYPE(tv->m_type) || IS_DOUBLE_TYPE(tv->m_type); } if (isArrayKey()) { return IS_INT_TYPE(tv->m_type) || IS_STRING_TYPE(tv->m_type); } if (tv->m_type == KindOfObject) { if (!isObjectOrTypeAlias()) 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 (isProfileRequest()) InstanceBits::profile(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 HHVM_FN(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 (isProfileRequest() && c) { InstanceBits::profile(c->preClass()->name()); } if (c && tv->m_data.pobj->instanceof(c)) { return true; } return !selfOrParentOrCallable && checkTypeAliasObj(tv); } if (isObjectOrTypeAlias()) { do { switch (tv->m_type) { case KindOfInt64: if (interface_supports_int(m_typeName)) { return true; } continue; case KindOfDouble: if (interface_supports_double(m_typeName)) { return true; } continue; case KindOfStaticString: case KindOfString: if (interface_supports_string(m_typeName)) { return true; } continue; case KindOfArray: if (interface_supports_array(m_typeName)) { return true; } continue; case KindOfUninit: case KindOfNull: case KindOfBoolean: case KindOfObject: case KindOfResource: continue; case KindOfRef: case KindOfClass: break; } not_reached(); } while (0); if (isCallable()) { return HHVM_FN(is_callable)(tvAsCVarRef(tv)); } return isPrecise() && checkTypeAliasNonObj(tv); } return m_type.dt && equivDataTypes(*m_type.dt, tv->m_type); }