2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/vm/type_constraint.h"
19 #include "hphp/util/base.h"
20 #include "hphp/util/trace.h"
21 #include "hphp/runtime/ext/ext_function.h"
22 #include "hphp/runtime/vm/hhbc.h"
23 #include "hphp/runtime/vm/class.h"
24 #include "hphp/runtime/vm/unit.h"
25 #include "hphp/runtime/vm/func.h"
26 #include "hphp/runtime/vm/jit/translator-inline.h"
27 #include "hphp/runtime/base/builtin_functions.h"
31 TRACE_SET_MOD(runtime
);
35 // TODO(#2322864): this is a hack until we can get rid of the "Xhp"
37 const StaticString
s_xhp("Xhp");
38 bool blacklistedName(const StringData
* sd
) {
39 if (!sd
) return false;
40 return sd
->isame(s_xhp
.get());
45 TypeConstraint::TypeMap
TypeConstraint::s_typeNamesToTypes
;
47 void TypeConstraint::init() {
48 if (UNLIKELY(s_typeNamesToTypes
.empty())) {
50 const StringData
* name
;
53 { StringData::GetStaticString("bool"), { KindOfBoolean
,
55 { StringData::GetStaticString("boolean"), { KindOfBoolean
,
58 { StringData::GetStaticString("int"), { KindOfInt64
,
60 { StringData::GetStaticString("integer"), { KindOfInt64
,
63 { StringData::GetStaticString("real"), { KindOfDouble
,
65 { StringData::GetStaticString("double"), { KindOfDouble
,
67 { StringData::GetStaticString("float"), { KindOfDouble
,
70 { StringData::GetStaticString("string"), { KindOfString
,
73 { StringData::GetStaticString("array"), { KindOfArray
,
76 { StringData::GetStaticString("self"), { KindOfObject
,
78 { StringData::GetStaticString("parent"), { KindOfObject
,
80 { StringData::GetStaticString("callable"), { KindOfObject
,
81 MetaType::Callable
}},
83 for (unsigned i
= 0; i
< sizeof(pairs
) / sizeof(Pair
); ++i
) {
84 s_typeNamesToTypes
[pairs
[i
].name
] = pairs
[i
].type
;
88 if (m_typeName
&& isExtended()) {
90 "Only nullable extended type hints are implemented");
93 if (blacklistedName(m_typeName
) ||
94 (isExtended() && !RuntimeOption::EvalCheckExtendedTypeHints
)) {
97 if (m_typeName
== nullptr) {
98 m_type
.m_dt
= KindOfInvalid
;
99 m_type
.m_metatype
= MetaType::Precise
;
104 TRACE(5, "TypeConstraint: this %p type %s, nullable %d\n",
105 this, m_typeName
->data(), nullable());
106 if (!mapGet(s_typeNamesToTypes
, m_typeName
, &dtype
) ||
107 !(hhType() || dtype
.m_dt
== KindOfArray
|| dtype
.isParent() ||
109 TRACE(5, "TypeConstraint: this %p no such type %s, treating as object\n",
110 this, m_typeName
->data());
111 m_type
= { KindOfObject
, MetaType::Precise
};
112 m_namedEntity
= Unit::GetNamedEntity(m_typeName
);
113 TRACE(5, "TypeConstraint: NamedEntity: %p\n", m_namedEntity
);
117 assert(m_type
.m_dt
!= KindOfStaticString
);
118 assert(IMPLIES(isParent(), m_type
.m_dt
== KindOfObject
));
119 assert(IMPLIES(isSelf(), m_type
.m_dt
== KindOfObject
));
120 assert(IMPLIES(isCallable(), m_type
.m_dt
== KindOfObject
));
126 * We don't need to autoload classes because you can't have an
127 * instance of a class if it's not defined. However, we need to
128 * autoload typedefs because they can affect whether the
129 * VerifyParamType would succeed.
131 const TypedefReq
* getTypedefWithAutoload(const NamedEntity
* ne
,
132 const StringData
* name
) {
133 auto def
= ne
->getCachedTypedef();
135 String
nameStr(const_cast<StringData
*>(name
));
136 if (!AutoloadHandler::s_instance
->autoloadType(nameStr
)) {
139 def
= ne
->getCachedTypedef();
144 bool TypeConstraint::checkTypedefNonObj(const TypedValue
* tv
) const {
145 assert(tv
->m_type
!= KindOfObject
); // this checks when tv is not an object
146 assert(!isSelf() && !isParent());
148 auto const td
= getTypedefWithAutoload(m_namedEntity
, m_typeName
);
149 if (!td
) return false;
150 if (td
->nullable
&& IS_NULL_TYPE(tv
->m_type
)) return true;
151 return td
->kind
== KindOfAny
|| equivDataTypes(td
->kind
, tv
->m_type
);
154 bool TypeConstraint::checkTypedefObj(const TypedValue
* tv
) const {
155 assert(tv
->m_type
== KindOfObject
); // this checks when tv is an object
156 assert(!isSelf() && !isParent() && !isCallable());
158 auto const td
= getTypedefWithAutoload(m_namedEntity
, m_typeName
);
159 if (!td
) return false;
160 if (td
->nullable
&& IS_NULL_TYPE(tv
->m_type
)) return true;
161 if (td
->kind
!= KindOfObject
) return false;
162 return td
->klass
&& tv
->m_data
.pobj
->instanceof(td
->klass
);
166 TypeConstraint::check(const TypedValue
* tv
, const Func
* func
) const {
167 assert(hasConstraint());
169 // This is part of the interpreter runtime; perf matters.
170 if (tv
->m_type
== KindOfRef
) {
171 tv
= tv
->m_data
.pref
->tv();
173 if (nullable() && IS_NULL_TYPE(tv
->m_type
)) return true;
175 if (tv
->m_type
== KindOfObject
) {
176 if (!isObjectOrTypedef()) return false;
177 // Perfect match seems common enough to be worth skipping the hash
179 if (m_typeName
->isame(tv
->m_data
.pobj
->getVMClass()->name())) {
180 if (shouldProfile()) Class::profileInstanceOf(m_typeName
);
183 const Class
*c
= nullptr;
184 const bool selfOrParentOrCallable
= isSelf() || isParent() || isCallable();
185 if (selfOrParentOrCallable
) {
187 selfToClass(func
, &c
);
188 } else if (isParent()) {
189 parentToClass(func
, &c
);
191 assert(isCallable());
192 return f_is_callable(tvAsCVarRef(tv
));
195 // We can't save the Class* since it moves around from request
197 assert(m_namedEntity
);
198 c
= Unit::lookupClass(m_namedEntity
);
200 if (shouldProfile() && c
) {
201 Class::profileInstanceOf(c
->preClass()->name());
203 if (c
&& tv
->m_data
.pobj
->instanceof(c
)) {
206 return !selfOrParentOrCallable
&& checkTypedefObj(tv
);
209 if (tv
->m_type
== KindOfArray
&&
210 isObjectOrTypedef() &&
211 interface_supports_array(m_typeName
)) {
215 if (isObjectOrTypedef()) {
217 return f_is_callable(tvAsCVarRef(tv
));
219 return isPrecise() && checkTypedefNonObj(tv
);
222 return equivDataTypes(m_type
.m_dt
, tv
->m_type
);
226 TypeConstraint::checkPrimitive(DataType dt
) const {
227 assert(m_type
.m_dt
!= KindOfObject
);
228 assert(dt
!= KindOfRef
);
229 if (nullable() && IS_NULL_TYPE(dt
)) return true;
230 return equivDataTypes(m_type
.m_dt
, dt
);
233 static const char* describe_actual_type(const TypedValue
* tv
) {
235 switch (tv
->m_type
) {
237 case KindOfNull
: return "null";
238 case KindOfBoolean
: return "bool";
239 case KindOfInt64
: return "int";
240 case KindOfDouble
: return "double";
241 case KindOfStaticString
:
242 case KindOfString
: return "string";
243 case KindOfArray
: return "array";
245 return tv
->m_data
.pobj
->o_getClassName().c_str();
252 void TypeConstraint::verifyFail(const Func
* func
, int paramNum
,
253 const TypedValue
* tv
) const {
254 Transl::VMRegAnchor _
;
255 std::ostringstream fname
;
256 fname
<< func
->fullName()->data() << "()";
257 const StringData
* tn
= typeName();
259 selfToTypeName(func
, &tn
);
260 } else if (isParent()) {
261 parentToTypeName(func
, &tn
);
264 auto const givenType
= describe_actual_type(tv
);
267 // Extended type hints raise warnings instead of recoverable
268 // errors for now, to ease migration (we used to not check these
269 // at all at runtime).
271 "only nullable extended type hints are currently supported");
273 "Argument %d to %s must be of type ?%s, %s given",
274 paramNum
+ 1, fname
.str().c_str(), tn
->data(), givenType
);
276 raise_recoverable_error(
277 "Argument %d passed to %s must be an instance of %s, %s given",
278 paramNum
+ 1, fname
.str().c_str(), tn
->data(), givenType
);
282 void TypeConstraint::selfToClass(const Func
* func
, const Class
**cls
) const {
283 const Class
* c
= func
->cls();
289 void TypeConstraint::selfToTypeName(const Func
* func
,
290 const StringData
**typeName
) const {
291 const Class
* c
= func
->cls();
293 *typeName
= c
->name();
297 void TypeConstraint::parentToClass(const Func
* func
, const Class
**cls
) const {
298 Class
* c1
= func
->cls();
299 const Class
* c2
= c1
? c1
->parent() : nullptr;
305 void TypeConstraint::parentToTypeName(const Func
* func
,
306 const StringData
**typeName
) const {
307 const Class
* c
= nullptr;
308 parentToClass(func
, &c
);
310 *typeName
= c
->name();