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 +----------------------------------------------------------------------+
16 #include "hphp/compiler/expression/parameter_expression.h"
17 #include "hphp/compiler/type_annotation.h"
18 #include "hphp/compiler/analysis/function_scope.h"
19 #include "hphp/compiler/analysis/file_scope.h"
20 #include "hphp/compiler/analysis/variable_table.h"
21 #include "hphp/compiler/analysis/class_scope.h"
22 #include "hphp/compiler/analysis/code_error.h"
23 #include "hphp/util/text-util.h"
24 #include "hphp/compiler/option.h"
25 #include "hphp/compiler/expression/constant_expression.h"
26 #include "hphp/runtime/vm/runtime.h"
27 #include "hphp/runtime/vm/type-constraint.h"
31 ///////////////////////////////////////////////////////////////////////////////
32 // constructors/destructors
34 ParameterExpression::ParameterExpression(
35 EXPRESSION_CONSTRUCTOR_PARAMETERS
,
36 TypeAnnotationPtr type
,
38 const std::string
&name
,
41 ExpressionPtr defaultValue
,
42 ExpressionPtr attributeList
,
44 : Expression(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(ParameterExpression
))
45 , m_originalType(type
)
49 , m_modifier(modifier
)
50 , m_defaultValue(defaultValue
)
51 , m_attributeList(attributeList
)
52 , m_variadic(variadic
)
54 m_type
= toLower(type
? type
->vanillaName() : "");
56 m_defaultValue
->setContext(InParameterExpression
);
60 ExpressionPtr
ParameterExpression::clone() {
61 ParameterExpressionPtr
exp(new ParameterExpression(*this));
62 Expression::deepCopy(exp
);
63 exp
->m_defaultValue
= Clone(m_defaultValue
);
64 exp
->m_attributeList
= Clone(m_attributeList
);
68 const std::string
ParameterExpression::getOriginalTypeHint() const {
69 assert(hasTypeHint());
70 return m_originalType
->vanillaName();
73 const std::string
ParameterExpression::getUserTypeHint() const {
74 assert(hasUserType());
75 return m_originalType
->fullName();
78 const std::string
ParameterExpression::getTypeHintDisplayName() const {
79 auto name
= m_originalType
->vanillaName();
80 const char* str
= name
.c_str();
81 auto len
= name
.size();
82 if (len
> 3 && tolower(str
[0]) == 'h' && tolower(str
[1]) == 'h' &&
85 const char* stripped
= str
+ 3;
88 strip
= (!strcasecmp(stripped
, "int") ||
89 !strcasecmp(stripped
, "num"));
91 case 4: strip
= !strcasecmp(stripped
, "bool"); break;
92 case 5: strip
= !strcasecmp(stripped
, "float"); break;
93 case 6: strip
= !strcasecmp(stripped
, "string"); break;
94 case 8: strip
= (!strcasecmp(stripped
, "resource") ||
95 !strcasecmp(stripped
, "arraykey"));
107 ///////////////////////////////////////////////////////////////////////////////
110 void ParameterExpression::parseHandler(FileScopeRawPtr file
,
112 // Trait has not been 'inlined' into using class so context is not available
113 if (!m_type
.empty() && !cls
->isTrait()) {
114 fixupSelfAndParentTypehints(cls
);
116 if (m_defaultValue
) {
117 compatibleDefault(file
);
122 void ParameterExpression::fixupSelfAndParentTypehints(ClassScopePtr cls
) {
123 if (m_type
== "self") {
124 m_type
= toLower(cls
->getOriginalName());
125 } else if (m_type
== "parent") {
126 if (!cls
->getOriginalParent().empty()) {
127 m_type
= toLower(cls
->getOriginalParent());
132 ///////////////////////////////////////////////////////////////////////////////
133 // static analysis functions
135 void ParameterExpression::analyzeProgram(AnalysisResultPtr ar
) {
136 if (m_defaultValue
) m_defaultValue
->analyzeProgram(ar
);
139 ConstructPtr
ParameterExpression::getNthKid(int n
) const {
142 return m_defaultValue
;
147 return ConstructPtr();
150 int ParameterExpression::getKidCount() const {
154 void ParameterExpression::setNthKid(int n
, ConstructPtr cp
) {
157 m_defaultValue
= dynamic_pointer_cast
<Expression
>(cp
);
164 static bool useHackTypeHintErrorMessage(const char* hint
) {
165 String
typeName(hint
);
166 MaybeDataType dt
= nameToMaybeDataType(typeName
.get());
167 if (!dt
.hasValue()) {
182 void ParameterExpression::compatibleDefault(FileScopeRawPtr file
) {
184 if (!m_defaultValue
|| !hasTypeHint()) return;
186 DataType defaultType
= KindOfUninit
;
187 if (m_defaultValue
->isArray()) {
188 defaultType
= KindOfArray
;
189 } else if (m_defaultValue
->isLiteralNull()) {
190 defaultType
= KindOfNull
;
192 Variant defaultValue
;
193 if (m_defaultValue
->getScalarValue(defaultValue
)) {
194 defaultType
= defaultValue
.getType();
198 const char* hint
= getTypeHint().c_str();
199 // type declarations mean that we must accept arbitrary types for
200 // arbitrary strings in HipHopSyntax mode. But we can still enforce
202 auto const acceptAny
= m_hhType
&& (strcasecmp(hint
, "HH\\bool") &&
203 strcasecmp(hint
, "HH\\int") &&
204 strcasecmp(hint
, "HH\\num") &&
205 strcasecmp(hint
, "HH\\arraykey") &&
206 strcasecmp(hint
, "HH\\float") &&
207 strcasecmp(hint
, "HH\\num") &&
208 strcasecmp(hint
, "HH\\string") &&
209 strcasecmp(hint
, "HH\\vec") &&
210 strcasecmp(hint
, "HH\\dict") &&
211 strcasecmp(hint
, "HH\\keyset") &&
212 strcasecmp(hint
, "HH\\varray") &&
213 strcasecmp(hint
, "HH\\darray") &&
214 strcasecmp(hint
, "HH\\varray_or_darray") &&
215 strcasecmp(hint
, "array"));
217 // Normally a named type like 'int' is compatible with Int but not integer
218 // Since the default value's type is inferred from the value itself it is
219 // ok to compare against the lower case version of the type hint in hint
221 switch (defaultType
) {
223 // PHP 7 allows user defined constants as parameter default values
224 compat
= m_hhType
|| RuntimeOption::PHP7_ScalarTypes
;
231 compat
= acceptAny
|| !strcasecmp(hint
, "HH\\bool");
235 compat
= (acceptAny
||
236 !strcasecmp(hint
, "HH\\int") ||
237 !strcasecmp(hint
, "HH\\num") ||
238 !strcasecmp(hint
, "HH\\arraykey") ||
239 // PHP 7 allows widening conversions
240 (RuntimeOption::PHP7_ScalarTypes
&&
241 !strcasecmp(hint
, "HH\\float")));
245 compat
= (acceptAny
||
246 !strcasecmp(hint
, "HH\\float") ||
247 !strcasecmp(hint
, "HH\\num"));
250 case KindOfPersistentString
:
252 compat
= (acceptAny
||
253 !strcasecmp(hint
, "HH\\string") ||
254 !strcasecmp(hint
, "HH\\arraykey"));
257 case KindOfPersistentVec
:
259 compat
= (acceptAny
|| !strcasecmp(hint
, "HH\\vec"));
262 case KindOfPersistentDict
:
264 compat
= (acceptAny
|| !strcasecmp(hint
, "HH\\dict"));
267 case KindOfPersistentKeyset
:
269 compat
= (acceptAny
|| !strcasecmp(hint
, "HH\\keyset"));
272 case KindOfPersistentArray
:
274 compat
= (acceptAny
||
275 !strcasecmp(hint
, "array") ||
276 !strcasecmp(hint
, "HH\\varray") ||
277 !strcasecmp(hint
, "HH\\darray") ||
278 !strcasecmp(hint
, "HH\\varray_or_darray")
291 always_assert(false /* likely parser bug */);
295 const char* msg
= "Default value for parameter %s with type %s "
296 "needs to have the same type as the type hint %s";
298 if (!strcasecmp(hint
, "array")) {
299 msg
= "Default value for parameter %s with array type hint "
300 "can only be an array or NULL";
301 } else if (!useHackTypeHintErrorMessage(hint
)) {
302 msg
= "Default value for parameter %s with a class type hint "
307 auto const& name
= getName();
308 auto const tdefault
= HPHP::tname(defaultType
);
310 Compiler::BadDefaultValueType
, msg
,
311 name
.c_str(), tdefault
.c_str(),
312 getTypeHintDisplayName().c_str());
316 ///////////////////////////////////////////////////////////////////////////////
317 // code generation functions
319 void ParameterExpression::outputPHP(CodeGenerator
&cg
, AnalysisResultPtr ar
) {
320 if (!m_type
.empty()) cg_printf("%s ", m_originalType
->vanillaName().c_str());
321 if (m_ref
) cg_printf("&");
322 cg_printf("$%s", m_name
.c_str());
323 if (m_defaultValue
) {
325 m_defaultValue
->outputPHP(cg
, ar
);