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

400 linhas
13 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/array_element_expression.h>
#include <compiler/expression/simple_variable.h>
#include <compiler/expression/scalar_expression.h>
#include <compiler/analysis/variable_table.h>
#include <compiler/analysis/code_error.h>
#include <compiler/option.h>
#include <compiler/expression/static_member_expression.h>
#include <compiler/analysis/function_scope.h>
#include <util/parser/hphp.tab.hpp>
#include <runtime/base/complex_types.h>
#include <runtime/base/builtin_functions.h>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
// constructors/destructors
ArrayElementExpression::ArrayElementExpression
(EXPRESSION_CONSTRUCTOR_PARAMETERS,
ExpressionPtr variable, ExpressionPtr offset)
: Expression(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(ArrayElementExpression)),
LocalEffectsContainer(AccessorEffect),
m_variable(variable), m_offset(offset), m_global(false),
m_dynamicGlobal(false) {
m_variable->setContext(Expression::AccessContext);
if (m_variable->is(Expression::KindOfSimpleVariable)) {
SimpleVariablePtr var =
dynamic_pointer_cast<SimpleVariable>(m_variable);
if (var->getName() == "GLOBALS") {
m_global = true;
m_dynamicGlobal = true;
if (m_offset && m_offset->is(Expression::KindOfScalarExpression)) {
ScalarExpressionPtr offset =
dynamic_pointer_cast<ScalarExpression>(m_offset);
if (offset->isLiteralString()) {
m_globalName = offset->getIdentifier();
if (!m_globalName.empty()) {
m_dynamicGlobal = false;
}
}
}
}
}
}
ExpressionPtr ArrayElementExpression::clone() {
ArrayElementExpressionPtr exp(new ArrayElementExpression(*this));
Expression::deepCopy(exp);
exp->m_variable = Clone(m_variable);
exp->m_offset = Clone(m_offset);
return exp;
}
void ArrayElementExpression::setContext(Context context) {
m_context |= context;
switch (context) {
case Expression::LValue:
if (!hasContext(Expression::UnsetContext)) {
m_variable->setContext(Expression::LValue);
}
if (m_variable->is(Expression::KindOfObjectPropertyExpression)) {
m_variable->clearContext(Expression::NoLValueWrapper);
}
// special case for $GLOBALS[], we do not need lvalue wrapper
if (m_variable->is(Expression::KindOfSimpleVariable)) {
SimpleVariablePtr var =
dynamic_pointer_cast<SimpleVariable>(m_variable);
if (var->getName() == "GLOBALS") {
m_context |= Expression::NoLValueWrapper;
}
}
break;
case Expression::DeepAssignmentLHS:
case Expression::DeepOprLValue:
case Expression::ExistContext:
case Expression::UnsetContext:
case Expression::DeepReference:
m_variable->setContext(context);
break;
case Expression::RefValue:
case Expression::RefParameter:
m_variable->setContext(DeepReference);
break;
case Expression::InvokeArgument:
m_variable->setContext(context);
setContext(NoLValueWrapper);
default:
break;
}
}
void ArrayElementExpression::clearContext(Context context) {
m_context &= ~context;
switch (context) {
case Expression::LValue:
case Expression::DeepOprLValue:
case Expression::DeepAssignmentLHS:
case Expression::UnsetContext:
case Expression::DeepReference:
m_variable->clearContext(context);
break;
case Expression::InvokeArgument:
m_variable->clearContext(context);
clearContext(NoLValueWrapper);
break;
case Expression::RefValue:
case Expression::RefParameter:
m_variable->clearContext(DeepReference);
break;
default:
break;
}
}
///////////////////////////////////////////////////////////////////////////////
// parser functions
bool ArrayElementExpression::appendClass(ExpressionPtr cls,
AnalysisResultConstPtr ar,
FileScopePtr file) {
if (m_variable->is(Expression::KindOfArrayElementExpression)) {
return dynamic_pointer_cast<ArrayElementExpression>(m_variable)
->appendClass(cls, ar, file);
}
if (m_variable->is(Expression::KindOfSimpleVariable) ||
m_variable->is(Expression::KindOfDynamicVariable)) {
StaticMemberExpressionPtr sme(
new StaticMemberExpression(
m_variable->getScope(), m_variable->getLocation(),
cls, m_variable));
sme->onParse(ar, file);
m_variable = sme;
m_global = m_dynamicGlobal = false;
m_globalName.clear();
return true;
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
// static analysis functions
bool ArrayElementExpression::isTemporary() const {
return !m_global &&
!(m_context & (AccessContext|LValue|RefValue|UnsetContext));
}
void ArrayElementExpression::analyzeProgram(AnalysisResultPtr ar) {
m_variable->analyzeProgram(ar);
if (m_offset) m_offset->analyzeProgram(ar);
if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
if (m_global) {
if (getContext() & (LValue|RefValue|DeepReference)) {
setContext(NoLValueWrapper);
} else if (!m_dynamicGlobal &&
!(getContext() &
(LValue|RefValue|RefParameter|DeepReference|
UnsetContext|ExistContext))) {
VariableTablePtr vars = ar->getVariables();
Symbol *sym = vars->getSymbol(m_globalName);
if (!sym || sym->getDeclaration().get() == this) {
Compiler::Error(Compiler::UseUndeclaredGlobalVariable,
shared_from_this());
}
}
} else {
TypePtr at(m_variable->getActualType());
TypePtr et(m_variable->getExpectedType());
if (et &&
(et->is(Type::KindOfSequence) ||
et->is(Type::KindOfAutoSequence)) &&
at && at->isExactType()) {
// since Sequence maps to Variant in the runtime,
// using Sequence for the expected type will
// never allow the necessary casts to be generated.
m_variable->setExpectedType(at);
}
}
}
}
ConstructPtr ArrayElementExpression::getNthKid(int n) const {
switch (n) {
case 0:
return m_variable;
case 1:
return m_offset;
default:
assert(false);
break;
}
return ConstructPtr();
}
int ArrayElementExpression::getKidCount() const {
return 2;
}
void ArrayElementExpression::setNthKid(int n, ConstructPtr cp) {
switch (n) {
case 0:
m_variable = boost::dynamic_pointer_cast<Expression>(cp);
break;
case 1:
m_offset = boost::dynamic_pointer_cast<Expression>(cp);
break;
default:
assert(false);
break;
}
}
bool ArrayElementExpression::canonCompare(ExpressionPtr e) const {
return m_offset && Expression::canonCompare(e);
}
ExpressionPtr ArrayElementExpression::preOptimize(AnalysisResultConstPtr ar) {
if (!(m_context & (RefValue|LValue|UnsetContext|OprLValue|
InvokeArgument|DeepReference|DeepOprLValue))) {
if (m_offset && m_variable->isScalar()) {
Variant v, o;
if (m_variable->getScalarValue(v)) {
if (m_context & ExistContext &&
!v.isArray() &&
!v.isString() &&
!m_offset->hasEffect()) {
return replaceValue(makeConstant(ar, "null"));
}
if (m_offset->isScalar() && m_offset->getScalarValue(o)) {
try {
g_context->setThrowAllErrors(true);
Variant res = v.rvalAt(
o, hasContext(ExistContext) ?
AccessFlags::None : AccessFlags::Error);
g_context->setThrowAllErrors(false);
return replaceValue(makeScalarExpression(ar, res));
} catch (...) {
g_context->setThrowAllErrors(false);
}
}
}
}
}
return ExpressionPtr();
}
ExpressionPtr ArrayElementExpression::postOptimize(AnalysisResultConstPtr ar) {
if (!hasLocalEffect(AccessorEffect)) return ExpressionPtr();
TypePtr at(m_variable->getActualType());
if (at && (at->is(Type::KindOfString) || at->is(Type::KindOfArray))) {
clearLocalEffect(AccessorEffect);
return dynamic_pointer_cast<Expression>(shared_from_this());
}
return ExpressionPtr();
}
/**
* ArrayElementExpression comes from:
*
* reference_variable[|expr]
* ->object_dim_list[|expr]
* encaps T_VARIABLE[expr]
* encaps ${T_STRING[expr]}
*/
TypePtr ArrayElementExpression::inferTypes(AnalysisResultPtr ar,
TypePtr type, bool coerce) {
ConstructPtr self = shared_from_this();
if (m_offset &&
!(m_context & (UnsetContext | ExistContext |
InvokeArgument | LValue | RefValue))) {
setLocalEffect(DiagnosticEffect);
}
if (m_context & (AssignmentLHS|OprLValue)) {
clearLocalEffect(AccessorEffect);
} else if (m_context & (LValue | RefValue)) {
setLocalEffect(CreateEffect);
}
// handling $GLOBALS[...]
if (m_variable->is(Expression::KindOfSimpleVariable)) {
SimpleVariablePtr var =
dynamic_pointer_cast<SimpleVariable>(m_variable);
if (var->getName() == "GLOBALS") {
clearLocalEffect(AccessorEffect);
m_global = true;
m_dynamicGlobal = true;
getScope()->getVariables()->
setAttribute(VariableTable::NeedGlobalPointer);
VariableTablePtr vars = ar->getVariables();
Lock l(ar->getMutex());
if (m_offset && m_offset->is(Expression::KindOfScalarExpression)) {
ScalarExpressionPtr offset =
dynamic_pointer_cast<ScalarExpression>(m_offset);
if (offset->isLiteralString()) {
m_globalName = offset->getIdentifier();
if (!m_globalName.empty()) {
m_dynamicGlobal = false;
clearLocalEffect(DiagnosticEffect);
getScope()->getVariables()->
setAttribute(VariableTable::NeedGlobalPointer);
TypePtr ret;
if (coerce) {
ret = vars->add(m_globalName, type, true, ar, self,
ModifierExpressionPtr());
} else {
ret = vars->checkVariable(m_globalName, type, coerce, ar, self);
}
getScope()->getVariables()->addSuperGlobal(m_globalName);
return ret;
}
}
} else {
vars->setAttribute(VariableTable::ContainsDynamicVariable);
}
if (hasContext(LValue) || hasContext(RefValue)) {
vars->forceVariants(ar, VariableTable::AnyVars);
vars->setAttribute(VariableTable::ContainsLDynamicVariable);
}
if (m_offset) {
m_offset->inferAndCheck(ar, Type::Primitive, false);
}
return m_implementedType = Type::Variant; // so not to lose values
}
}
if ((hasContext(LValue) || hasContext(RefValue)) &&
!hasContext(UnsetContext)) {
m_variable->setContext(LValue);
}
TypePtr varType;
if (m_offset) {
varType = m_variable->inferAndCheck(ar, coerce ? Type::AutoSequence :
Type::Sequence, coerce);
m_offset->inferAndCheck(ar, Type::Some, false);
} else {
if (hasContext(ExistContext) || hasContext(UnsetContext)) {
if (getScope()->isFirstPass()) {
Compiler::Error(Compiler::InvalidArrayElement, self);
}
}
m_variable->inferAndCheck(ar, Type::Array, true);
}
if (varType && Type::SameType(varType, Type::String)) {
m_implementedType.reset();
return Type::String;
}
TypePtr ret = propagateTypes(ar, Type::Variant);
m_implementedType = Type::Variant;
return ret; // so not to lose values
}
ExpressionPtr ArrayElementExpression::unneeded() {
if (m_global) {
if (m_offset) return m_offset->unneeded();
}
return Expression::unneeded();
}
///////////////////////////////////////////////////////////////////////////////
// code generation functions
void ArrayElementExpression::outputPHP(CodeGenerator &cg,
AnalysisResultPtr ar) {
if (Option::ConvertSuperGlobals && m_global && !m_dynamicGlobal &&
getScope() && (getScope()->is(BlockScope::ProgramScope) ||
getScope()-> getVariables()->
isConvertibleSuperGlobal(m_globalName))) {
cg_printf("$%s", m_globalName.c_str());
} else {
m_variable->outputPHP(cg, ar);
cg_printf("[");
if (m_offset) m_offset->outputPHP(cg, ar);
cg_printf("]");
}
}