/* +----------------------------------------------------------------------+ | 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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(m_value)->inferenceImpl( ar, Type::Some, false); } else if (m_value->is(Expression::KindOfUnaryOpExpression)) { UnaryOpExpressionPtr uexp = dynamic_pointer_cast(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(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(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(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(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(cp); break; case 1: m_value = boost::dynamic_pointer_cast(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(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(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(val)); val = el->listValue(); continue; } if (val->is(KindOfAssignmentExpression)) { val = static_pointer_cast(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(shared_from_this())); return replaceValue(rep); } if (!m_ref && m_variable->is(KindOfArrayElementExpression)) { ArrayElementExpressionPtr ae( static_pointer_cast(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(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(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(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(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(")"); } }