Fix a bug where we set AttrUnique on non-unique classes
[hiphop-php.git] / hphp / compiler / expression / static_member_expression.cpp
blobd789fc936d2d03ce3070c0726f9083fdf659a573
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/compiler/expression/static_member_expression.h"
18 #include "hphp/compiler/expression/simple_variable.h"
19 #include "hphp/compiler/expression/dynamic_variable.h"
20 #include "hphp/compiler/expression/scalar_expression.h"
21 #include "hphp/compiler/analysis/class_scope.h"
22 #include "hphp/compiler/analysis/variable_table.h"
23 #include "hphp/compiler/analysis/code_error.h"
24 #include "hphp/compiler/analysis/function_scope.h"
25 #include "hphp/util/util.h"
26 #include "hphp/util/hash.h"
27 #include "hphp/parser/hphp.tab.hpp"
28 #include "hphp/compiler/option.h"
30 using namespace HPHP;
32 ///////////////////////////////////////////////////////////////////////////////
33 // constructors/destructors
35 StaticMemberExpression::StaticMemberExpression
36 (EXPRESSION_CONSTRUCTOR_PARAMETERS,
37 ExpressionPtr classExp, ExpressionPtr exp)
38 : Expression(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(StaticMemberExpression)),
39 StaticClassName(classExp), m_exp(exp), m_valid(false),
40 m_dynamicClass(false) {
41 if (exp->is(KindOfSimpleVariable)) {
42 SimpleVariablePtr s(dynamic_pointer_cast<SimpleVariable>(exp));
43 m_exp = ExpressionPtr
44 (new ScalarExpression(getScope(), getLocation(),
45 T_STRING, s->getName(), true));
47 } else {
48 always_assert(exp->is(KindOfDynamicVariable));
49 m_exp = dynamic_pointer_cast<DynamicVariable>(exp)->getSubExpression();
53 ExpressionPtr StaticMemberExpression::clone() {
54 StaticMemberExpressionPtr exp(new StaticMemberExpression(*this));
55 Expression::deepCopy(exp);
56 if (m_class) exp->m_class = m_class->clone();
57 exp->m_exp = m_exp->clone();
58 return exp;
61 ///////////////////////////////////////////////////////////////////////////////
62 // parser functions
64 ///////////////////////////////////////////////////////////////////////////////
65 // static analysis functions
67 bool StaticMemberExpression::findMember(AnalysisResultPtr ar, string &name,
68 Symbol *&sym) {
69 if (m_exp->is(Expression::KindOfScalarExpression)) {
70 ScalarExpressionPtr var = dynamic_pointer_cast<ScalarExpression>(m_exp);
71 name = var->getString();
74 if (m_class) return false;
76 sym = nullptr;
77 m_resolvedClass = resolveClass();
78 if (!m_resolvedClass) return isRedeclared();
80 if (m_resolvedClass->derivesFromRedeclaring() == Derivation::Redeclaring) {
81 m_dynamicClass = true;
84 if (m_dynamicClass) return true;
86 if (!name.empty()) {
87 ClassScopePtr parent = m_resolvedClass;
88 sym = m_resolvedClass->findProperty(parent, name, ar);
89 if (sym && sym->isStatic()) {
90 m_resolvedClass = parent;
91 } else {
92 sym = nullptr;
96 return true;
99 void StaticMemberExpression::analyzeProgram(AnalysisResultPtr ar) {
100 if (m_class) {
101 m_class->analyzeProgram(ar);
102 } else if (ar->getPhase() >= AnalysisResult::AnalyzeAll) {
103 Symbol *sym;
104 string name;
105 if (findMember(ar, name, sym)) {
106 if (m_resolvedClass) {
107 m_resolvedClass->addUse(getScope(), BlockScope::UseKindStaticRef);
108 if (!sym && !m_dynamicClass && !name.empty() &&
109 ar->getPhase() == AnalysisResult::AnalyzeFinal &&
110 !m_resolvedClass->isTrait()) {
111 Compiler::Error(Compiler::UseUndeclaredVariable, shared_from_this());
115 addUserClass(ar, m_className);
117 m_exp->analyzeProgram(ar);
120 ConstructPtr StaticMemberExpression::getNthKid(int n) const {
121 switch (n) {
122 case 0:
123 return m_class;
124 case 1:
125 return m_exp;
126 default:
127 assert(false);
128 break;
130 return ConstructPtr();
133 int StaticMemberExpression::getKidCount() const {
134 return 2;
137 void StaticMemberExpression::setNthKid(int n, ConstructPtr cp) {
138 switch (n) {
139 case 0:
140 m_class = dynamic_pointer_cast<Expression>(cp);
141 break;
142 case 1:
143 m_exp = dynamic_pointer_cast<Expression>(cp);
144 break;
145 default:
146 assert(false);
147 break;
151 ExpressionPtr StaticMemberExpression::preOptimize(AnalysisResultConstPtr ar) {
152 if (m_class) updateClassName();
153 return ExpressionPtr();
156 ExpressionPtr StaticMemberExpression::postOptimize(AnalysisResultConstPtr ar) {
157 Symbol *sym = nullptr;
158 if (m_class) updateClassName();
159 if (m_class || !m_resolvedClass || !m_valid ||
160 !m_exp->is(Expression::KindOfScalarExpression)) {
161 return ExpressionPtr();
164 ClassScopePtr cls = ar->findExactClass(shared_from_this(), m_className);
165 if (!cls || (cls->isVolatile() && !isPresent())) {
166 return ExpressionPtr();
169 ScalarExpressionPtr var = dynamic_pointer_cast<ScalarExpression>(m_exp);
170 const std::string &name = var->getString();
172 sym = cls->findProperty(cls, name, ar);
173 if (sym && !sym->isIndirectAltered() && sym->isStatic()) {
174 if (sym->isPrivate() ? cls == getClassScope() :
175 sym->isProtected() ?
176 getClassScope() && getClassScope()->derivesFrom(ar, cls->getName(),
177 true, false) :
178 true) {
179 ConstructPtr init = sym->getClassInitVal();
180 if (init) {
181 ExpressionPtr rep = dynamic_pointer_cast<Expression>(init);
182 if (rep->isScalar()) {
183 ExpressionPtr repClone = Clone(rep, getScope());
184 if (!repClone->getActualType()) {
185 repClone->setActualType(getActualType());
187 return replaceValue(repClone);
192 return ExpressionPtr();
196 * static_member can only be one of these two forms:
198 * T::$member
199 * T::$$member or T::${$member}, where $member can be an arbitrary expression
200 * The former is represented by a ScalarExpression with value "member",
201 * the latter by the expression $member.
203 TypePtr StaticMemberExpression::inferTypes(AnalysisResultPtr ar,
204 TypePtr type, bool coerce) {
205 assert(getScope()->is(BlockScope::FunctionScope));
206 ConstructPtr self = shared_from_this();
208 bool modified = m_context & (LValue | RefValue | UnsetContext | RefParameter);
209 if (m_context & (RefValue|RefParameter)) {
210 coerce = true;
211 type = Type::Variant;
213 if (m_class) {
214 if (modified) {
215 if (m_exp->is(Expression::KindOfScalarExpression)) {
216 ScalarExpressionPtr var = dynamic_pointer_cast<ScalarExpression>(m_exp);
217 const std::string &name = var->getString();
218 ar->forceClassVariants(name, getOriginalClass(), true, true);
219 } else {
220 ar->forceClassVariants(getOriginalClass(), true, true);
223 m_class->inferAndCheck(ar, Type::Any, false);
224 m_exp->inferAndCheck(ar, Type::String, false);
225 return Type::Variant;
228 m_exp->inferAndCheck(ar, Type::String, false);
229 m_valid = true;
231 Symbol *sym;
232 string name;
233 m_valid = findMember(ar, name, sym);
234 if (!m_valid) {
235 if (getScope()->isFirstPass()) {
236 ClassScopeRawPtr cscope = getClassScope();
237 if (!cscope ||
238 !cscope->isTrait() ||
239 (!isSelf() && !isParent())) {
240 Compiler::Error(Compiler::UnknownClass, self);
243 } else if (m_resolvedClass) {
244 m_resolvedClass->addUse(getScope(), BlockScope::UseKindStaticRef);
247 VariableTablePtr variables = getScope()->getVariables();
248 variables->setAttribute(VariableTable::NeedGlobalPointer);
250 if (!name.empty()) {
251 if (!m_resolvedClass && !isRedeclared()) {
252 m_implementedType.reset();
253 return Type::Variant;
255 bool found = false;
256 TypePtr tp;
257 if (isRedeclared()) {
258 BOOST_FOREACH(ClassScopePtr clsr,
259 ar->findRedeclaredClasses(m_className)) {
260 sym = clsr->findProperty(clsr, name, ar);
261 if (sym && sym->isStatic()) {
263 GET_LOCK(clsr);
264 if (modified) {
265 sym->setType(ar, getScope(), Type::Variant, true);
266 sym->setIndirectAltered();
267 } else {
268 clsr->checkProperty(getScope(), sym, type, coerce, ar);
271 found = true;
274 tp = Type::Variant;
275 sym = nullptr;
276 } else if (sym) {
277 assert(m_resolvedClass);
279 GET_LOCK(m_resolvedClass);
280 tp = m_resolvedClass->checkProperty(getScope(), sym, type, coerce, ar);
282 found = true;
283 if (modified) {
284 // concurrent modifications here are OK because:
285 // 1) you never clear the bit (you only set it to true)
286 // 2) the value isn't read in type inference
287 sym->setIndirectAltered();
289 } else {
290 GET_LOCK(m_resolvedClass);
291 m_resolvedClass->getVariables()->
292 forceVariant(ar, name,
293 m_resolvedClass == getOriginalClass() ?
294 VariableTable::AnyStaticVars :
295 VariableTable::NonPrivateStaticVars);
296 tp = Type::Variant;
298 m_valid = found || isRedeclared() || m_dynamicClass;
299 m_implementedType.reset();
300 return tp;
303 if (m_resolvedClass || isRedeclared()) {
304 if (modified) {
305 if (isRedeclared()) {
306 BOOST_FOREACH(ClassScopePtr clsr,
307 ar->findRedeclaredClasses(m_className)) {
308 int mask = clsr == getOriginalClass() ?
309 VariableTable::AnyStaticVars : VariableTable::NonPrivateStaticVars;
310 GET_LOCK(clsr);
311 clsr->getVariables()->forceVariants(ar, mask);
313 } else {
314 int mask = m_resolvedClass == getOriginalClass() ?
315 VariableTable::AnyStaticVars : VariableTable::NonPrivateStaticVars;
316 GET_LOCK(m_resolvedClass);
317 m_resolvedClass->getVariables()->forceVariants(ar, mask);
322 // we have to use a variant to hold dynamic value
323 m_implementedType.reset();
324 return Type::Variant;
327 unsigned StaticMemberExpression::getCanonHash() const {
328 int64_t val = Expression::getCanonHash() +
329 hash_string(Util::toLower(m_className).c_str(), m_className.size());
330 return ~unsigned(val) ^ unsigned(val >> 32);
333 bool StaticMemberExpression::canonCompare(ExpressionPtr e) const {
334 if (!Expression::canonCompare(e)) return false;
335 StaticMemberExpressionPtr s =
336 static_pointer_cast<StaticMemberExpression>(e);
337 return m_className == s->m_className;
340 ///////////////////////////////////////////////////////////////////////////////
342 void StaticMemberExpression::outputCodeModel(CodeGenerator &cg) {
343 cg.printObjectHeader("ClassPropertyExpression", 3);
344 StaticClassName::outputCodeModel(cg);
345 if (m_exp->is(Expression::KindOfScalarExpression)) {
346 cg.printPropertyHeader("propertyName");
347 ScalarExpressionPtr var = dynamic_pointer_cast<ScalarExpression>(m_exp);
348 cg.printValue(var->getString());
349 } else {
350 cg.printPropertyHeader("propertyExpression");
351 m_exp->outputCodeModel(cg);
353 cg.printPropertyHeader("sourceLocation");
354 cg.printLocation(this->getLocation());
355 cg.printObjectFooter();
358 ///////////////////////////////////////////////////////////////////////////////
359 // code generation functions
361 void StaticMemberExpression::outputPHP(CodeGenerator &cg,
362 AnalysisResultPtr ar) {
363 StaticClassName::outputPHP(cg, ar);
365 cg_printf("::$");
366 bool needsClose = false;
367 switch (m_exp->getKindOf()) {
368 case KindOfScalarExpression:
370 ScalarExpressionPtr var = dynamic_pointer_cast<ScalarExpression>(m_exp);
371 cg_printf("%s", var->getString().c_str());
372 return;
374 case KindOfSimpleVariable:
375 case KindOfDynamicVariable:
376 break;
377 default:
378 cg_printf("{");
379 needsClose = true;
381 m_exp->outputPHP(cg, ar);
382 if (needsClose) {
383 cg_printf("}");