Implement HHVM parsing for optional shape fields
[hiphop-php.git] / hphp / compiler / type_annotation.cpp
blobce071e47d0a44feb477c5ce1fed6667140d5e365
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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/compiler/type_annotation.h"
19 #include <vector>
21 #include "hphp/compiler/code_generator.h"
22 #include "hphp/util/text-util.h"
23 #include "hphp/runtime/base/type-array.h"
24 #include "hphp/runtime/base/type-structure.h"
25 #include "hphp/runtime/base/type-variant.h"
27 namespace HPHP {
29 ///////////////////////////////////////////////////////////////////////////////
30 // constructors/destructors
32 TypeAnnotation::TypeAnnotation(const std::string &name,
33 TypeAnnotationPtr typeArgs) : m_name(name),
34 m_typeArgs(typeArgs),
35 m_typeList(TypeAnnotationPtr()),
36 m_nullable(false),
37 m_soft(false),
38 m_tuple(false),
39 m_function(false),
40 m_xhp(false),
41 m_typevar(false),
42 m_typeaccess(false),
43 m_shape(false),
44 m_clsCnsShapeField(false),
45 m_optionalShapeField(false) { }
47 std::string TypeAnnotation::vanillaName() const {
48 // filter out types that should not be exposed to the runtime
49 if (m_nullable || m_soft || m_typevar || m_function || m_typeaccess) {
50 return "";
52 if (!strcasecmp(m_name.c_str(), "HH\\mixed") ||
53 !strcasecmp(m_name.c_str(), "HH\\this")) {
54 return "";
56 return m_name;
59 std::string TypeAnnotation::fullName() const {
60 std::string name;
61 if (m_soft) {
62 name += '@';
64 if (m_nullable) {
65 name += '?';
68 if (m_function) {
69 functionTypeName(name);
70 } else if (m_typeaccess) {
71 accessTypeName(name);
72 } else if (m_xhp) {
73 xhpTypeName(name);
74 } else if (m_tuple) {
75 tupleTypeName(name);
76 } else if (m_shape) {
77 shapeTypeName(name);
78 } else if (m_typeArgs) {
79 genericTypeName(name);
80 } else {
81 name += m_name;
83 return name;
86 MaybeDataType TypeAnnotation::dataType() const {
87 if (m_function || m_xhp || m_tuple) {
88 return KindOfObject;
90 if (m_typeArgs) {
91 return !strcasecmp(m_name.c_str(), "array") ? KindOfArray : KindOfObject;
93 if (m_nullable || m_soft) {
94 return folly::none;
96 if (!strcasecmp(m_name.c_str(), "null") ||
97 !strcasecmp(m_name.c_str(), "HH\\void")) {
98 return KindOfNull;
100 if (!strcasecmp(m_name.c_str(), "HH\\bool")) return KindOfBoolean;
101 if (!strcasecmp(m_name.c_str(), "HH\\int")) return KindOfInt64;
102 if (!strcasecmp(m_name.c_str(), "HH\\float")) return KindOfDouble;
103 if (!strcasecmp(m_name.c_str(), "HH\\num")) return folly::none;
104 if (!strcasecmp(m_name.c_str(), "HH\\arraykey")) return folly::none;
105 if (!strcasecmp(m_name.c_str(), "HH\\string")) return KindOfString;
106 if (!strcasecmp(m_name.c_str(), "array")) return KindOfArray;
107 if (!strcasecmp(m_name.c_str(), "HH\\dict")) return KindOfDict;
108 if (!strcasecmp(m_name.c_str(), "HH\\vec")) return KindOfVec;
109 if (!strcasecmp(m_name.c_str(), "HH\\keyset")) return KindOfKeyset;
110 if (!strcasecmp(m_name.c_str(), "HH\\resource")) return KindOfResource;
111 if (!strcasecmp(m_name.c_str(), "HH\\mixed")) return folly::none;
113 return KindOfObject;
116 void TypeAnnotation::getAllSimpleNames(std::vector<std::string>& names) const {
117 names.push_back(m_name);
118 if (m_typeList) {
119 m_typeList->getAllSimpleNames(names);
120 } else if (m_typeArgs) {
121 // do not return the shape fields and keys
122 if (!m_shape) {
123 m_typeArgs->getAllSimpleNames(names);
128 void TypeAnnotation::shapeTypeName(std::string& name) const {
129 name += "HH\\shape(";
130 TypeAnnotationPtr shapeField = m_typeArgs;
131 auto sep = "";
132 while (shapeField) {
133 name += sep;
135 if (shapeField->isClsCnsShapeField()) {
136 folly::toAppend(shapeField->m_name, &name);
137 } else {
138 folly::toAppend("'", shapeField->m_name, "'", &name);
140 auto fieldValue = shapeField->m_typeArgs;
141 assert(fieldValue);
142 folly::toAppend("=>", fieldValue->fullName(), &name);
144 sep = ", ";
145 shapeField = shapeField->m_typeList;
148 name += ")";
151 void TypeAnnotation::functionTypeName(std::string &name) const {
152 name += "(function (";
153 // return value of function types is the first element of type list
154 TypeAnnotationPtr retType = m_typeArgs;
155 TypeAnnotationPtr typeEl = m_typeArgs->m_typeList;
156 auto sep = "";
157 while (typeEl) {
158 folly::toAppend(sep, typeEl->fullName(), &name);
159 typeEl = typeEl->m_typeList;
160 sep = ", ";
162 // add function return value
163 folly::toAppend("): ", retType->fullName(), ")", &name);
166 // xhp names are mangled so we get them back to their original definition
167 // @see the mangling in ScannerToken::xhpLabel
168 void TypeAnnotation::xhpTypeName(std::string &name) const {
169 // remove prefix if any
170 if (m_name.compare(0, sizeof("xhp_") - 1, "xhp_") == 0) {
171 name += std::string(m_name).replace(0, sizeof("xhp_") - 1, ":");
172 } else {
173 name += m_name;
175 // un-mangle back
176 replaceAll(name, "__", ":");
177 replaceAll(name, "_", "-");
180 void TypeAnnotation::tupleTypeName(std::string &name) const {
181 name += "(";
182 TypeAnnotationPtr typeEl = m_typeArgs;
183 auto sep = "";
184 while (typeEl) {
185 folly::toAppend(sep, typeEl->fullName(), &name);
186 typeEl = typeEl->m_typeList;
187 sep = ", ";
189 name += ")";
192 void TypeAnnotation::genericTypeName(std::string &name) const {
193 folly::toAppend(m_name, "<", &name);
194 TypeAnnotationPtr typeEl = m_typeArgs;
195 auto sep = "";
196 while (typeEl) {
197 folly::toAppend(sep, typeEl->fullName(), &name);
198 typeEl = typeEl->m_typeList;
199 sep = ", ";
201 name += ">";
204 void TypeAnnotation::accessTypeName(std::string &name) const {
205 name += m_name;
206 TypeAnnotationPtr typeEl = m_typeArgs;
207 while (typeEl) {
208 folly::toAppend("::", typeEl->fullName(), &name);
209 typeEl = typeEl->m_typeList;
213 void TypeAnnotation::appendToTypeList(TypeAnnotationPtr typeList) {
214 if (m_typeList) {
215 TypeAnnotationPtr current = m_typeList;
216 while (current->m_typeList) {
217 current = current->m_typeList;
219 current->m_typeList = typeList;
220 } else {
221 m_typeList = typeList;
225 int TypeAnnotation::numTypeArgs() const {
226 int n = 0;
227 TypeAnnotationPtr typeEl = m_typeArgs;
228 while (typeEl) {
229 ++n;
230 typeEl = typeEl->m_typeList;
232 return n;
235 TypeAnnotationPtr TypeAnnotation::getTypeArg(int n) const {
236 int i = 0;
237 TypeAnnotationPtr typeEl = m_typeArgs;
238 while (typeEl) {
239 if (i == n) {
240 return typeEl;
242 ++i;
243 typeEl = typeEl->m_typeList;
245 return TypeAnnotationPtr();
248 bool TypeAnnotation::isPrimType(const char* str) const{
249 return !strcasecmp(m_name.c_str(), str);
252 TypeStructure::Kind TypeAnnotation::getKind() const {
253 if (isVoid()) {
254 return TypeStructure::Kind::T_void;
257 // Primitive types
258 if (isPrimType("HH\\int")) {
259 return TypeStructure::Kind::T_int;
261 if (isPrimType("HH\\bool")) {
262 return TypeStructure::Kind::T_bool;
264 if (isPrimType("HH\\float")) {
265 return TypeStructure::Kind::T_float;
267 if (isPrimType("HH\\string")) {
268 return TypeStructure::Kind::T_string;
270 if (isPrimType("HH\\resource")) {
271 return TypeStructure::Kind::T_resource;
273 if (isPrimType("HH\\num")) {
274 return TypeStructure::Kind::T_num;
276 if (isPrimType("HH\\arraykey")) {
277 return TypeStructure::Kind::T_arraykey;
279 if (isPrimType("HH\\noreturn")) {
280 return TypeStructure::Kind::T_noreturn;
283 if (isMixed()) {
284 return TypeStructure::Kind::T_mixed;
286 if (m_tuple) {
287 return TypeStructure::Kind::T_tuple;
289 if (m_function) {
290 return TypeStructure::Kind::T_fun;
292 if (!strcasecmp(m_name.c_str(), "array")) {
293 return (m_shape)
294 ? TypeStructure::Kind::T_shape
295 : TypeStructure::Kind::T_array;
297 if (!strcasecmp(m_name.c_str(), "HH\\dict")) {
298 return TypeStructure::Kind::T_dict;
300 if (!strcasecmp(m_name.c_str(), "HH\\vec")) {
301 return TypeStructure::Kind::T_vec;
303 if (!strcasecmp(m_name.c_str(), "HH\\keyset")) {
304 return TypeStructure::Kind::T_keyset;
306 if (m_typevar) {
307 return TypeStructure::Kind::T_typevar;
309 if (m_typeaccess) {
310 return TypeStructure::Kind::T_typeaccess;
312 if (m_xhp) {
313 // TODO(7657500): in the runtime, resolve this type to a class.
314 return TypeStructure::Kind::T_xhp;
317 return TypeStructure::Kind::T_unresolved;
320 const StaticString
321 s_nullable("nullable"),
322 s_kind("kind"),
323 s_name("name"),
324 s_classname("classname"),
325 s_elem_types("elem_types"),
326 s_return_type("return_type"),
327 s_param_types("param_types"),
328 s_generic_types("generic_types"),
329 s_root_name("root_name"),
330 s_access_list("access_list"),
331 s_fields("fields"),
332 s_is_cls_cns("is_cls_cns"),
333 s_value("value"),
334 s_typevars("typevars")
337 /* Turns the argsList linked list of TypeAnnotation into a positioned
338 * static array. */
339 Array TypeAnnotation::argsListToScalarArray(TypeAnnotationPtr ta) const {
340 int i = 0;
341 auto typeargs = Array::Create();
343 auto typeEl = ta;
344 while (typeEl) {
345 typeargs.add(i, Variant(typeEl->getScalarArrayRep()));
346 ++i;
347 typeEl = typeEl->m_typeList;
349 return typeargs;
352 void TypeAnnotation::shapeFieldsToScalarArray(Array& rep,
353 TypeAnnotationPtr ta) const {
354 auto fields = Array::Create();
355 auto shapeField = ta;
356 while (shapeField) {
357 assert(shapeField->m_typeArgs);
358 auto field = Array::Create();
359 if (shapeField->isClsCnsShapeField()) field.add(s_is_cls_cns, true_varNR);
360 field.add(s_value, Variant(shapeField->m_typeArgs->getScalarArrayRep()));
361 fields.add(String(shapeField->m_name), Variant(field.get()));
362 shapeField = shapeField->m_typeList;
364 rep.add(s_fields, Variant(fields));
367 Array TypeAnnotation::getScalarArrayRep() const {
368 auto rep = Array::Create();
370 bool nullable = (bool) m_nullable;
371 if (nullable) {
372 rep.add(s_nullable, true_varNR);
375 TypeStructure::Kind kind = getKind();
376 rep.add(s_kind, Variant(static_cast<uint8_t>(kind)));
378 switch (kind) {
379 case TypeStructure::Kind::T_tuple:
380 assert(m_typeArgs);
381 rep.add(s_elem_types, Variant(argsListToScalarArray(m_typeArgs)));
382 break;
383 case TypeStructure::Kind::T_fun:
384 assert(m_typeArgs);
385 // return type is the first of the typeArgs
386 rep.add(s_return_type, Variant(m_typeArgs->getScalarArrayRep()));
387 rep.add(s_param_types,
388 Variant(argsListToScalarArray(m_typeArgs->m_typeList)));
389 break;
390 case TypeStructure::Kind::T_array:
391 case TypeStructure::Kind::T_dict:
392 case TypeStructure::Kind::T_vec:
393 case TypeStructure::Kind::T_keyset:
394 if (m_typeArgs) {
395 rep.add(s_generic_types, Variant(argsListToScalarArray(m_typeArgs)));
397 break;
398 case TypeStructure::Kind::T_shape:
399 shapeFieldsToScalarArray(rep, m_typeArgs);
400 break;
401 case TypeStructure::Kind::T_typevar:
402 rep.add(s_name, Variant(m_name));
403 break;
404 case TypeStructure::Kind::T_typeaccess: {
405 // for now, only store the vanilla names (strings) as part of the
406 // access list
407 rep.add(s_root_name, Variant(m_name));
408 auto accList = Array::Create();
409 auto typeEl = m_typeArgs;
410 int i = 0;
411 while (typeEl) {
412 accList.add(i, Variant(typeEl->vanillaName()));
413 ++i;
414 typeEl = typeEl->m_typeList;
416 rep.add(s_access_list, Variant(accList));
417 break;
419 case TypeStructure::Kind::T_xhp:
420 rep.add(s_classname, Variant(m_name));
421 break;
422 case TypeStructure::Kind::T_unresolved:
423 rep.add(s_classname, Variant(m_name));
424 if (m_typeArgs) {
425 rep.add(s_generic_types, Variant(argsListToScalarArray(m_typeArgs)));
427 break;
428 default:
429 break;
432 if (!m_generics.empty()) {
433 rep.add(s_typevars, Variant(m_generics));
436 rep.setEvalScalar();
437 return rep;