0b7e8d1251
If a static property is provably not modified, and it has a literal initializer, it is optimized away at translation time. We should check that its accessible before doing that.
369 linhas
12 KiB
C++
369 linhas
12 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include <compiler/expression/static_member_expression.h>
|
|
#include <compiler/expression/simple_variable.h>
|
|
#include <compiler/expression/dynamic_variable.h>
|
|
#include <compiler/expression/scalar_expression.h>
|
|
#include <compiler/analysis/class_scope.h>
|
|
#include <compiler/analysis/variable_table.h>
|
|
#include <compiler/analysis/code_error.h>
|
|
#include <compiler/analysis/function_scope.h>
|
|
#include <util/util.h>
|
|
#include <util/hash.h>
|
|
#include <util/parser/hphp.tab.hpp>
|
|
#include <compiler/option.h>
|
|
|
|
using namespace HPHP;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// constructors/destructors
|
|
|
|
StaticMemberExpression::StaticMemberExpression
|
|
(EXPRESSION_CONSTRUCTOR_PARAMETERS,
|
|
ExpressionPtr classExp, ExpressionPtr exp)
|
|
: Expression(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(StaticMemberExpression)),
|
|
StaticClassName(classExp), m_exp(exp), m_valid(false),
|
|
m_dynamicClass(false) {
|
|
if (exp->is(KindOfSimpleVariable)) {
|
|
SimpleVariablePtr s(dynamic_pointer_cast<SimpleVariable>(exp));
|
|
m_exp = ExpressionPtr
|
|
(new ScalarExpression(getScope(), getLocation(),
|
|
T_STRING, s->getName(), true));
|
|
|
|
} else {
|
|
always_assert(exp->is(KindOfDynamicVariable));
|
|
m_exp = dynamic_pointer_cast<DynamicVariable>(exp)->getSubExpression();
|
|
}
|
|
}
|
|
|
|
ExpressionPtr StaticMemberExpression::clone() {
|
|
StaticMemberExpressionPtr exp(new StaticMemberExpression(*this));
|
|
Expression::deepCopy(exp);
|
|
if (m_class) exp->m_class = m_class->clone();
|
|
exp->m_exp = m_exp->clone();
|
|
return exp;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// parser functions
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// static analysis functions
|
|
|
|
bool StaticMemberExpression::findMember(AnalysisResultPtr ar, string &name,
|
|
Symbol *&sym) {
|
|
if (m_exp->is(Expression::KindOfScalarExpression)) {
|
|
ScalarExpressionPtr var = dynamic_pointer_cast<ScalarExpression>(m_exp);
|
|
name = var->getString();
|
|
}
|
|
|
|
if (m_class) return false;
|
|
|
|
sym = nullptr;
|
|
m_resolvedClass = resolveClass();
|
|
if (!m_resolvedClass) return isRedeclared();
|
|
|
|
if (m_resolvedClass->derivesFromRedeclaring()) {
|
|
m_dynamicClass = true;
|
|
}
|
|
|
|
if (m_dynamicClass) return true;
|
|
|
|
if (!name.empty()) {
|
|
ClassScopePtr parent = m_resolvedClass;
|
|
sym = m_resolvedClass->findProperty(parent, name, ar);
|
|
if (sym && sym->isStatic()) {
|
|
m_resolvedClass = parent;
|
|
} else {
|
|
sym = nullptr;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void StaticMemberExpression::analyzeProgram(AnalysisResultPtr ar) {
|
|
if (m_class) {
|
|
m_class->analyzeProgram(ar);
|
|
} else if (ar->getPhase() >= AnalysisResult::AnalyzeAll) {
|
|
Symbol *sym;
|
|
string name;
|
|
if (findMember(ar, name, sym)) {
|
|
if (m_resolvedClass) {
|
|
m_resolvedClass->addUse(getScope(), BlockScope::UseKindStaticRef);
|
|
if (!sym && !m_dynamicClass && !name.empty() &&
|
|
ar->getPhase() == AnalysisResult::AnalyzeFinal &&
|
|
!m_resolvedClass->isTrait()) {
|
|
Compiler::Error(Compiler::UseUndeclaredVariable, shared_from_this());
|
|
}
|
|
}
|
|
}
|
|
addUserClass(ar, m_className);
|
|
}
|
|
m_exp->analyzeProgram(ar);
|
|
}
|
|
|
|
ConstructPtr StaticMemberExpression::getNthKid(int n) const {
|
|
switch (n) {
|
|
case 0:
|
|
return m_class;
|
|
case 1:
|
|
return m_exp;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
return ConstructPtr();
|
|
}
|
|
|
|
int StaticMemberExpression::getKidCount() const {
|
|
return 2;
|
|
}
|
|
|
|
void StaticMemberExpression::setNthKid(int n, ConstructPtr cp) {
|
|
switch (n) {
|
|
case 0:
|
|
m_class = boost::dynamic_pointer_cast<Expression>(cp);
|
|
break;
|
|
case 1:
|
|
m_exp = boost::dynamic_pointer_cast<Expression>(cp);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
ExpressionPtr StaticMemberExpression::preOptimize(AnalysisResultConstPtr ar) {
|
|
if (m_class) updateClassName();
|
|
return ExpressionPtr();
|
|
}
|
|
|
|
ExpressionPtr StaticMemberExpression::postOptimize(AnalysisResultConstPtr ar) {
|
|
Symbol *sym = nullptr;
|
|
if (m_class) updateClassName();
|
|
if (m_class || !m_resolvedClass || !m_valid ||
|
|
!m_exp->is(Expression::KindOfScalarExpression)) {
|
|
return ExpressionPtr();
|
|
}
|
|
|
|
ClassScopePtr cls = ar->findExactClass(shared_from_this(), m_className);
|
|
if (!cls || (cls->isVolatile() && !isPresent())) {
|
|
return ExpressionPtr();
|
|
}
|
|
|
|
ScalarExpressionPtr var = dynamic_pointer_cast<ScalarExpression>(m_exp);
|
|
const std::string &name = var->getString();
|
|
|
|
sym = cls->findProperty(cls, name, ar);
|
|
if (sym && !sym->isIndirectAltered() && sym->isStatic()) {
|
|
if (sym->isPrivate() ? cls == getClassScope() :
|
|
sym->isProtected() ?
|
|
getClassScope() && getClassScope()->derivesFrom(ar, cls->getName(),
|
|
true, false) :
|
|
true) {
|
|
ConstructPtr init = sym->getClassInitVal();
|
|
if (init) {
|
|
ExpressionPtr rep = dynamic_pointer_cast<Expression>(init);
|
|
if (rep->isScalar()) {
|
|
ExpressionPtr repClone = Clone(rep, getScope());
|
|
if (!repClone->getActualType()) {
|
|
repClone->setActualType(getActualType());
|
|
}
|
|
return replaceValue(repClone);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ExpressionPtr();
|
|
}
|
|
|
|
/**
|
|
* static_member can only be one of these two forms:
|
|
*
|
|
* T::$member
|
|
* T::$$member or T::${$member}, where $member can be an arbitrary expression
|
|
* The former is represented by a ScalarExpression with value "member",
|
|
* the latter by the expression $member.
|
|
*/
|
|
TypePtr StaticMemberExpression::inferTypes(AnalysisResultPtr ar,
|
|
TypePtr type, bool coerce) {
|
|
assert(getScope()->is(BlockScope::FunctionScope));
|
|
ConstructPtr self = shared_from_this();
|
|
|
|
bool modified = m_context & (LValue | RefValue | UnsetContext | RefParameter);
|
|
if (m_context & (RefValue|RefParameter)) {
|
|
coerce = true;
|
|
type = Type::Variant;
|
|
}
|
|
if (m_class) {
|
|
if (modified) {
|
|
if (m_exp->is(Expression::KindOfScalarExpression)) {
|
|
ScalarExpressionPtr var = dynamic_pointer_cast<ScalarExpression>(m_exp);
|
|
const std::string &name = var->getString();
|
|
ar->forceClassVariants(name, getOriginalClass(), true, true);
|
|
} else {
|
|
ar->forceClassVariants(getOriginalClass(), true, true);
|
|
}
|
|
}
|
|
m_class->inferAndCheck(ar, Type::Any, false);
|
|
m_exp->inferAndCheck(ar, Type::String, false);
|
|
return Type::Variant;
|
|
}
|
|
|
|
m_exp->inferAndCheck(ar, Type::String, false);
|
|
m_valid = true;
|
|
|
|
Symbol *sym;
|
|
string name;
|
|
m_valid = findMember(ar, name, sym);
|
|
if (!m_valid) {
|
|
if (getScope()->isFirstPass()) {
|
|
ClassScopeRawPtr cscope = getClassScope();
|
|
if (!cscope ||
|
|
!cscope->isTrait() ||
|
|
(!isSelf() && !isParent())) {
|
|
Compiler::Error(Compiler::UnknownClass, self);
|
|
}
|
|
}
|
|
} else if (m_resolvedClass) {
|
|
m_resolvedClass->addUse(getScope(), BlockScope::UseKindStaticRef);
|
|
}
|
|
|
|
VariableTablePtr variables = getScope()->getVariables();
|
|
variables->setAttribute(VariableTable::NeedGlobalPointer);
|
|
|
|
if (!name.empty()) {
|
|
if (!m_resolvedClass && !isRedeclared()) {
|
|
m_implementedType.reset();
|
|
return Type::Variant;
|
|
}
|
|
bool found = false;
|
|
TypePtr tp;
|
|
if (isRedeclared()) {
|
|
BOOST_FOREACH(ClassScopePtr clsr,
|
|
ar->findRedeclaredClasses(m_className)) {
|
|
sym = clsr->findProperty(clsr, name, ar);
|
|
if (sym && sym->isStatic()) {
|
|
{
|
|
GET_LOCK(clsr);
|
|
if (modified) {
|
|
sym->setType(ar, getScope(), Type::Variant, true);
|
|
sym->setIndirectAltered();
|
|
} else {
|
|
clsr->checkProperty(getScope(), sym, type, coerce, ar);
|
|
}
|
|
}
|
|
found = true;
|
|
}
|
|
}
|
|
tp = Type::Variant;
|
|
sym = nullptr;
|
|
} else if (sym) {
|
|
assert(m_resolvedClass);
|
|
{
|
|
GET_LOCK(m_resolvedClass);
|
|
tp = m_resolvedClass->checkProperty(getScope(), sym, type, coerce, ar);
|
|
}
|
|
found = true;
|
|
if (modified) {
|
|
// concurrent modifications here are OK because:
|
|
// 1) you never clear the bit (you only set it to true)
|
|
// 2) the value isn't read in type inference
|
|
sym->setIndirectAltered();
|
|
}
|
|
} else {
|
|
GET_LOCK(m_resolvedClass);
|
|
m_resolvedClass->getVariables()->
|
|
forceVariant(ar, name,
|
|
m_resolvedClass == getOriginalClass() ?
|
|
VariableTable::AnyStaticVars :
|
|
VariableTable::NonPrivateStaticVars);
|
|
tp = Type::Variant;
|
|
}
|
|
m_valid = found || isRedeclared() || m_dynamicClass;
|
|
m_implementedType.reset();
|
|
return tp;
|
|
}
|
|
|
|
if (m_resolvedClass || isRedeclared()) {
|
|
if (modified) {
|
|
if (isRedeclared()) {
|
|
BOOST_FOREACH(ClassScopePtr clsr,
|
|
ar->findRedeclaredClasses(m_className)) {
|
|
int mask = clsr == getOriginalClass() ?
|
|
VariableTable::AnyStaticVars : VariableTable::NonPrivateStaticVars;
|
|
GET_LOCK(clsr);
|
|
clsr->getVariables()->forceVariants(ar, mask);
|
|
}
|
|
} else {
|
|
int mask = m_resolvedClass == getOriginalClass() ?
|
|
VariableTable::AnyStaticVars : VariableTable::NonPrivateStaticVars;
|
|
GET_LOCK(m_resolvedClass);
|
|
m_resolvedClass->getVariables()->forceVariants(ar, mask);
|
|
}
|
|
}
|
|
}
|
|
|
|
// we have to use a variant to hold dynamic value
|
|
m_implementedType.reset();
|
|
return Type::Variant;
|
|
}
|
|
|
|
unsigned StaticMemberExpression::getCanonHash() const {
|
|
int64_t val = Expression::getCanonHash() +
|
|
hash_string(Util::toLower(m_className).c_str(), m_className.size());
|
|
return ~unsigned(val) ^ unsigned(val >> 32);
|
|
}
|
|
|
|
bool StaticMemberExpression::canonCompare(ExpressionPtr e) const {
|
|
if (!Expression::canonCompare(e)) return false;
|
|
StaticMemberExpressionPtr s =
|
|
static_pointer_cast<StaticMemberExpression>(e);
|
|
return m_className == s->m_className;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// code generation functions
|
|
|
|
void StaticMemberExpression::outputPHP(CodeGenerator &cg,
|
|
AnalysisResultPtr ar) {
|
|
StaticClassName::outputPHP(cg, ar);
|
|
|
|
cg_printf("::$");
|
|
bool needsClose = false;
|
|
switch (m_exp->getKindOf()) {
|
|
case KindOfScalarExpression:
|
|
{
|
|
ScalarExpressionPtr var = dynamic_pointer_cast<ScalarExpression>(m_exp);
|
|
cg_printf("%s", var->getString().c_str());
|
|
return;
|
|
}
|
|
case KindOfSimpleVariable:
|
|
case KindOfDynamicVariable:
|
|
break;
|
|
default:
|
|
cg_printf("{");
|
|
needsClose = true;
|
|
}
|
|
m_exp->outputPHP(cg, ar);
|
|
if (needsClose) {
|
|
cg_printf("}");
|
|
}
|
|
}
|