2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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/base/type-structure.h"
19 #include "hphp/runtime/base/array-data.h"
20 #include "hphp/runtime/base/array-data-defs.h"
21 #include "hphp/runtime/base/array-init.h"
22 #include "hphp/runtime/base/exceptions.h"
23 #include "hphp/runtime/base/string-data.h"
24 #include "hphp/runtime/base/typed-value.h"
25 #include "hphp/runtime/base/type-array.h"
26 #include "hphp/runtime/base/type-variant.h"
27 #include "hphp/runtime/vm/named-entity.h"
28 #include "hphp/runtime/vm/unit.h"
29 #include "hphp/util/text-util.h"
39 * These static strings are the same as the ones in
40 * hphp/compiler/type_annotation.cpp, where the typeAnnotArrays are
41 * originally generated. See TypeAnnotation::getScalarArrayRep().
44 s_nullable("nullable"),
46 s_classname("classname"),
48 s_elem_types("elem_types"),
49 s_return_type("return_type"),
50 s_param_types("param_types"),
51 s_generic_types("generic_types"),
52 s_root_name("root_name"),
53 s_access_list("access_list"),
55 s_allows_unknown_fields("allows_unknown_fields"),
56 s_is_cls_cns("is_cls_cns"),
57 s_optional_shape_field("optional_shape_field"),
62 s_callable("callable"),
64 s_typevars("typevars"),
65 s_typevar_types("typevar_types")
73 s_string("HH\\string"),
74 s_resource("HH\\resource"),
76 s_arraykey("HH\\arraykey"),
77 s_noreturn("HH\\noreturn"),
83 s_keyset("HH\\keyset"),
84 s_vec_or_dict("HH\\vec_or_dict")
87 std::string
fullName(const Array
& arr
);
89 void functionTypeName(const Array
& arr
, std::string
& name
) {
90 name
+= "(function (";
92 assertx(arr
.exists(s_return_type
));
93 auto const retType
= arr
[s_return_type
].toCArrRef();
95 assertx(arr
.exists(s_param_types
));
96 auto const params
= arr
[s_param_types
].toCArrRef();
99 auto const sz
= params
.size();
100 for (auto i
= 0; i
< sz
; i
++) {
101 auto const param
= params
[i
].toCArrRef();
102 folly::toAppend(sep
, fullName(param
), &name
);
106 // add funciton return type
107 folly::toAppend("): ", fullName(retType
), ")", &name
);
110 void accessTypeName(const Array
& arr
, std::string
& name
) {
111 assertx(arr
.exists(s_root_name
));
112 auto const rootName
= arr
[s_root_name
].toCStrRef();
113 name
+= rootName
.toCppString();
115 assertx(arr
.exists(s_access_list
));
116 auto const accessList
= arr
[s_access_list
].toCArrRef();
117 auto const sz
= accessList
.size();
118 for (auto i
= 0; i
< sz
; i
++) {
119 folly::toAppend("::",
120 accessList
[i
].toCStrRef().toCppString(),
125 // xhp names are mangled so we get them back to their original definition
126 // see the mangling in ScannerToken::xhpLabel
127 void xhpTypeName(const Array
& arr
, std::string
& name
) {
128 assertx(arr
.exists(s_classname
));
129 std::string clsName
= arr
[s_classname
].toCStrRef().toCppString();
130 // remove prefix if any
131 if (clsName
.compare(0, sizeof("xhp_") - 1, "xhp_") == 0) {
132 name
+= clsName
.replace(0, sizeof("xhp_") - 1, ":");
137 replaceAll(name
, "__", ":");
138 replaceAll(name
, "_", "-");
141 void tupleTypeName(const Array
& arr
, std::string
& name
) {
143 assertx(arr
.exists(s_elem_types
));
144 auto const elems
= arr
[s_elem_types
].toCArrRef();
145 auto const sz
= elems
.size();
147 for (auto i
= 0; i
< sz
; i
++) {
148 auto const elem
= elems
[i
].toCArrRef();
149 folly::toAppend(sep
, fullName(elem
), &name
);
156 void genericTypeName(const Array
& arr
, std::string
& name
) {
158 assertx(arr
.exists(s_generic_types
));
159 auto const args
= arr
[s_generic_types
].toCArrRef();
160 auto const sz
= args
.size();
162 for (auto i
= 0; i
< sz
; i
++) {
163 auto const arg
= args
[i
].toCArrRef();
164 folly::toAppend(sep
, fullName(arg
), &name
);
170 void shapeTypeName(const Array
& arr
, std::string
& name
) {
171 // works for both resolved and unresolved TypeStructures
173 assertx(arr
.exists(s_fields
));
174 auto const fields
= arr
[s_fields
].toCArrRef();
175 auto const sz
= fields
.size();
177 for (auto i
= 0; i
< sz
; i
++) {
179 auto const field
= fields
->getKey(i
);
180 auto value
= fields
->getValue(i
).toCArrRef();
182 if (value
.exists(s_optional_shape_field
)) {
185 if (value
.exists(s_value
)) {
186 // if unresolved, ignore wrapper
187 if (value
.exists(s_is_cls_cns
)) quote
= "";
188 value
= value
[s_value
].toCArrRef();
190 auto const fieldType
= field
.getType();
191 if (isStringType(fieldType
)) {
192 folly::toAppend(quote
, field
.toCStrRef().data(), quote
, &name
);
193 } else if (isIntType(fieldType
)) {
194 folly::toAppend(field
.toInt64Val(), &name
);
197 folly::toAppend("=>", fullName(value
), &name
);
201 if (arr
.exists(s_allows_unknown_fields
)) {
202 folly::toAppend(sep
, "...", &name
);
208 std::string
fullName(const Array
& arr
) {
210 if (arr
.exists(s_nullable
)) {
211 assertx(arr
[s_nullable
].toBoolean());
215 assertx(arr
.exists(s_kind
));
217 TypeStructure::Kind kind
=
218 TypeStructure::Kind(arr
[s_kind
].toInt64Val());
220 case TypeStructure::Kind::T_void
:
223 case TypeStructure::Kind::T_int
:
226 case TypeStructure::Kind::T_bool
:
229 case TypeStructure::Kind::T_float
:
232 case TypeStructure::Kind::T_string
:
235 case TypeStructure::Kind::T_resource
:
238 case TypeStructure::Kind::T_num
:
241 case TypeStructure::Kind::T_arraykey
:
244 case TypeStructure::Kind::T_noreturn
:
247 case TypeStructure::Kind::T_mixed
:
250 case TypeStructure::Kind::T_tuple
:
251 tupleTypeName(arr
, name
);
253 case TypeStructure::Kind::T_fun
:
254 functionTypeName(arr
, name
);
256 case TypeStructure::Kind::T_array
:
258 if (arr
.exists(s_generic_types
)) {
259 genericTypeName(arr
, name
);
262 case TypeStructure::Kind::T_shape
:
264 shapeTypeName(arr
, name
);
266 case TypeStructure::Kind::T_dict
:
268 if (arr
.exists(s_generic_types
)) {
269 genericTypeName(arr
, name
);
272 case TypeStructure::Kind::T_vec
:
274 if (arr
.exists(s_generic_types
)) {
275 genericTypeName(arr
, name
);
278 case TypeStructure::Kind::T_keyset
:
280 if (arr
.exists(s_generic_types
)) {
281 genericTypeName(arr
, name
);
284 case TypeStructure::Kind::T_vec_or_dict
:
285 name
+= s_vec_or_dict
;
286 if (arr
.exists(s_generic_types
)) {
287 genericTypeName(arr
, name
);
290 case TypeStructure::Kind::T_typevar
:
291 assertx(arr
.exists(s_name
));
292 name
+= arr
[s_name
].toCStrRef().toCppString();
294 case TypeStructure::Kind::T_typeaccess
:
295 accessTypeName(arr
, name
);
297 case TypeStructure::Kind::T_xhp
:
298 xhpTypeName(arr
, name
);
300 case TypeStructure::Kind::T_class
:
301 case TypeStructure::Kind::T_interface
:
302 case TypeStructure::Kind::T_trait
:
303 case TypeStructure::Kind::T_enum
:
304 case TypeStructure::Kind::T_unresolved
:
305 assertx(arr
.exists(s_classname
));
306 name
+= arr
[s_classname
].toCStrRef().toCppString();
307 if (arr
.exists(s_generic_types
)) genericTypeName(arr
, name
);
314 Array
resolveTS(const Array
& arr
,
315 const Class::Const
& typeCns
,
316 const Class
* typeCnsCls
,
317 const Array
& generics
,
320 Array
resolveList(const Array
& arr
,
321 const Class::Const
& typeCns
,
322 const Class
* typeCnsCls
,
323 const Array
& generics
,
325 auto const sz
= arr
.size();
327 VArrayInit
newarr(sz
);
328 for (auto i
= 0; i
< sz
; i
++) {
329 auto elemArr
= arr
[i
].toArray();
330 auto elem
= resolveTS(elemArr
, typeCns
, typeCnsCls
, generics
, persistent
);
331 newarr
.append(Variant(elem
));
334 return newarr
.toArray();
337 std::string
resolveContextMsg(const Class::Const
& typeCns
,
338 const Class
* typeCnsCls
) {
339 std::string
msg("when resolving ");
341 folly::toAppend("type constant ", typeCnsCls
->name()->data(),
342 "::", typeCns
.name
->data(),
345 folly::toAppend("type alias ", typeCns
.name
->data(), &msg
);
350 /* returns the unresolved TypeStructure; if aliasName is not an alias,
351 * return an empty Array. */
352 Array
getAlias(const String
& aliasName
, bool& persistent
) {
353 if (aliasName
.same(s_this
) || Unit::lookupClass(aliasName
.get())) {
354 return Array::CreateDArray();
357 auto persistentTA
= true;
358 auto typeAliasReq
= Unit::loadTypeAlias(aliasName
.get(), &persistentTA
);
359 if (!typeAliasReq
) return Array::CreateDArray();
361 // this returned type structure is unresolved.
362 assertx(typeAliasReq
->typeStructure
.isDictOrDArray());
363 persistent
&= persistentTA
;
364 return typeAliasReq
->typeStructure
;
367 const Class
* getClass(const String
& clsName
,
368 const Class::Const
& typeCns
,
369 const Class
* typeCnsCls
,
371 auto checkPersistent
= [&persistent
](const Class
* cls
) {
372 persistent
&= classHasPersistentRDS(cls
);
376 // the original unresolved type structure came from a type constant
377 // (instead of a type alias), and may have this/self/parent.
379 // HH\this: late static binding
380 if (clsName
.same(s_this
)) {
381 return checkPersistent(typeCnsCls
);
384 auto declCls
= typeCns
.cls
;
386 if (clsName
.same(s_self
)) {
387 return checkPersistent(declCls
);
390 if (clsName
.same(s_parent
)) {
391 auto parent
= declCls
->parent();
394 "%s, class %s does not have a parent",
395 resolveContextMsg(typeCns
, typeCnsCls
).c_str(),
396 declCls
->name()->data());
398 return checkPersistent(parent
);
403 auto ts
= getAlias(name
, persistent
);
404 while (!ts
.empty()) {
405 assertx(ts
.exists(s_kind
));
406 if (!ts
.exists(s_classname
)) {
407 // not a class, interface, trait, enum, or alias
409 "%s, alias %s does not resolve to a class",
410 resolveContextMsg(typeCns
, typeCnsCls
).c_str(),
413 name
= ts
[s_classname
].toCStrRef();
414 ts
= getAlias(name
, persistent
);
417 auto const cls
= Unit::loadClass(name
.get());
420 "%s, class %s not found",
421 resolveContextMsg(typeCns
, typeCnsCls
).c_str(),
425 return checkPersistent(cls
);
428 /* Given an unresolved T_shape TypeStructure, returns the __fields__
429 * portion of the array with all the field names resolved to string
431 Array
resolveShape(const Array
& arr
,
432 const Class::Const
& typeCns
,
433 const Class
* typeCnsCls
,
434 const Array
& generics
,
436 assertx(arr
.exists(s_kind
));
437 assertx(static_cast<TypeStructure::Kind
>(arr
[s_kind
].toInt64Val())
438 == TypeStructure::Kind::T_shape
);
439 assertx(arr
.exists(s_fields
));
441 auto newfields
= Array::CreateDArray();
442 auto const fields
= arr
[s_fields
].toCArrRef();
443 auto const sz
= fields
.size();
444 for (auto i
= 0; i
< sz
; i
++) {
445 Variant key
= fields
->getKey(i
);
446 auto const wrapper
= fields
->getValue(i
).toCArrRef();
447 if (wrapper
.exists(s_is_cls_cns
)) {
448 // if the shape field name is a class constant, its name is
449 // double colon delimited clsName::cnsName
450 auto const clsCns
= key
.toCStrRef().toCppString();
451 std::string clsName
, cnsName
;
452 folly::split("::", clsCns
, clsName
, cnsName
);
454 // look up clsName::cnsName
455 auto cls
= getClass(String(clsName
), typeCns
, typeCnsCls
, persistent
);
456 auto cnsValue
= cls
->clsCnsGet(String(cnsName
).get());
458 if (isStringType(cnsValue
.m_type
) || isIntType(cnsValue
.m_type
)) {
459 key
= tvAsVariant(&cnsValue
);
462 "class constant %s::%s is not a string or an int "
463 "and cannot be used as shape field names",
464 clsName
.c_str(), cnsName
.c_str());
467 assertx(wrapper
.exists(s_value
));
468 auto valueArr
= wrapper
[s_value
].toArray();
470 resolveTS(valueArr
, typeCns
, typeCnsCls
, generics
, persistent
);
472 if (wrapper
.exists(s_optional_shape_field
)) {
473 value
.add(s_optional_shape_field
, true_varNR
.tv());
476 newfields
.add(key
, Variant(value
));
482 void resolveClass(Array
& ret
,
483 const String
& clsName
,
484 const Class::Const
& typeCns
,
485 const Class
* typeCnsCls
,
487 auto const cls
= getClass(clsName
, typeCns
, typeCnsCls
, persistent
);
489 TypeStructure::Kind resolvedKind
;
490 if (isNormalClass(cls
)) {
491 resolvedKind
= TypeStructure::Kind::T_class
;
492 } else if (isInterface(cls
)) {
493 resolvedKind
= TypeStructure::Kind::T_interface
;
494 } else if (isTrait(cls
)) {
495 resolvedKind
= TypeStructure::Kind::T_trait
;
496 } else if (isEnum(cls
)) {
497 resolvedKind
= TypeStructure::Kind::T_enum
;
502 ret
.set(s_kind
, Variant(static_cast<uint8_t>(resolvedKind
)));
503 ret
.add(s_classname
, Variant(makeStaticString(cls
->name())));
506 Array
resolveGenerics(const Array
& arr
,
507 const Class::Const
& typeCns
,
508 const Class
* typeCnsCls
,
509 const Array
& generics
,
511 auto genericsArr
= arr
[s_generic_types
].toArray();
512 return resolveList(genericsArr
, typeCns
, typeCnsCls
, generics
, persistent
);
515 Array
resolveTS(const Array
& arr
,
516 const Class::Const
& typeCns
,
517 const Class
* typeCnsCls
,
518 const Array
& generics
,
520 assertx(arr
.exists(s_kind
));
521 auto const kind
= static_cast<TypeStructure::Kind
>(
522 arr
[s_kind
].toInt64Val());
524 auto newarr
= Array::CreateDArray();
525 if (arr
.exists(s_nullable
)) newarr
.add(s_nullable
, true_varNR
.tv());
526 newarr
.add(s_kind
, Variant(static_cast<uint8_t>(kind
)));
528 if (arr
.exists(s_allows_unknown_fields
)) {
529 newarr
.add(s_allows_unknown_fields
, true_varNR
.tv());
533 case TypeStructure::Kind::T_tuple
: {
534 assertx(arr
.exists(s_elem_types
));
535 auto const elemsArr
= arr
[s_elem_types
].toCArrRef();
536 auto const elemTypes
=
537 resolveList(elemsArr
, typeCns
, typeCnsCls
, generics
, persistent
);
538 newarr
.add(s_elem_types
, Variant(elemTypes
));
541 case TypeStructure::Kind::T_fun
: {
542 assertx(arr
.exists(s_return_type
));
543 auto const returnArr
= arr
[s_return_type
].toCArrRef();
544 auto const returnType
=
545 resolveTS(returnArr
, typeCns
, typeCnsCls
, generics
, persistent
);
546 newarr
.add(s_return_type
, Variant(returnType
));
548 assertx(arr
.exists(s_param_types
));
549 auto const paramsArr
= arr
[s_param_types
].toCArrRef();
550 auto const paramTypes
=
551 resolveList(paramsArr
, typeCns
, typeCnsCls
, generics
, persistent
);
552 newarr
.add(s_param_types
, Variant(paramTypes
));
555 case TypeStructure::Kind::T_array
:
556 case TypeStructure::Kind::T_dict
:
557 case TypeStructure::Kind::T_vec
:
558 case TypeStructure::Kind::T_keyset
:
559 case TypeStructure::Kind::T_vec_or_dict
: {
560 if (arr
.exists(s_generic_types
)) {
561 newarr
.add(s_generic_types
,
562 Variant(resolveGenerics(arr
, typeCns
, typeCnsCls
,
563 generics
, persistent
)));
567 case TypeStructure::Kind::T_shape
: {
569 resolveShape(arr
, typeCns
, typeCnsCls
, generics
, persistent
);
570 newarr
.add(s_fields
, Variant(fields
));
573 case TypeStructure::Kind::T_unresolved
: {
574 assertx(arr
.exists(s_classname
));
575 auto const clsName
= arr
[s_classname
].toCStrRef();
576 auto ts
= getAlias(clsName
, persistent
);
578 if (ts
.exists(s_typevars
) && arr
.exists(s_generic_types
)) {
579 std::vector
<std::string
> typevars
;
580 folly::split(",", ts
[s_typevars
].toCStrRef().data(), typevars
);
581 ts
.remove(s_typevars
);
584 resolveGenerics(arr
, typeCns
, typeCnsCls
, generics
, persistent
);
586 auto const sz
= std::min(static_cast<ssize_t
>(typevars
.size()),
587 generic_types
.size());
588 DArrayInit
newarr(sz
);
589 for (auto i
= 0; i
< sz
; i
++) {
590 newarr
.add(String(typevars
[i
]), generic_types
[i
]);
592 auto generics
= newarr
.toArray();
593 ts
= TypeStructure::resolve(clsName
, ts
, persistent
, generics
);
594 ts
.add(s_typevar_types
, Variant(generics
));
596 ts
= TypeStructure::resolve(clsName
, ts
, persistent
);
598 if (arr
.exists(s_nullable
)) {
599 ts
.add(s_nullable
, true_varNR
.tv());
605 /* Special cases for 'callable': Hack typechecker throws a naming error
606 * (unbound name), however, hhvm still supports this type hint to be
607 * compatible with php. We simply return as a OF_CLASS with class name
608 * set to 'callable'. */
609 if (clsName
.same(s_callable
)) {
611 Variant(static_cast<uint8_t>(TypeStructure::Kind::T_class
)));
612 newarr
.add(s_classname
, Variant(clsName
));
615 resolveClass(newarr
, clsName
, typeCns
, typeCnsCls
, persistent
);
616 if (arr
.exists(s_generic_types
)) {
617 newarr
.add(s_generic_types
,
618 Variant(resolveGenerics(arr
, typeCns
, typeCnsCls
,
619 generics
, persistent
)));
623 case TypeStructure::Kind::T_typeaccess
: {
624 /* type access is a root class (may be HH\this) followed by a series of
625 * type constants, i.e., cls::TC1::TC2::TC3. Each type constant other
626 * than the last one in the chain must refer to a class or an
628 assertx(arr
.exists(s_root_name
));
629 auto clsName
= arr
[s_root_name
].toCStrRef();
630 assertx(arr
.exists(s_access_list
));
631 auto const accList
= arr
[s_access_list
].toCArrRef();
632 auto const sz
= accList
.size();
634 for (auto i
= 0; i
< sz
; i
++) {
635 auto const cls
= getClass(clsName
, typeCns
, typeCnsCls
, persistent
);
636 auto const cnsName
= accList
[i
].toCStrRef();
637 if (!cls
->hasTypeConstant(cnsName
.get())) {
639 "%s, class %s does not have non-abstract "
641 resolveContextMsg(typeCns
, typeCnsCls
).c_str(),
645 auto tv
= cls
->clsCnsGet(cnsName
.get(), /* includeTypeCns = */ true);
646 assertx(isArrayLikeType(tv
.m_type
));
647 typeCnsVal
= Array(tv
.m_data
.parr
);
648 assertx(typeCnsVal
.isDictOrDArray());
649 if (i
== sz
- 1) break;
651 // if there are more accesses, keep resolving
652 assertx(typeCnsVal
.exists(s_kind
));
653 auto kind
= static_cast<TypeStructure::Kind
>
654 (typeCnsVal
[s_kind
].toInt64Val());
655 if (kind
!= TypeStructure::Kind::T_class
656 && kind
!= TypeStructure::Kind::T_interface
) {
658 "%s, %s::%s does not resolve to a class or "
659 "an interface and cannot contain type constant %s",
660 resolveContextMsg(typeCns
, typeCnsCls
).c_str(),
663 accList
[i
+1].toCStrRef().data());
665 assertx(typeCnsVal
.exists(s_classname
));
666 clsName
= typeCnsVal
[s_classname
].toCStrRef();
669 if (arr
.exists(s_nullable
)) {
670 typeCnsVal
.add(s_nullable
, true_varNR
.tv());
675 case TypeStructure::Kind::T_typevar
: {
676 assertx(arr
.exists(s_name
));
677 auto const name
= arr
[s_name
].toCStrRef();
678 return generics
.exists(name
) ? generics
[name
].toDArray() : arr
.toDArray();
680 case TypeStructure::Kind::T_xhp
:
682 return arr
.toDArray();
685 if(arr
.exists(s_typevars
)) newarr
.add(s_typevars
, arr
[s_typevars
]);
690 } // anonymous namespace
692 String
TypeStructure::toString(const Array
& arr
) {
693 if (arr
.empty()) return String();
695 return String(fullName(arr
));
699 * Constructs a scalar array with all the shape field names, this/self/parent,
700 * classes, type accesses, and type aliases resolved.
702 Array
TypeStructure::resolve(const Class::Const
& typeCns
,
703 const Class
* typeCnsCls
,
705 assertx(typeCns
.isType());
706 assertx(isArrayLikeType(typeCns
.val
.m_type
));
707 assertx(typeCns
.name
);
710 Array
arr(typeCns
.val
.m_data
.parr
);
711 return resolveTS(arr
, typeCns
, typeCnsCls
, Array(), persistent
);
715 * Called by TypeAliasReq to get resolved TypeStructure for type aliases.
717 Array
TypeStructure::resolve(const String
& aliasName
,
720 const Array
& generics
) {
721 // use a bogus constant to store the name
723 cns
.name
= aliasName
.get();
725 auto newarr
= resolveTS(arr
, cns
, nullptr, generics
, persistent
);
726 newarr
.add(s_alias
, Variant(aliasName
));