Arquivos
hhvm/hphp/compiler/expression/object_property_expression.cpp
T

350 linhas
11 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/object_property_expression.h>
#include <compiler/expression/scalar_expression.h>
#include <compiler/expression/expression_list.h>
#include <compiler/analysis/code_error.h>
#include <compiler/analysis/class_scope.h>
#include <compiler/analysis/function_scope.h>
#include <compiler/analysis/file_scope.h>
#include <compiler/analysis/variable_table.h>
#include <compiler/option.h>
#include <compiler/expression/simple_variable.h>
#include <util/hash.h>
#include <util/parser/hphp.tab.hpp>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
// constructors/destructors
ObjectPropertyExpression::ObjectPropertyExpression
(EXPRESSION_CONSTRUCTOR_PARAMETERS,
ExpressionPtr object, ExpressionPtr property)
: Expression(
EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(ObjectPropertyExpression)),
LocalEffectsContainer(AccessorEffect),
m_object(object), m_property(property), m_propSym(nullptr) {
m_valid = false;
m_propSymValid = false;
m_object->setContext(Expression::ObjectContext);
m_object->setContext(Expression::AccessContext);
}
ExpressionPtr ObjectPropertyExpression::clone() {
ObjectPropertyExpressionPtr exp(new ObjectPropertyExpression(*this));
Expression::deepCopy(exp);
exp->m_object = Clone(m_object);
exp->m_property = Clone(m_property);
return exp;
}
///////////////////////////////////////////////////////////////////////////////
// parser functions
///////////////////////////////////////////////////////////////////////////////
// static analysis functions
bool ObjectPropertyExpression::isTemporary() const {
return !m_valid && !(m_context & (LValue | RefValue | UnsetContext));
}
bool ObjectPropertyExpression::isNonPrivate(AnalysisResultPtr ar) {
// To tell whether a property is declared as private in the context
ClassScopePtr cls = getOriginalClass();
if (!cls || !cls->getVariables()->hasNonStaticPrivate()) return true;
if (m_property->getKindOf() != Expression::KindOfScalarExpression) {
return false;
}
ScalarExpressionPtr name =
dynamic_pointer_cast<ScalarExpression>(m_property);
string propName = name->getLiteralString();
if (propName.empty()) {
return false;
}
Symbol *sym = cls->getVariables()->getSymbol(propName);
if (!sym || sym->isStatic() || !sym->isPrivate()) return true;
return false;
}
void ObjectPropertyExpression::setContext(Context context) {
m_context |= context;
switch (context) {
case Expression::LValue:
if (!hasContext(Expression::UnsetContext)) {
m_object->setContext(Expression::LValue);
}
break;
case Expression::DeepAssignmentLHS:
case Expression::DeepOprLValue:
case Expression::ExistContext:
case Expression::UnsetContext:
case Expression::DeepReference:
case Expression::InvokeArgument:
m_object->setContext(context);
break;
case Expression::RefValue:
case Expression::RefParameter:
m_object->setContext(DeepReference);
break;
default:
break;
}
if (!m_valid &&
(m_context & (LValue|RefValue)) &&
!(m_context & AssignmentLHS)) {
setLocalEffect(CreateEffect);
}
if (context == InvokeArgument) {
setContext(NoLValueWrapper);
}
}
void ObjectPropertyExpression::clearContext(Context context) {
m_context &= ~context;
switch (context) {
case Expression::LValue:
case Expression::DeepOprLValue:
case Expression::DeepAssignmentLHS:
case Expression::UnsetContext:
case Expression::DeepReference:
case Expression::InvokeArgument:
m_object->clearContext(context);
break;
case Expression::RefValue:
case Expression::RefParameter:
m_object->clearContext(DeepReference);
break;
default:
break;
}
if (!(m_context & (LValue|RefValue))) {
clearLocalEffect(CreateEffect);
}
if (context == InvokeArgument) {
clearContext(NoLValueWrapper);
}
}
void ObjectPropertyExpression::analyzeProgram(AnalysisResultPtr ar) {
m_object->analyzeProgram(ar);
m_property->analyzeProgram(ar);
if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
if (m_valid && !hasLocalEffect(UnknownEffect) &&
!m_object->isThis() &&
(!m_object->is(KindOfSimpleVariable) ||
!static_pointer_cast<SimpleVariable>(m_object)->isGuarded())) {
setLocalEffect(DiagnosticEffect);
}
}
}
ConstructPtr ObjectPropertyExpression::getNthKid(int n) const {
switch (n) {
case 0:
return m_object;
case 1:
return m_property;
default:
assert(false);
break;
}
return ConstructPtr();
}
int ObjectPropertyExpression::getKidCount() const {
return 2;
}
void ObjectPropertyExpression::setNthKid(int n, ConstructPtr cp) {
switch (n) {
case 0:
m_object = boost::dynamic_pointer_cast<Expression>(cp);
break;
case 1:
m_property = boost::dynamic_pointer_cast<Expression>(cp);
break;
default:
assert(false);
break;
}
}
TypePtr ObjectPropertyExpression::inferTypes(AnalysisResultPtr ar,
TypePtr type, bool coerce) {
m_valid = false;
ConstructPtr self = shared_from_this();
TypePtr objectType = m_object->inferAndCheck(ar, Type::Some, false);
if (!m_property->is(Expression::KindOfScalarExpression)) {
m_property->inferAndCheck(ar, Type::String, false);
// we also lost track of which class variable an expression is about, hence
// any type inference could be wrong. Instead, we just force variants on
// all class variables.
if (m_context & (LValue | RefValue)) {
ar->forceClassVariants(getOriginalClass(), false, true);
}
return Type::Variant; // we have to use a variant to hold dynamic value
}
ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>(m_property);
const string &name = exp->getLiteralString();
if (name.empty()) {
m_property->inferAndCheck(ar, Type::String, false);
if (m_context & (LValue | RefValue)) {
ar->forceClassVariants(getOriginalClass(), false, true);
}
return Type::Variant; // we have to use a variant to hold dynamic value
}
m_property->inferAndCheck(ar, Type::String, false);
ClassScopePtr cls;
if (objectType && !objectType->getName().empty()) {
// what object-> has told us
cls = ar->findExactClass(shared_from_this(), objectType->getName());
} else {
if ((m_context & LValue) && objectType &&
!objectType->is(Type::KindOfObject) &&
!objectType->is(Type::KindOfVariant) &&
!objectType->is(Type::KindOfSome) &&
!objectType->is(Type::KindOfAny)) {
m_object->inferAndCheck(ar, Type::Object, true);
}
}
if (!cls) {
if (m_context & (LValue | RefValue | DeepReference | UnsetContext)) {
ar->forceClassVariants(name, getOriginalClass(), false, true);
}
return Type::Variant;
}
// resolved to this class
if (m_context & RefValue) {
type = Type::Variant;
coerce = true;
}
// use $this inside a static function
if (m_object->isThis()) {
FunctionScopePtr func = m_object->getOriginalFunction();
if (!func || func->isStatic()) {
if (getScope()->isFirstPass()) {
Compiler::Error(Compiler::MissingObjectContext, self);
}
m_actualType = Type::Variant;
return m_actualType;
}
}
assert(cls);
if (!m_propSym || cls != m_objectClass.lock()) {
m_objectClass = cls;
ClassScopePtr parent;
m_propSym = cls->findProperty(parent, name, ar);
if (m_propSym) {
if (!parent) {
parent = cls;
}
m_symOwner = parent;
always_assert(m_propSym->isPresent());
m_propSymValid =
(!m_propSym->isPrivate() || getOriginalClass() == parent) &&
!m_propSym->isStatic();
if (m_propSymValid) {
m_symOwner->addUse(getScope(),
BlockScope::GetNonStaticRefUseKind(
m_propSym->getHash()));
}
}
}
TypePtr ret;
if (m_propSymValid && (!cls->derivesFromRedeclaring() ||
m_propSym->isPrivate())) {
always_assert(m_symOwner);
TypePtr t(m_propSym->getType());
if (t && t->is(Type::KindOfVariant)) {
// only check property if we could possibly do some work
ret = t;
} else {
if (coerce && type->is(Type::KindOfAutoSequence) &&
(!t || t->is(Type::KindOfVoid) ||
t->is(Type::KindOfSome) || t->is(Type::KindOfArray))) {
type = Type::Array;
}
assert(getScope()->is(BlockScope::FunctionScope));
GET_LOCK(m_symOwner);
ret = m_symOwner->checkProperty(getScope(), m_propSym, type, coerce, ar);
}
always_assert(m_object->getActualType() &&
m_object->getActualType()->isSpecificObject());
m_valid = true;
return ret;
} else {
m_actualType = Type::Variant;
return m_actualType;
}
}
ExpressionPtr
ObjectPropertyExpression::postOptimize(AnalysisResultConstPtr ar) {
bool changed = false;
if (m_objectClass && hasLocalEffect(AccessorEffect)) {
int prop = hasContext(AssignmentLHS) ?
ClassScope::MayHaveUnknownPropSetter :
hasContext(ExistContext) ?
ClassScope::MayHaveUnknownPropTester :
hasContext(UnsetContext) && hasContext(LValue) ?
ClassScope::MayHavePropUnsetter :
ClassScope::MayHaveUnknownPropGetter;
if ((m_context & (AssignmentLHS|OprLValue)) ||
!m_objectClass->implementsAccessor(prop)) {
clearLocalEffect(AccessorEffect);
changed = true;
}
}
if (m_valid &&
(hasLocalEffect(AccessorEffect) || hasLocalEffect(CreateEffect))) {
clearLocalEffect(AccessorEffect);
clearLocalEffect(CreateEffect);
changed = true;
}
return changed ?
dynamic_pointer_cast<Expression>(shared_from_this()) :
ExpressionPtr();
}
///////////////////////////////////////////////////////////////////////////////
// code generation functions
void ObjectPropertyExpression::outputPHP(CodeGenerator &cg,
AnalysisResultPtr ar) {
m_object->outputPHP(cg, ar);
cg_printf("->");
if (m_property->getKindOf() == Expression::KindOfScalarExpression) {
m_property->outputPHP(cg, ar);
} else {
cg_printf("{");
m_property->outputPHP(cg, ar);
cg_printf("}");
}
}