Arquivos
hhvm/hphp/compiler/expression/list_assignment.cpp
T
jan 1ecf21f22b ListAssignment: define RHS kind explicitly for all expression types
It's easy to introduce bug by adding a new expression and not defining
its ListAssignment RHS kind. Add all expression types explicitly and
remove default case so that it becomes compile time error if the list is
not updated.
2013-03-15 09:07:36 -07:00

282 linhas
9.1 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/list_assignment.h>
#include <compiler/expression/assignment_expression.h>
#include <compiler/expression/expression_list.h>
#include <compiler/analysis/file_scope.h>
#include <compiler/analysis/function_scope.h>
#include <compiler/expression/array_element_expression.h>
#include <compiler/expression/object_property_expression.h>
#include <compiler/expression/unary_op_expression.h>
#include <compiler/expression/binary_op_expression.h>
#include <util/parser/hphp.tab.hpp>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
// constructors/destructors
/*
Determine whether the rhs behaves normall, or abnormally.
1) If the expression is the silence operator, recurse on the inner expression.
2) If the expression is a list assignment expression, recurse on the
RHS of the expression.
3) If the expression is one of the following, then E behaves normally:
Simple/Dynamic variable (including $this and superglobals)
Array element expression
Property expression
Static variable expression
Function call expression
Preinc/predec expression (but not postinc/postdec)
Assignment expression
Assignment op expression
Binding assignment expression
Include/require expression
Eval expression
Array expression
Array cast expression
4) For all other expressions, E behaves abnormally. This includes:
All binary operator expressions
All unary operator expressions except silence and preinc/predec
Scalar expression of type null, bool, int, double, or string
Qop expression (?:)
Constant expression
Class constant expression
Isset or empty expression
Exit expression
Instanceof expression
*/
static ListAssignment::RHSKind GetRHSKind(ExpressionPtr rhs) {
switch (rhs->getKindOf()) {
case Expression::KindOfSimpleVariable:
case Expression::KindOfDynamicVariable:
case Expression::KindOfArrayElementExpression:
case Expression::KindOfObjectPropertyExpression:
case Expression::KindOfStaticMemberExpression:
case Expression::KindOfSimpleFunctionCall:
case Expression::KindOfDynamicFunctionCall:
case Expression::KindOfObjectMethodExpression:
case Expression::KindOfNewObjectExpression:
case Expression::KindOfAssignmentExpression:
case Expression::KindOfExpressionList:
case Expression::KindOfIncludeExpression:
return ListAssignment::Regular;
case Expression::KindOfListAssignment:
return GetRHSKind(static_pointer_cast<ListAssignment>(rhs)->getArray());
case Expression::KindOfUnaryOpExpression: {
UnaryOpExpressionPtr u(static_pointer_cast<UnaryOpExpression>(rhs));
switch (u->getOp()) {
case '@':
return GetRHSKind(u->getExpression());
case T_INC:
case T_DEC:
return u->getFront() ?
ListAssignment::Regular : ListAssignment::Checked;
case T_EVAL:
case T_ARRAY:
case T_ARRAY_CAST:
return ListAssignment::Regular;
default:
return ListAssignment::Null;
}
break;
}
case Expression::KindOfBinaryOpExpression: {
BinaryOpExpressionPtr b(static_pointer_cast<BinaryOpExpression>(rhs));
return b->isAssignmentOp() || b->getOp() == '+' ?
ListAssignment::Regular : ListAssignment::Null;
}
case Expression::KindOfQOpExpression:
return ListAssignment::Checked;
// invalid context
case Expression::KindOfArrayPairExpression:
case Expression::KindOfParameterExpression:
case Expression::KindOfModifierExpression:
case Expression::KindOfUserAttribute:
always_assert(false);
// non-arrays
case Expression::KindOfScalarExpression:
case Expression::KindOfConstantExpression:
case Expression::KindOfClassConstantExpression:
case Expression::KindOfEncapsListExpression:
case Expression::KindOfClosureExpression:
return ListAssignment::Null;
}
// unreachable for known expression kinds
always_assert(false);
}
static bool AssignmentCouldSet(ExpressionListPtr vars, ExpressionPtr var) {
for (int i = 0; i < vars->getCount(); i++) {
ExpressionPtr v = (*vars)[i];
if (!v) continue;
if (v->is(Expression::KindOfSimpleVariable) &&
v->canonCompare(var)) {
return true;
}
if (v->is(Expression::KindOfDynamicVariable)) return true;
if (v->is(Expression::KindOfListAssignment) &&
AssignmentCouldSet(static_pointer_cast<ListAssignment>(v)->
getVariables(), var)) {
return true;
}
}
return false;
}
ListAssignment::ListAssignment
(EXPRESSION_CONSTRUCTOR_PARAMETERS,
ExpressionListPtr variables, ExpressionPtr array)
: Expression(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(ListAssignment)),
m_variables(variables), m_array(array), m_rhsKind(Regular) {
setLValue();
if (m_array) {
m_rhsKind = GetRHSKind(m_array);
if (m_array->is(KindOfSimpleVariable)) {
if (AssignmentCouldSet(m_variables, m_array)) {
m_array->setContext(LValue);
}
}
}
}
ExpressionPtr ListAssignment::clone() {
ListAssignmentPtr exp(new ListAssignment(*this));
Expression::deepCopy(exp);
exp->m_variables = Clone(m_variables);
exp->m_array = Clone(m_array);
return exp;
}
void ListAssignment::setLValue() {
if (m_variables) {
for (int i = 0; i < m_variables->getCount(); i++) {
ExpressionPtr exp = (*m_variables)[i];
if (exp) {
if (exp->is(Expression::KindOfListAssignment)) {
ListAssignmentPtr sublist =
dynamic_pointer_cast<ListAssignment>(exp);
sublist->setLValue();
} else {
// Magic contexts I took from assignment expression
exp->setContext(Expression::DeepAssignmentLHS);
exp->setContext(Expression::AssignmentLHS);
exp->setContext(Expression::LValue);
exp->setContext(Expression::NoLValueWrapper);
}
}
}
}
}
///////////////////////////////////////////////////////////////////////////////
// parser functions
///////////////////////////////////////////////////////////////////////////////
// static analysis functions
void ListAssignment::analyzeProgram(AnalysisResultPtr ar) {
if (m_variables) m_variables->analyzeProgram(ar);
if (m_array) m_array->analyzeProgram(ar);
FunctionScopePtr func = getFunctionScope();
if (func) func->disableInline();
if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
if (m_variables) {
for (int i = 0; i < m_variables->getCount(); i++) {
ExpressionPtr exp = (*m_variables)[i];
if (exp) {
if (!exp->is(Expression::KindOfListAssignment)) {
CheckNeeded(exp, ExpressionPtr());
}
}
}
}
}
}
ConstructPtr ListAssignment::getNthKid(int n) const {
switch (n) {
case 0:
return m_variables;
case 1:
return m_array;
default:
assert(false);
break;
}
return ConstructPtr();
}
int ListAssignment::getKidCount() const {
return 2;
}
void ListAssignment::setNthKid(int n, ConstructPtr cp) {
switch (n) {
case 0:
m_variables = boost::dynamic_pointer_cast<ExpressionList>(cp);
break;
case 1:
m_array = boost::dynamic_pointer_cast<Expression>(cp);
break;
default:
assert(false);
break;
}
}
TypePtr ListAssignment::inferTypes(AnalysisResultPtr ar, TypePtr type,
bool coerce) {
if (m_variables) {
for (int i = m_variables->getCount(); i--; ) {
ExpressionPtr exp = (*m_variables)[i];
if (exp) {
if (exp->is(Expression::KindOfListAssignment)) {
exp->inferAndCheck(ar, Type::Any, false);
} else {
inferAssignmentTypes(ar, Type::Variant, true, exp);
}
}
}
}
if (!m_array) return TypePtr();
return m_array->inferAndCheck(ar, Type::Variant, false);
}
///////////////////////////////////////////////////////////////////////////////
// code generation functions
void ListAssignment::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
cg_printf("list(");
if (m_variables) m_variables->outputPHP(cg, ar);
if (m_array) {
cg_printf(") = ");
m_array->outputPHP(cg, ar);
} else {
cg_printf(")");
}
}