Arquivos
hhvm/hphp/compiler/expression/unary_op_expression.cpp
T
mwilliams a8e7668189 Get rid of lots of outputCPP code
Very little is actually used anymore
2013-03-07 20:44:34 -08:00

620 linhas
18 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/unary_op_expression.h>
#include <compiler/expression/object_property_expression.h>
#include <util/parser/hphp.tab.hpp>
#include <compiler/analysis/code_error.h>
#include <compiler/analysis/file_scope.h>
#include <compiler/statement/statement_list.h>
#include <compiler/option.h>
#include <compiler/expression/expression_list.h>
#include <compiler/analysis/function_scope.h>
#include <compiler/expression/simple_variable.h>
#include <compiler/analysis/variable_table.h>
#include <compiler/expression/scalar_expression.h>
#include <compiler/expression/constant_expression.h>
#include <compiler/expression/binary_op_expression.h>
#include <compiler/expression/encaps_list_expression.h>
#include <runtime/base/type_conversions.h>
#include <runtime/base/builtin_functions.h>
#include <compiler/parser/parser.h>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
// constructors/destructors
inline void UnaryOpExpression::ctorInit() {
switch (m_op) {
case T_INC:
case T_DEC:
m_localEffects = AssignEffect;
m_exp->setContext(Expression::OprLValue);
m_exp->setContext(Expression::DeepOprLValue);
// this is hacky, what we need is LValueWrapper
if (!m_exp->is(Expression::KindOfSimpleVariable)) {
m_exp->setContext(Expression::LValue);
m_exp->clearContext(Expression::NoLValueWrapper);
}
break;
case T_PRINT:
m_localEffects = IOEffect;
break;
case T_EXIT:
case T_INCLUDE:
case T_INCLUDE_ONCE:
case T_REQUIRE:
case T_REQUIRE_ONCE:
case T_EVAL:
case T_CLONE:
m_localEffects = UnknownEffect;
break;
case T_ISSET:
case T_EMPTY:
setExistContext();
break;
case T_UNSET:
m_localEffects = AssignEffect;
m_exp->setContext(UnsetContext);
break;
case T_CLASS:
case T_FUNCTION:
m_localEffects = CreateEffect;
break;
case T_ARRAY:
default:
break;
}
}
void UnaryOpExpression::setDefinedScope(BlockScopeRawPtr scope) {
always_assert(m_op == T_CLASS || m_op == T_FUNCTION);
m_definedScope = scope;
}
UnaryOpExpression::UnaryOpExpression
(EXPRESSION_CONSTRUCTOR_BASE_PARAMETERS, ExpressionPtr exp, int op, bool front)
: Expression(EXPRESSION_CONSTRUCTOR_BASE_PARAMETER_VALUES),
m_exp(exp), m_op(op), m_front(front) {
ctorInit();
}
UnaryOpExpression::UnaryOpExpression
(EXPRESSION_CONSTRUCTOR_PARAMETERS, ExpressionPtr exp, int op, bool front)
: Expression(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(UnaryOpExpression)),
m_exp(exp), m_op(op), m_front(front) {
ctorInit();
}
ExpressionPtr UnaryOpExpression::clone() {
UnaryOpExpressionPtr exp(new UnaryOpExpression(*this));
Expression::deepCopy(exp);
exp->m_exp = Clone(m_exp);
return exp;
}
bool UnaryOpExpression::isTemporary() const {
switch (m_op) {
case '!':
case '+':
case '-':
case '~':
case T_ARRAY:
return true;
}
return false;
}
bool UnaryOpExpression::isScalar() const {
switch (m_op) {
case '!':
case '+':
case '-':
case '~':
case '@':
return m_exp->isScalar();
case T_ARRAY:
return (!m_exp || m_exp->isScalar());
default:
break;
}
return false;
}
bool UnaryOpExpression::isCast() const {
switch (m_op) {
case T_INT_CAST:
case T_DOUBLE_CAST:
case T_STRING_CAST:
case T_ARRAY_CAST:
case T_OBJECT_CAST:
case T_BOOL_CAST:
return true;
default: break;
}
return false;
}
TypePtr UnaryOpExpression::getCastType() const {
switch (m_op) {
case T_INT_CAST: return Type::Int64;
case T_DOUBLE_CAST: return Type::Double;
case T_STRING_CAST: return Type::String;
case T_ARRAY_CAST: return Type::Array;
case T_OBJECT_CAST: return Type::Object;
case T_BOOL_CAST: return Type::Boolean;
default: break;
}
return TypePtr();
}
bool UnaryOpExpression::isRefable(bool checkError /*= false */) const {
if (m_op == T_INC || m_op == T_DEC) {
return m_exp->isRefable(checkError);
}
return false;
}
bool UnaryOpExpression::isThis() const {
return false;
}
bool UnaryOpExpression::containsDynamicConstant(AnalysisResultPtr ar) const {
switch (m_op) {
case '+':
case '-':
case T_ARRAY:
return m_exp && m_exp->containsDynamicConstant(ar);
default:
break;
}
return false;
}
bool UnaryOpExpression::getScalarValue(Variant &value) {
if (m_exp) {
if (m_op == T_ARRAY) {
return m_exp->getScalarValue(value);
}
Variant t;
return m_exp->getScalarValue(t) &&
preCompute(t, value);
}
if (m_op != T_ARRAY) return false;
value = Array::Create();
return true;
}
///////////////////////////////////////////////////////////////////////////////
// parser functions
void UnaryOpExpression::onParse(AnalysisResultConstPtr ar, FileScopePtr scope) {
if (m_op == T_EVAL) {
ConstructPtr self = shared_from_this();
Compiler::Error(Compiler::UseEvaluation, self);
scope->setAttribute(FileScope::ContainsLDynamicVariable);
}
}
///////////////////////////////////////////////////////////////////////////////
// static analysis functions
void UnaryOpExpression::analyzeProgram(AnalysisResultPtr ar) {
if (m_exp) m_exp->analyzeProgram(ar);
if ((m_op == T_CLASS || m_op == T_FUNCTION) &&
ar->getPhase() == AnalysisResult::AnalyzeFinal) {
ar->link(getFileScope(), m_definedScope->getContainingFile());
}
}
bool UnaryOpExpression::preCompute(CVarRef value, Variant &result) {
bool ret = true;
try {
g_context->setThrowAllErrors(true);
switch(m_op) {
case '!':
result = (!toBoolean(value)); break;
case '+':
result = value.unary_plus(); break;
case '-':
result = value.negate(); break;
case '~':
result = ~value; break;
case '@':
result = value; break;
case T_INT_CAST:
result = value.toInt64(); break;
case T_DOUBLE_CAST:
result = toDouble(value); break;
case T_STRING_CAST:
result = toString(value); break;
case T_BOOL_CAST:
result = toBoolean(value); break;
case T_EMPTY:
result = empty(value); break;
case T_ISSET:
result = isset(value); break;
case T_INC:
case T_DEC:
assert(false);
default:
ret = false;
break;
}
} catch (...) {
ret = false;
}
g_context->setThrowAllErrors(false);
return ret;
}
ConstructPtr UnaryOpExpression::getNthKid(int n) const {
switch (n) {
case 0:
return m_exp;
default:
assert(false);
break;
}
return ConstructPtr();
}
int UnaryOpExpression::getKidCount() const {
return 1;
}
void UnaryOpExpression::setNthKid(int n, ConstructPtr cp) {
switch (n) {
case 0:
m_exp = boost::dynamic_pointer_cast<Expression>(cp);
break;
default:
assert(false);
break;
}
}
bool UnaryOpExpression::canonCompare(ExpressionPtr e) const {
if (!Expression::canonCompare(e)) return false;
UnaryOpExpressionPtr u =
static_pointer_cast<UnaryOpExpression>(e);
return m_op == u->m_op &&
m_front == u->m_front;
}
ExpressionPtr UnaryOpExpression::preOptimize(AnalysisResultConstPtr ar) {
Variant value;
Variant result;
if (m_exp && ar->getPhase() >= AnalysisResult::FirstPreOptimize) {
if (m_op == T_UNSET) {
if (m_exp->isScalar() ||
(m_exp->is(KindOfExpressionList) &&
static_pointer_cast<ExpressionList>(m_exp)->getCount() == 0)) {
recomputeEffects();
return CONSTANT("null");
}
return ExpressionPtr();
}
}
if (m_op == T_ISSET && m_exp->is(KindOfExpressionList) &&
static_pointer_cast<ExpressionList>(m_exp)->getListKind() ==
ExpressionList::ListKindParam) {
ExpressionListPtr el(static_pointer_cast<ExpressionList>(m_exp));
result = true;
int i = 0, n = el->getCount();
for (; i < n; i++) {
ExpressionPtr e((*el)[i]);
if (!e || !e->isScalar() || !e->getScalarValue(value)) break;
if (!isset(value)) {
result = false;
}
}
if (i == n) {
return replaceValue(makeScalarExpression(ar, result));
}
} else if (m_op != T_ARRAY &&
m_exp &&
m_exp->isScalar() &&
m_exp->getScalarValue(value) &&
preCompute(value, result)) {
return replaceValue(makeScalarExpression(ar, result));
} else if (m_op == T_BOOL_CAST) {
switch (m_exp->getKindOf()) {
default: break;
case KindOfBinaryOpExpression: {
int op = static_pointer_cast<BinaryOpExpression>(m_exp)->getOp();
switch (op) {
case T_LOGICAL_OR:
case T_BOOLEAN_OR:
case T_LOGICAL_AND:
case T_BOOLEAN_AND:
case T_LOGICAL_XOR:
case T_INSTANCEOF:
case '<':
case T_IS_SMALLER_OR_EQUAL:
case '>':
case T_IS_GREATER_OR_EQUAL:
case T_IS_IDENTICAL:
case T_IS_NOT_IDENTICAL:
case T_IS_EQUAL:
case T_IS_NOT_EQUAL:
return m_exp;
}
break;
}
case KindOfUnaryOpExpression: {
int op = static_pointer_cast<UnaryOpExpression>(m_exp)->getOp();
switch (op) {
case T_BOOL_CAST:
case '!':
case T_ISSET:
case T_EMPTY:
case T_PRINT:
return m_exp;
}
break;
}
}
}
return ExpressionPtr();
}
ExpressionPtr UnaryOpExpression::postOptimize(AnalysisResultConstPtr ar) {
if (m_op == T_PRINT && m_exp->is(KindOfEncapsListExpression) &&
!m_exp->hasEffect()) {
EncapsListExpressionPtr e = static_pointer_cast<EncapsListExpression>
(m_exp);
e->stripConcat();
}
if (m_op == T_UNSET_CAST && !hasEffect()) {
if (!getScope()->getVariables()->
getAttribute(VariableTable::ContainsCompact) ||
!m_exp->isScalar()) {
return CONSTANT("null");
}
} else if (m_op == T_UNSET && m_exp->is(KindOfExpressionList) &&
!static_pointer_cast<ExpressionList>(m_exp)->getCount()) {
recomputeEffects();
return CONSTANT("null");
} else if (m_op == T_BOOL_CAST) {
if (m_exp->getActualType() &&
m_exp->getActualType()->is(Type::KindOfBoolean)) {
return replaceValue(m_exp);
}
} else if (m_op != T_ARRAY &&
m_exp &&
m_exp->isScalar()) {
Variant value;
Variant result;
if (m_exp->getScalarValue(value) && preCompute(value, result)) {
return replaceValue(makeScalarExpression(ar, result));
}
}
return ExpressionPtr();
}
void UnaryOpExpression::setExistContext() {
if (m_exp) {
if (m_exp->is(Expression::KindOfExpressionList)) {
ExpressionListPtr exps = dynamic_pointer_cast<ExpressionList>(m_exp);
if (exps->getListKind() == ExpressionList::ListKindParam) {
for (int i = 0; i < exps->getCount(); i++) {
(*exps)[i]->setContext(Expression::ExistContext);
}
return;
}
}
m_exp->setContext(Expression::ExistContext);
}
}
TypePtr UnaryOpExpression::inferTypes(AnalysisResultPtr ar, TypePtr type,
bool coerce) {
TypePtr et; // expected m_exp's type
TypePtr rt; // return type
switch (m_op) {
case '!': et = rt = Type::Boolean; break;
case '+':
case '-': et = Type::Numeric; rt = Type::Numeric; break;
case T_INC:
case T_DEC:
case '~': et = rt = Type::Primitive; break;
case T_CLONE: et = Type::Some; rt = Type::Object; break;
case '@': et = type; rt = Type::Variant; break;
case T_INT_CAST: et = rt = Type::Int64; break;
case T_DOUBLE_CAST: et = rt = Type::Double; break;
case T_STRING_CAST: et = rt = Type::String; break;
case T_ARRAY: et = Type::Some; rt = Type::Array; break;
case T_ARRAY_CAST: et = rt = Type::Array; break;
case T_OBJECT_CAST: et = rt = Type::Object; break;
case T_BOOL_CAST: et = rt = Type::Boolean; break;
case T_UNSET_CAST: et = Type::Some; rt = Type::Variant; break;
case T_UNSET: et = Type::Null; rt = Type::Variant; break;
case T_EXIT: et = Type::Primitive; rt = Type::Variant; break;
case T_PRINT: et = Type::String; rt = Type::Int64; break;
case T_ISSET: et = Type::Variant; rt = Type::Boolean;
setExistContext();
break;
case T_EMPTY: et = Type::Some; rt = Type::Boolean;
setExistContext();
break;
case T_INCLUDE:
case T_INCLUDE_ONCE:
case T_REQUIRE:
case T_REQUIRE_ONCE: et = Type::String; rt = Type::Variant; break;
case T_EVAL:
et = Type::String;
rt = Type::Any;
getScope()->getVariables()->forceVariants(ar, VariableTable::AnyVars);
break;
case T_DIR:
case T_FILE: et = rt = Type::String; break;
default:
assert(false);
}
if (m_exp) {
TypePtr expType = m_exp->inferAndCheck(ar, et, false);
if (Type::SameType(expType, Type::String) &&
(m_op == T_INC || m_op == T_DEC)) {
rt = expType = m_exp->inferAndCheck(ar, Type::Variant, true);
}
switch (m_op) {
case '+':
case '-':
if (Type::SameType(expType, Type::Int64) ||
Type::SameType(expType, Type::Double)) {
rt = expType;
}
break;
case T_INC:
case T_DEC:
case '~':
if (Type::SameType(expType, Type::Int64) ||
Type::SameType(expType, Type::Double) ||
Type::SameType(expType, Type::String)) {
rt = expType;
}
break;
case T_ISSET:
case T_EMPTY:
if (m_exp->is(Expression::KindOfExpressionList)) {
ExpressionListPtr exps =
dynamic_pointer_cast<ExpressionList>(m_exp);
if (exps->getListKind() == ExpressionList::ListKindParam) {
for (int i = 0; i < exps->getCount(); i++) {
SetExpTypeForExistsContext(ar, (*exps)[i], m_op == T_EMPTY);
}
}
} else {
SetExpTypeForExistsContext(ar, m_exp, m_op == T_EMPTY);
}
break;
default:
break;
}
}
return rt;
}
void UnaryOpExpression::SetExpTypeForExistsContext(AnalysisResultPtr ar,
ExpressionPtr e,
bool allowPrimitives) {
if (!e) return;
TypePtr at(e->getActualType());
if (!allowPrimitives && at &&
at->isExactType() && at->isPrimitive()) {
at = e->inferAndCheck(ar, Type::Variant, true);
}
TypePtr it(e->getImplementedType());
TypePtr et(e->getExpectedType());
if (et && et->is(Type::KindOfVoid)) e->setExpectedType(TypePtr());
if (at && (!it || Type::IsMappedToVariant(it)) &&
((allowPrimitives && Type::HasFastCastMethod(at)) ||
(!allowPrimitives &&
(at->is(Type::KindOfObject) ||
at->is(Type::KindOfArray) ||
at->is(Type::KindOfString))))) {
e->setExpectedType(it ? at : TypePtr());
}
}
ExpressionPtr UnaryOpExpression::unneededHelper() {
if ((m_op != '@' && m_op != T_ISSET && m_op != T_EMPTY) ||
!m_exp->getContainedEffects()) {
return Expression::unneededHelper();
}
if (m_op == '@') {
m_exp = m_exp->unneeded();
}
return static_pointer_cast<Expression>(shared_from_this());
}
///////////////////////////////////////////////////////////////////////////////
// code generation functions
void UnaryOpExpression::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
if (m_front) {
switch (m_op) {
case T_CLONE: cg_printf("clone "); break;
case T_INC: cg_printf("++"); break;
case T_DEC: cg_printf("--"); break;
case '+': cg_printf("+"); break;
case '-': cg_printf("-"); break;
case '!': cg_printf("!"); break;
case '~': cg_printf("~"); break;
case T_INT_CAST: cg_printf("(int)"); break;
case T_DOUBLE_CAST: cg_printf("(double)"); break;
case T_STRING_CAST: cg_printf("(string)"); break;
case T_ARRAY_CAST: cg_printf("(array)"); break;
case T_OBJECT_CAST: cg_printf("(object)"); break;
case T_BOOL_CAST: cg_printf("(bool)"); break;
case T_UNSET: cg_printf("unset("); break;
case T_UNSET_CAST: cg_printf("(unset)"); break;
case T_EXIT: cg_printf("exit("); break;
case '@': cg_printf("@"); break;
case T_ARRAY: cg_printf("array("); break;
case T_PRINT: cg_printf("print "); break;
case T_ISSET: cg_printf("isset("); break;
case T_EMPTY: cg_printf("empty("); break;
case T_INCLUDE: cg_printf("include "); break;
case T_INCLUDE_ONCE: cg_printf("include_once "); break;
case T_EVAL: cg_printf("eval("); break;
case T_REQUIRE: cg_printf("require "); break;
case T_REQUIRE_ONCE: cg_printf("require_once "); break;
case T_FILE: cg_printf("__FILE__"); break;
case T_DIR: cg_printf("__DIR__"); break;
case T_CLASS: cg_printf("class "); break;
case T_FUNCTION: cg_printf("function "); break;
default:
assert(false);
}
}
if (m_exp) m_exp->outputPHP(cg, ar);
if (m_front) {
switch (m_op) {
case T_UNSET:
case T_EXIT:
case T_ARRAY:
case T_ISSET:
case T_EMPTY:
case T_EVAL: cg_printf(")"); break;
default:
break;
}
} else {
switch (m_op) {
case T_INC: cg_printf("++"); break;
case T_DEC: cg_printf("--"); break;
default:
assert(false);
}
}
}