Clean up VectorEffects::init
[hiphop-php.git] / hphp / runtime / vm / type_constraint.cpp
blobc4a61e0a82a260c5880c67b9e16f0b1d65ce633d
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
29 namespace HPHP {
31 TRACE_SET_MOD(runtime);
33 namespace {
35 // TODO(#2322864): this is a hack until we can get rid of the "Xhp"
36 // psuedo-type.
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())) {
49 const struct Pair {
50 const StringData* name;
51 Type type;
52 } pairs[] = {
53 { StringData::GetStaticString("bool"), { KindOfBoolean,
54 MetaType::Precise }},
55 { StringData::GetStaticString("boolean"), { KindOfBoolean,
56 MetaType::Precise }},
58 { StringData::GetStaticString("int"), { KindOfInt64,
59 MetaType::Precise }},
60 { StringData::GetStaticString("integer"), { KindOfInt64,
61 MetaType::Precise }},
63 { StringData::GetStaticString("real"), { KindOfDouble,
64 MetaType::Precise }},
65 { StringData::GetStaticString("double"), { KindOfDouble,
66 MetaType::Precise }},
67 { StringData::GetStaticString("float"), { KindOfDouble,
68 MetaType::Precise }},
70 { StringData::GetStaticString("string"), { KindOfString,
71 MetaType::Precise }},
73 { StringData::GetStaticString("array"), { KindOfArray,
74 MetaType::Precise }},
76 { StringData::GetStaticString("self"), { KindOfObject,
77 MetaType::Self }},
78 { StringData::GetStaticString("parent"), { KindOfObject,
79 MetaType::Parent }},
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()) {
89 assert(nullable() &&
90 "Only nullable extended type hints are implemented");
93 if (blacklistedName(m_typeName) ||
94 (isExtended() && !RuntimeOption::EvalCheckExtendedTypeHints)) {
95 m_typeName = nullptr;
97 if (m_typeName == nullptr) {
98 m_type.m_dt = KindOfInvalid;
99 m_type.m_metatype = MetaType::Precise;
100 return;
103 Type dtype;
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() ||
108 dtype.isSelf())) {
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);
114 return;
116 m_type = dtype;
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));
124 * Note:
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();
134 if (!def) {
135 String nameStr(const_cast<StringData*>(name));
136 if (!AutoloadHandler::s_instance->autoloadType(nameStr)) {
137 return nullptr;
139 def = ne->getCachedTypedef();
141 return def;
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);
165 bool
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
178 // table lookup.
179 if (m_typeName->isame(tv->m_data.pobj->getVMClass()->name())) {
180 if (shouldProfile()) Class::profileInstanceOf(m_typeName);
181 return true;
183 const Class *c = nullptr;
184 const bool selfOrParentOrCallable = isSelf() || isParent() || isCallable();
185 if (selfOrParentOrCallable) {
186 if (isSelf()) {
187 selfToClass(func, &c);
188 } else if (isParent()) {
189 parentToClass(func, &c);
190 } else {
191 assert(isCallable());
192 return f_is_callable(tvAsCVarRef(tv));
194 } else {
195 // We can't save the Class* since it moves around from request
196 // to 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)) {
204 return true;
206 return !selfOrParentOrCallable && checkTypedefObj(tv);
209 if (tv->m_type == KindOfArray &&
210 isObjectOrTypedef() &&
211 interface_supports_array(m_typeName)) {
212 return true;
215 if (isObjectOrTypedef()) {
216 if (isCallable()) {
217 return f_is_callable(tvAsCVarRef(tv));
219 return isPrecise() && checkTypedefNonObj(tv);
222 return equivDataTypes(m_type.m_dt, tv->m_type);
225 bool
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) {
234 tv = tvToCell(tv);
235 switch (tv->m_type) {
236 case KindOfUninit:
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";
244 case KindOfObject:
245 return tv->m_data.pobj->o_getClassName().c_str();
246 default:
247 assert(false);
249 not_reached();
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();
258 if (isSelf()) {
259 selfToTypeName(func, &tn);
260 } else if (isParent()) {
261 parentToTypeName(func, &tn);
264 auto const givenType = describe_actual_type(tv);
266 if (isExtended()) {
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).
270 assert(nullable() &&
271 "only nullable extended type hints are currently supported");
272 raise_warning(
273 "Argument %d to %s must be of type ?%s, %s given",
274 paramNum + 1, fname.str().c_str(), tn->data(), givenType);
275 } else {
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();
284 if (c) {
285 *cls = c;
289 void TypeConstraint::selfToTypeName(const Func* func,
290 const StringData **typeName) const {
291 const Class* c = func->cls();
292 if (c) {
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;
300 if (c2) {
301 *cls = c2;
305 void TypeConstraint::parentToTypeName(const Func* func,
306 const StringData **typeName) const {
307 const Class* c = nullptr;
308 parentToClass(func, &c);
309 if (c) {
310 *typeName = c->name();
314 } // HPHP::VM