Arquivos
hhvm/hphp/compiler/expression/assignment_expression.cpp
T
Jordan Delong 363d1bb20f Code move src/ -> hphp/
This change is mostly for FB internal organizational reasons.
Building is not effected beyond the fact that the target now
lands in hphp/hhvm/hhvm rather than src/hhvm/hhvm.
2013-02-11 02:10:41 -08:00

538 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/assignment_expression.h>
#include <compiler/expression/array_element_expression.h>
#include <compiler/expression/object_property_expression.h>
#include <compiler/analysis/code_error.h>
#include <compiler/expression/constant_expression.h>
#include <compiler/expression/simple_variable.h>
#include <compiler/analysis/block_scope.h>
#include <compiler/analysis/variable_table.h>
#include <compiler/analysis/constant_table.h>
#include <compiler/analysis/file_scope.h>
#include <compiler/expression/unary_op_expression.h>
#include <util/parser/hphp.tab.hpp>
#include <compiler/option.h>
#include <compiler/analysis/class_scope.h>
#include <compiler/analysis/function_scope.h>
#include <compiler/expression/scalar_expression.h>
#include <compiler/expression/expression_list.h>
#include <compiler/expression/simple_function_call.h>
#include <runtime/base/complex_types.h>
#include <runtime/base/builtin_functions.h>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
// constructors/destructors
AssignmentExpression::AssignmentExpression
(EXPRESSION_CONSTRUCTOR_PARAMETERS,
ExpressionPtr variable, ExpressionPtr value, bool ref)
: Expression(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(AssignmentExpression)),
m_variable(variable), m_value(value), m_ref(ref) {
m_variable->setContext(Expression::DeepAssignmentLHS);
m_variable->setContext(Expression::AssignmentLHS);
m_variable->setContext(Expression::LValue);
m_variable->setContext(Expression::NoLValueWrapper);
m_value->setContext(Expression::AssignmentRHS);
if (ref) {
m_variable->setContext(Expression::RefAssignmentLHS);
m_value->setContext(Expression::RefValue);
// we have &new special case that's handled in this class
m_value->setContext(Expression::NoRefWrapper);
}
}
ExpressionPtr AssignmentExpression::clone() {
AssignmentExpressionPtr exp(new AssignmentExpression(*this));
Expression::deepCopy(exp);
exp->m_variable = Clone(m_variable);
exp->m_value = Clone(m_value);
return exp;
}
///////////////////////////////////////////////////////////////////////////////
// parser functions
void AssignmentExpression::onParseRecur(AnalysisResultConstPtr ar,
ClassScopePtr scope) {
// This is that much we can do during parse phase.
TypePtr type;
if (m_value->is(Expression::KindOfScalarExpression)) {
type = static_pointer_cast<ScalarExpression>(m_value)->inferenceImpl(
ar, Type::Some, false);
} else if (m_value->is(Expression::KindOfUnaryOpExpression)) {
UnaryOpExpressionPtr uexp =
dynamic_pointer_cast<UnaryOpExpression>(m_value);
if (uexp->getOp() == T_ARRAY) {
type = Type::Array;
}
}
if (!type) type = Type::Some;
if (m_variable->is(Expression::KindOfConstantExpression)) {
// ...as in ClassConstant statement
// We are handling this one here, not in ClassConstant, purely because
// we need "value" to store in constant table.
ConstantExpressionPtr exp =
dynamic_pointer_cast<ConstantExpression>(m_variable);
scope->getConstants()->add(exp->getName(), type, m_value, ar, m_variable);
} else if (m_variable->is(Expression::KindOfSimpleVariable)) {
SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(m_variable);
scope->getVariables()->add(var->getName(), type, true, ar,
shared_from_this(), scope->getModifiers());
var->clearContext(Declaration); // to avoid wrong CodeError
} else {
assert(false); // parse phase shouldn't handle anything else
}
}
///////////////////////////////////////////////////////////////////////////////
// static analysis functions
int AssignmentExpression::getLocalEffects() const {
return AssignEffect;
}
void AssignmentExpression::analyzeProgram(AnalysisResultPtr ar) {
m_variable->analyzeProgram(ar);
m_value->analyzeProgram(ar);
if (ar->getPhase() == AnalysisResult::AnalyzeAll) {
if (m_ref && m_variable->is(Expression::KindOfSimpleVariable)) {
SimpleVariablePtr var =
dynamic_pointer_cast<SimpleVariable>(m_variable);
const std::string &name = var->getName();
VariableTablePtr variables = getScope()->getVariables();
variables->addUsed(name);
}
} else if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
if (m_variable->is(Expression::KindOfConstantExpression)) {
ConstantExpressionPtr exp =
dynamic_pointer_cast<ConstantExpression>(m_variable);
if (!m_value->isScalar()) {
getScope()->getConstants()->setDynamic(ar, exp->getName(), false);
}
} else {
CheckNeeded(m_variable, m_value);
}
}
}
ConstructPtr AssignmentExpression::getNthKid(int n) const {
switch (n) {
case 0:
return m_variable;
case 1:
return m_value;
default:
assert(false);
break;
}
return ConstructPtr();
}
int AssignmentExpression::getKidCount() const {
return 2;
}
void AssignmentExpression::setNthKid(int n, ConstructPtr cp) {
switch (n) {
case 0:
m_variable = boost::dynamic_pointer_cast<Expression>(cp);
break;
case 1:
m_value = boost::dynamic_pointer_cast<Expression>(cp);
break;
default:
assert(false);
break;
}
}
bool AssignmentExpression::isSimpleGlobalAssign(StringData **name,
TypedValue *tv) const {
if (!m_variable->is(KindOfArrayElementExpression)) return false;
ArrayElementExpressionPtr ae(
static_pointer_cast<ArrayElementExpression>(m_variable));
if (!ae->isSuperGlobal() || ae->isDynamicGlobal()) return false;
Variant v;
if (!m_value->getScalarValue(v) || v.is(KindOfArray)) return false;
if (name) {
*name = StringData::GetStaticString(ae->getGlobalName());
}
if (tv) {
if (v.isString()) {
v = StringData::GetStaticString(v.toCStrRef().get());
}
*tv = *v.asTypedValue();
}
return true;
}
ExpressionPtr AssignmentExpression::optimize(AnalysisResultConstPtr ar) {
if (m_variable->is(Expression::KindOfSimpleVariable)) {
SimpleVariablePtr var =
dynamic_pointer_cast<SimpleVariable>(m_variable);
if (var->checkUnused() &&
!CheckNeeded(var, m_value)) {
if (m_value->getContainedEffects() != getContainedEffects()) {
recomputeEffects();
}
return replaceValue(m_value);
}
}
return ExpressionPtr();
}
ExpressionPtr AssignmentExpression::preOptimize(AnalysisResultConstPtr ar) {
if (Option::EliminateDeadCode &&
ar->getPhase() >= AnalysisResult::FirstPreOptimize) {
// otherwise used & needed flags may not be up to date yet
ExpressionPtr rep = optimize(ar);
if (rep) return rep;
}
if (m_variable->getContainedEffects() & ~(CreateEffect|AccessorEffect)) {
return ExpressionPtr();
}
ExpressionPtr val = m_value;
while (val) {
if (val->is(KindOfExpressionList)) {
ExpressionListPtr el(static_pointer_cast<ExpressionList>(val));
val = el->listValue();
continue;
}
if (val->is(KindOfAssignmentExpression)) {
val = static_pointer_cast<AssignmentExpression>(val)->m_value;
continue;
}
break;
}
if (val && val->isScalar()) {
if (val != m_value) {
ExpressionListPtr rep(new ExpressionList(
getScope(), getLocation(),
ExpressionList::ListKindWrapped));
rep->addElement(m_value);
m_value = val->clone();
rep->addElement(static_pointer_cast<Expression>(shared_from_this()));
return replaceValue(rep);
}
if (!m_ref && m_variable->is(KindOfArrayElementExpression)) {
ArrayElementExpressionPtr ae(
static_pointer_cast<ArrayElementExpression>(m_variable));
ExpressionPtr avar(ae->getVariable());
ExpressionPtr aoff(ae->getOffset());
if (!aoff || aoff->isScalar()) {
avar = avar->getCanonLVal();
while (avar) {
if (avar->isScalar()) {
Variant v,o,r;
if (!avar->getScalarValue(v)) break;
if (!val->getScalarValue(r)) break;
try {
g_context->setThrowAllErrors(true);
if (aoff) {
if (!aoff->getScalarValue(o)) break;
v.set(o, r);
} else {
v.append(r);
}
g_context->setThrowAllErrors(false);
} catch (...) {
break;
}
ExpressionPtr rep(
new AssignmentExpression(
getScope(), getLocation(),
m_variable->replaceValue(Clone(ae->getVariable())),
makeScalarExpression(ar, v), false));
if (!isUnused()) {
ExpressionListPtr el(
new ExpressionList(
getScope(), getLocation(),
ExpressionList::ListKindWrapped));
el->addElement(rep);
el->addElement(val);
rep = el;
}
return replaceValue(rep);
}
avar = avar->getCanonPtr();
}
g_context->setThrowAllErrors(false);
}
}
}
return ExpressionPtr();
}
ExpressionPtr AssignmentExpression::postOptimize(AnalysisResultConstPtr ar) {
return optimize(ar);
}
TypePtr AssignmentExpression::inferTypes(AnalysisResultPtr ar, TypePtr type,
bool coerce) {
return inferAssignmentTypes(ar, type, coerce, m_variable, m_value);
}
///////////////////////////////////////////////////////////////////////////////
// code generation functions
void AssignmentExpression::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
m_variable->outputPHP(cg, ar);
cg_printf(" = ");
if (m_ref) cg_printf("&");
m_value->outputPHP(cg, ar);
}
static void wrapValue(CodeGenerator &cg, AnalysisResultPtr ar,
ExpressionPtr exp, bool ref, bool array, bool varnr) {
bool close = false;
if (ref) {
cg_printf("ref(");
close = true;
} else if (array && !exp->hasCPPTemp() &&
!exp->isTemporary() && !exp->isScalar() &&
exp->getActualType() && !exp->getActualType()->isPrimitive() &&
exp->getActualType()->getKindOf() != Type::KindOfString) {
cg_printf("wrap_variant(");
close = true;
} else if (varnr && exp->getCPPType()->isExactType()) {
bool isScalar = exp->isScalar();
if (!isScalar || !Option::UseScalarVariant) {
cg_printf("VarNR(");
close = true;
} else if (isScalar) {
assert(!cg.hasScalarVariant());
cg.setScalarVariant();
}
}
exp->outputCPP(cg, ar);
cg.clearScalarVariant();
if (close) cg_printf(")");
}
void AssignmentExpression::preOutputStash(CodeGenerator &cg,
AnalysisResultPtr ar, int state) {
if (hasCPPTemp()) return;
if (m_value->hasCPPTemp() &&
(Type::SameType(getType(), m_value->getType()) ||
(Type::IsMappedToVariant(getType()) &&
Type::IsMappedToVariant(m_value->getType())))) {
setUnused(true);
outputCPP(cg, ar);
cg_printf(";\n");
setCPPTemp(m_value->cppTemp());
return;
}
TypePtr at(getActualType());
TypePtr et(getExpectedType());
TypePtr it(getImplementedType());
if (at && !Type::IsMappedToVariant(at) &&
!et && it && Type::IsMappedToVariant(it)) {
m_value->preOutputStash(cg, ar, state);
if (!m_value->hasCPPTemp()) {
// preOutputStash did no work, so we need to
// explicitly do a stash
TypePtr t(m_value->getType());
bool constRef = !t->isPrimitive() &&
(m_value->isTemporary() || !m_value->isLocalExprAltered());
const string &tmp = m_value->genCPPTemp(cg, ar);
if (constRef) cg_printf("const ");
t->outputCPPDecl(cg, ar, getScope());
const char *ref = constRef ? "&" : "";
cg_printf(" %s%s((", ref, tmp.c_str());
m_value->outputCPP(cg, ar);
cg_printf("));\n");
m_value->setCPPTemp(tmp);
}
assert(m_value->hasCPPTemp());
setUnused(true);
outputCPP(cg, ar);
cg_printf(";\n");
setCPPTemp(m_value->cppTemp());
return;
}
return Expression::preOutputStash(cg, ar, state);
}
bool AssignmentExpression::preOutputCPP(CodeGenerator &cg, AnalysisResultPtr ar,
int state) {
if (hasContext(RefValue) && !m_ref) {
if (!cg.inExpression()) return true;
state |= FixOrder | ForceTemp;
}
if (m_variable->is(Expression::KindOfArrayElementExpression)) {
ExpressionPtr exp = m_value;
ExpressionPtr vv(
static_pointer_cast<ArrayElementExpression>(m_variable)->getVariable());
if ((vv->is(KindOfArrayElementExpression) ||
vv->is(KindOfObjectPropertyExpression)) &&
(vv->getContainedEffects() && (CreateEffect|AccessorEffect))) {
/*
We are in a case such as
$a->b['c'] = ...;
$a['b']['c'] = ...;
Where evaluating m_variable may modify $a. Unless we can prove that
the rhs is not referring to the same thing as $a, we must generate
a temporary for it (note that we could do better with the following
checks).
*/
if (!(m_ref && exp->isRefable()) &&
!exp->isTemporary() && !exp->isScalar() &&
exp->getActualType() && !exp->getActualType()->isPrimitive() &&
exp->getActualType()->getKindOf() != Type::KindOfString) {
state |= Expression::StashAll;
}
}
}
return Expression::preOutputCPP(cg, ar, state);
}
bool AssignmentExpression::SpecialAssignment(CodeGenerator &cg,
AnalysisResultPtr ar,
ExpressionPtr lval,
ExpressionPtr rval,
const char *rvalStr, bool ref) {
if (lval->is(KindOfArrayElementExpression)) {
ArrayElementExpressionPtr exp =
dynamic_pointer_cast<ArrayElementExpression>(lval);
if (!exp->isSuperGlobal() && !exp->isDynamicGlobal()) {
exp->getVariable()->outputCPP(cg, ar);
if (exp->getOffset()) {
cg_printf(".set(");
exp->getOffset()->outputCPP(cg, ar);
cg_printf(", (");
} else {
cg_printf(".append((");
}
if (rval) {
wrapValue(cg, ar, rval, ref,
(exp->getVariable()->is(KindOfArrayElementExpression) ||
exp->getVariable()->is(KindOfObjectPropertyExpression)) &&
(exp->getVariable()->getContainedEffects() &&
(CreateEffect|AccessorEffect)), true);
} else {
cg_printf(ref ? "ref(%s)" : "%s", rvalStr);
}
cg_printf(")");
ExpressionPtr off = exp->getOffset();
if (off) {
ScalarExpressionPtr sc =
dynamic_pointer_cast<ScalarExpression>(off);
if (sc) {
if (sc->isLiteralString()) {
String s(sc->getLiteralString());
int64 n;
if (!s.get()->isStrictlyInteger(n)) {
cg_printf(", true"); // skip toKey() at run time
}
}
}
}
cg_printf(")");
return true;
}
} else if (lval->is(KindOfObjectPropertyExpression)) {
ObjectPropertyExpressionPtr var(
dynamic_pointer_cast<ObjectPropertyExpression>(lval));
if (!var->isValid()) {
bool nonPrivate = var->isNonPrivate(ar);
var->outputCPPObject(cg, ar);
if (nonPrivate) {
cg_printf("o_setPublic(");
} else {
cg_printf("o_set(");
}
var->outputCPPProperty(cg, ar);
cg_printf(", %s", ref ? "ref(" : "");
if (rval) {
rval->outputCPP(cg, ar);
} else {
cg_printf(ref ? "ref(%s)" : "%s", rvalStr);
}
if (nonPrivate) {
cg_printf("%s)", ref ? ")" : "");
}
else {
cg_printf("%s%s)",
ref ? ")" : "",
lval->originalClassName(cg, true).c_str());
}
return true;
}
}
return false;
}
void AssignmentExpression::outputCPPImpl(CodeGenerator &cg,
AnalysisResultPtr ar) {
bool ref = (m_ref && m_value->isRefable());
bool setNull = false;
if (SpecialAssignment(cg, ar, m_variable, m_value, NULL, ref)) {
return;
}
if (m_value->isLiteralNull()) {
if (m_variable->is(Expression::KindOfSimpleVariable)) {
setNull = true;
} else {
TypePtr t = m_variable->getCPPType();
if (t && (t->is(Type::KindOfArray) ||
t->is(Type::KindOfObject) ||
t->is(Type::KindOfString))) {
setNull = true;
}
}
}
bool wrapped = true;
if (setNull) {
cg_printf("setNull(");
m_variable->outputCPP(cg, ar);
} else {
if (!m_variable->getCPPType()->isExactType() &&
!(m_value->hasCPPTemp() ?
m_value->getType() : m_value->getCPPType())->isExactType()) {
m_variable->outputCPP(cg, ar);
cg_printf(".assign%s(", ref ? "Ref" : "Val");
wrapped = true;
ref = false;
} else {
if (m_variable->getCPPType()->isExactType()) {
ref = false;
}
if ((wrapped = !isUnused())) {
cg_printf("(");
}
m_variable->outputCPP(cg, ar);
cg_printf(" = ");
}
wrapValue(cg, ar, m_value, ref, false, false);
}
if (wrapped) {
cg_printf(")");
}
}