ExpressionPtr AliasManager::canonicalizeRecur(ExpressionPtr e) { switch (e->getKindOf()) { case Expression::KindOfQOpExpression: canonicalizeKid(e, e->getNthExpr(0), 0); beginScope(); canonicalizeKid(e, e->getNthExpr(1), 1); resetScope(); canonicalizeKid(e, e->getNthExpr(2), 2); endScope(); return canonicalizeNode(e); case Expression::KindOfBinaryOpExpression: if (spc(BinaryOpExpression,e)->isShortCircuitOperator()) { canonicalizeKid(e, e->getNthExpr(0), 0); beginScope(); canonicalizeKid(e, e->getNthExpr(1), 1); endScope(); return canonicalizeNode(e); } break; default: break; } for (int i = 0, n = e->getKidCount(); i < n; i++) { canonicalizeKid(e, e->getNthExpr(i), i); } return canonicalizeNode(e); }
/** * Rewrites the expression rooted in ep so that it is in a form * that a query processor can evaluate while referencing only * state that is contained in the query processor or supplied * to the query processor in the form of arguments to the query. * For instance, a reference to a local variable in the scope * containing the query expression will be rewritten into a * reference to a (synthetic) parameter of the query expression. * This is similar to the way lambda expressions capture variables * from their enclosing environment. * Note that rewriting implies allocating new objects. * The original construct is not mutated in any way. * If the original construct is already in the right form, it is * returned as is. */ ExpressionPtr CaptureExtractor::rewrite(ExpressionPtr ep) { assert(ep != nullptr); switch (ep->getKindOf()) { case Expression::KindOfQueryExpression: { return rewriteQuery(static_pointer_cast<QueryExpression>(ep)); } case Expression::KindOfSelectClause: { // leave select clauses alone, another visitor deals with them. return ep; } case Expression::KindOfFromClause: case Expression::KindOfLetClause: case Expression::KindOfIntoClause: case Expression::KindOfWhereClause: { return rewriteSimpleClause(static_pointer_cast<SimpleQueryClause>(ep)); } case Expression::KindOfGroupClause: case Expression::KindOfJoinClause: case Expression::KindOfOrderbyClause: case Expression::KindOfOrdering: { // leave these alone. they are query specific and not parameterizable. return ep; } case Expression::KindOfObjectPropertyExpression: { return rewriteObjectProperty( static_pointer_cast<ObjectPropertyExpression>(ep)); } case Expression::KindOfSimpleFunctionCall: { return rewriteCall(static_pointer_cast<SimpleFunctionCall>(ep)); } case Expression::KindOfScalarExpression: { // Leave scalars alone. If the query processor can't handle them // rewriting won't help. return ep; } case Expression::KindOfUnaryOpExpression: { return rewriteUnary(static_pointer_cast<UnaryOpExpression>(ep)); } case Expression::KindOfBinaryOpExpression: { return rewriteBinary(static_pointer_cast<BinaryOpExpression>(ep)); } case Expression::KindOfSimpleVariable: { return rewriteSimpleVariable(static_pointer_cast<SimpleVariable>(ep)); } case Expression::KindOfExpressionList: { return rewriteExpressionList(static_pointer_cast<ExpressionList>(ep)); } default: { // If we get here, the expression is not a candidate for evaluation // by the query processor, so just turn it into a query parameter. return newQueryParamRef(ep); } } }
/* 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::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; default: break; } return ListAssignment::Null; }
static bool isReadOnlyAccess(ExpressionPtr e) { if (e->getContext() & (Expression::UnsetContext| Expression::RefValue| Expression::LValue)) { return false; } switch (e->getKindOf()) { case Expression::KindOfConstantExpression: case Expression::KindOfSimpleVariable: case Expression::KindOfArrayElementExpression: case Expression::KindOfDynamicVariable: return true; default: return false; } }
/** * Traverses the expression tree rooted at e and returns true if * any node in the tree is a simple variable that references a * name in m_boundVars. */ bool CaptureExtractor::dependsOnQueryOnlyState(ExpressionPtr e) { assert(e != nullptr); if (e->getKindOf() == Expression::KindOfSimpleVariable) { auto sv = static_pointer_cast<SimpleVariable>(e); auto varName = sv->getName(); for (auto &boundVar : m_boundVars) { if (varName == boundVar) return true; } return false; } auto numKids = e->getKidCount(); for (int i = 0; i < numKids; i++) { auto ei = e->getNthExpr(i); if (ei == nullptr) return false; //Default param if (dependsOnQueryOnlyState(ei)) return true; } return false; }
void DataFlowWalker::process(ExpressionPtr e, bool doAccessChains) { if ((e->getContext() & (Expression::AssignmentLHS|Expression::OprLValue)) || (!doAccessChains && e->hasContext(Expression::AccessContext))) { return; } switch (e->getKindOf()) { case Expression::KindOfListAssignment: processAccessChainLA(static_pointer_cast<ListAssignment>(e)); processAccess(e); break; case Expression::KindOfArrayElementExpression: case Expression::KindOfObjectPropertyExpression: if (!e->hasContext(Expression::AccessContext)) { processAccessChain(e); } // fall through case Expression::KindOfObjectMethodExpression: case Expression::KindOfDynamicFunctionCall: case Expression::KindOfSimpleFunctionCall: case Expression::KindOfNewObjectExpression: case Expression::KindOfIncludeExpression: case Expression::KindOfSimpleVariable: case Expression::KindOfDynamicVariable: case Expression::KindOfStaticMemberExpression: case Expression::KindOfConstantExpression: processAccess(e); break; case Expression::KindOfAssignmentExpression: case Expression::KindOfBinaryOpExpression: case Expression::KindOfUnaryOpExpression: { ExpressionPtr var = e->getStoreVariable(); if (var && var->getContext() & (Expression::AssignmentLHS| Expression::OprLValue)) { processAccessChain(var); processAccess(var); } // fall through } default: processAccess(e); break; } }
static inline std::string ExtractDocComment(ExpressionPtr e) { if (!e) return ""; switch (e->getKindOf()) { case Expression::KindOfAssignmentExpression: { AssignmentExpressionPtr ae(static_pointer_cast<AssignmentExpression>(e)); return ExtractDocComment(ae->getVariable()); } case Expression::KindOfSimpleVariable: { SimpleVariablePtr sv(static_pointer_cast<SimpleVariable>(e)); return sv->getDocComment(); } case Expression::KindOfConstantExpression: { ConstantExpressionPtr ce(static_pointer_cast<ConstantExpression>(e)); return ce->getDocComment(); } default: return ""; } return ""; }
static inline std::string ExtractInitializer(AnalysisResultPtr ar, ExpressionPtr e) { switch (e->getKindOf()) { case Expression::KindOfParameterExpression: { auto p = static_pointer_cast<ParameterExpression>(e); if (!p->defaultValue()) return ""; return p->defaultValue()->getText(ar); } default: // TODO(stephentu): this doesn't allow us to tell the difference between // something like: // class X { public $x; } versus // class X { public $x = null; } // we'll just end up treating both cases like the latter return e->getText(ar); } return ""; }
static ExpressionPtr cloneForInlineRecur(InlineCloneInfo &info, ExpressionPtr exp, const std::string &prefix, AnalysisResultConstPtr ar, FunctionScopePtr scope) { exp->getOriginalScope(); // make sure to cache the original scope exp->setBlockScope(scope); for (int i = 0, n = exp->getKidCount(); i < n; i++) { if (ExpressionPtr k = exp->getNthExpr(i)) { exp->setNthKid(i, cloneForInlineRecur(info, k, prefix, ar, scope)); } } StaticClassName *scn = dynamic_cast<StaticClassName*>(exp.get()); if (scn && scn->isStatic() && !info.staticClass.empty()) { scn->resolveStatic(info.staticClass); } switch (exp->getKindOf()) { case Expression::KindOfSimpleVariable: { SimpleVariablePtr sv(dynamic_pointer_cast<SimpleVariable>(exp)); if (sv->isSuperGlobal()) break; string name; if (sv->isThis()) { if (!info.callWithThis) { if (!sv->hasContext(Expression::ObjectContext)) { exp = sv->makeConstant(ar, "null"); } else { // This will produce the wrong error // we really want a "throw_fatal" ast node. exp = sv->makeConstant(ar, "null"); } break; } if (info.localThis.empty()) break; name = info.localThis; } else { name = prefix + sv->getName(); } SimpleVariablePtr rep(new SimpleVariable( exp->getScope(), exp->getLocation(), name)); rep->copyContext(sv); rep->updateSymbol(SimpleVariablePtr()); rep->getSymbol()->setHidden(); // Conservatively set flags to prevent // the alias manager from getting confused. // On the next pass, it will correct the values, // and optimize appropriately. rep->getSymbol()->setUsed(); rep->getSymbol()->setReferenced(); if (exp->getContext() & (Expression::LValue| Expression::RefValue| Expression::RefParameter)) { info.sepm[name] = rep; } exp = rep; } break; case Expression::KindOfObjectMethodExpression: { FunctionCallPtr call( static_pointer_cast<FunctionCall>(exp)); if (call->getFuncScope() == info.func) { call->setNoInline(); } break; } case Expression::KindOfSimpleFunctionCall: { SimpleFunctionCallPtr call(static_pointer_cast<SimpleFunctionCall>(exp)); call->addLateDependencies(ar); call->setLocalThis(info.localThis); if (call->getFuncScope() == info.func) { call->setNoInline(); } } default: break; } return exp; }
/* Determine whether the rhs behaves normally, 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 Anonymous class expression */ static ListAssignment::RHSKind GetRHSKind(ExpressionPtr rhs) { switch (rhs->getKindOf()) { case Construct::KindOfSimpleVariable: case Construct::KindOfDynamicVariable: case Construct::KindOfArrayElementExpression: case Construct::KindOfObjectPropertyExpression: case Construct::KindOfStaticMemberExpression: case Construct::KindOfSimpleFunctionCall: case Construct::KindOfDynamicFunctionCall: case Construct::KindOfObjectMethodExpression: case Construct::KindOfNewObjectExpression: case Construct::KindOfAssignmentExpression: case Construct::KindOfExpressionList: case Construct::KindOfIncludeExpression: case Construct::KindOfYieldExpression: case Construct::KindOfYieldFromExpression: case Construct::KindOfAwaitExpression: case Construct::KindOfClassExpression: return ListAssignment::Regular; case Construct::KindOfListAssignment: return GetRHSKind(static_pointer_cast<ListAssignment>(rhs)->getArray()); case Construct::KindOfUnaryOpExpression: { auto 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 Construct::KindOfBinaryOpExpression: { auto b = static_pointer_cast<BinaryOpExpression>(rhs); if (b->isAssignmentOp() || b->getOp() == '+' || b->getOp() == T_COLLECTION) { return ListAssignment::Regular; } return ListAssignment::Null; } case Construct::KindOfQOpExpression: case Construct::KindOfNullCoalesceExpression: return ListAssignment::Checked; // invalid context case Construct::KindOfExpression: case Construct::KindOfArrayPairExpression: case Construct::KindOfParameterExpression: case Construct::KindOfModifierExpression: case Construct::KindOfUserAttribute: always_assert(false); // non-arrays case Construct::KindOfScalarExpression: case Construct::KindOfConstantExpression: case Construct::KindOfClassConstantExpression: case Construct::KindOfEncapsListExpression: case Construct::KindOfClosureExpression: return ListAssignment::Null; #define STATEMENT_CASE(x) case Construct::KindOf##x: DECLARE_STATEMENT_TYPES(STATEMENT_CASE) { always_assert(false); } #undef STATEMENT_CASE } // unreachable for known expression kinds always_assert(false); }
void RefDict::updateAccess(ExpressionPtr e) { always_assert(!e->getScope()->inPseudoMain()); int eid = e->getCanonID(); int context = e->getContext(); if (first_pass) { if (!e->is(Expression::KindOfSimpleVariable) && !e->is(Expression::KindOfDynamicVariable)) return; e->clearAvailable(); e->clearReferencedValid(); e->clearReferenced(); SimpleVariablePtr ptr(dynamic_pointer_cast<SimpleVariable>(e)); if (ptr && (ptr->isSuperGlobal() || ptr->isThis())) return; if (e->is(Expression::KindOfSimpleVariable)) { if (BitOps::get_bit(eid, m_referenced)) { e->setReferenced(); } else if (!BitOps::get_bit(eid, m_killed)) { // use as a temp place holder e->setAvailable(); } } } // let the first pass information propagate for both passes, since // we need it in both contexts if (context & Expression::RefAssignmentLHS || context & Expression::RefValue || context & Expression::RefParameter || ((context & Expression::Declaration) == Expression::Declaration)) { if (e->is(Expression::KindOfSimpleVariable)) { BitOps::set_bit(eid, m_referenced, true); BitOps::set_bit(eid, m_killed, false); } else { // for dynamic variables, we must assume the worst BitOps::set(size(), m_referenced, -1); BitOps::set(size(), m_killed, 0); } } else if (e->is(Expression::KindOfSimpleVariable) && context & Expression::LValue && context & Expression::UnsetContext) { BitOps::set_bit(eid, m_referenced, false); BitOps::set_bit(eid, m_killed, true); } if (first_pass) return; // now we're on the second pass if (context & Expression::AssignmentLHS || context & Expression::OprLValue) { // we dealt with this node as a store expression return; } int cls = e->getExprClass(); bool isRhsNeeded = false; bool canKill = false; ExpressionPtr lhs; ExpressionPtr rhs; if (cls & Expression::Store) { // we care about two cases here switch (e->getKindOf()) { case Expression::KindOfAssignmentExpression: // $x = ... { AssignmentExpressionPtr assign( static_pointer_cast<AssignmentExpression>(e)); lhs = assign->getVariable(); rhs = assign->getValue(); isRhsNeeded = Expression::CheckNeededRHS(rhs); canKill = true; } break; case Expression::KindOfBinaryOpExpression: // $x += ... { BinaryOpExpressionPtr binop( static_pointer_cast<BinaryOpExpression>(e)); if (binop->getOp() == T_PLUS_EQUAL) { lhs = binop->getExp1(); rhs = binop->getExp2(); isRhsNeeded = Expression::CheckNeededRHS(rhs); } } break; default: break; } } bool isLhsSimpleVar = false; bool isLhsDynamic = false; bool isRefd = false; if (lhs) { isLhsSimpleVar = lhs->is(Expression::KindOfSimpleVariable); // TODO: can a variable only be simple or dynamic? // If so, this is un-necessary isLhsDynamic = lhs->is(Expression::KindOfDynamicVariable); if (isLhsSimpleVar) { // clean up the LHS AST lhs->clearAvailable(); lhs->clearNeeded(); lhs->clearNeededValid(); if (BitOps::get_bit(lhs->getCanonID(), m_obj)) { lhs->setNeeded(); lhs->setNeededValid(); } else if (!BitOps::get_bit(lhs->getCanonID(), m_noobj)) { lhs->setAvailable(); } } if (lhs->isReferencedValid() && lhs->isReferenced() && isRhsNeeded) { // could we possibly have modified another referenced variable? isRefd = true; } if (isLhsSimpleVar && isRhsNeeded) { // we see this case: // $x = new ... // so we mark $x as being needed BitOps::set_bit(lhs->getCanonID(), m_obj, true); BitOps::set_bit(lhs->getCanonID(), m_noobj, false); } else if (isLhsSimpleVar && canKill && !isRhsNeeded) { // we saw an assignment that was of the form // $x = <primitive> // we can now set $x to be not an object BitOps::set_bit(lhs->getCanonID(), m_obj, false); BitOps::set_bit(lhs->getCanonID(), m_noobj, true); } } if (isLhsDynamic && isRhsNeeded) { // in this case, we must set EVERY variable to contain an object BitOps::set(size(), m_obj, -1 /* true for each bit */); BitOps::set(size(), m_noobj, 0 /* false for each bit */); // we're done, since it can be no worse (no more conservative) than this return; } // do we see a load which could cause the value of this expr to be changed? // for example: // function foo(&$x) { $x = 10; } // $x = 30; // foo($x); /* <-- this is what we care about */ if ((cls & (Expression::Load|Expression::Call)) && (context & (Expression::RefValue|Expression::DeepReference))) { isRefd = true; } // we want to propagate this information to other simple vars we see if (e->is(Expression::KindOfSimpleVariable)) { // clean up the AST e->clearAvailable(); e->clearNeeded(); e->clearNeededValid(); SimpleVariablePtr svp(static_pointer_cast<SimpleVariable>(e)); if (svp->isSuperGlobal() || svp->isThis()) return; // update the AST to *before* the modification if (BitOps::get_bit(eid, m_obj)) { e->setNeeded(); e->setNeededValid(); } else if (!BitOps::get_bit(eid, m_noobj)) { // use as a temp place holder e->setAvailable(); } if (context & Expression::LValue && context & Expression::UnsetContext) { always_assert(!isRefd); // unset($x); BitOps::set_bit(eid, m_obj, false); BitOps::set_bit(eid, m_noobj, true); } else if (isRefd || ((context & Expression::Declaration) == Expression::Declaration)) { // if a simple variable has isRefd, then we need to mark it // as potentially containing an object. // also, if the simple variable is in global context // then we also mark it as potentially containing an object BitOps::set_bit(eid, m_obj, true); BitOps::set_bit(eid, m_noobj, false); } } if (isRefd) { // do a scan for every simple variable referenced value // in the dictionary and mark it as potentially // containing an object (in the bit vector) for (int i = size(); i--; ) { if (ExpressionPtr e = get(i)) { always_assert(e->is(Expression::KindOfSimpleVariable)); always_assert(((unsigned int)i) == e->getCanonID()); if (BitOps::get_bit(i, m_referenced)) { BitOps::set_bit(i, m_obj, true); BitOps::set_bit(i, m_noobj, false); } } } } }
/* Determine whether the rhs behaves normally, 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: case Expression::KindOfYieldExpression: case Expression::KindOfAwaitExpression: case Expression::KindOfQueryExpression: 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)); if (b->isAssignmentOp() || b->getOp() == '+' || b->getOp() == T_COLLECTION) { return ListAssignment::Regular; } return ListAssignment::Null; } case Expression::KindOfQOpExpression: return ListAssignment::Checked; // invalid context case Expression::KindOfArrayPairExpression: case Expression::KindOfParameterExpression: case Expression::KindOfModifierExpression: case Expression::KindOfUserAttribute: case Expression::KindOfFromClause: case Expression::KindOfLetClause: case Expression::KindOfWhereClause: case Expression::KindOfSelectClause: case Expression::KindOfIntoClause: case Expression::KindOfJoinClause: case Expression::KindOfGroupClause: case Expression::KindOfOrderbyClause: case Expression::KindOfOrdering: 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); }
ExpressionPtr AliasManager::canonicalizeNode(ExpressionPtr e) { e->setCanonPtr(ExpressionPtr()); e->setCanonID(0); switch (e->getKindOf()) { case Expression::KindOfObjectMethodExpression: case Expression::KindOfDynamicFunctionCall: case Expression::KindOfSimpleFunctionCall: case Expression::KindOfNewObjectExpression: add(m_bucketMap[0], e); break; case Expression::KindOfListAssignment: add(m_bucketMap[0], e); break; case Expression::KindOfAssignmentExpression: { AssignmentExpressionPtr ae = spc(AssignmentExpression,e); if (e->getContext() & Expression::DeadStore) { Construct::recomputeEffects(); return ae->getValue(); } ExpressionPtr rep; int interf = findInterf(ae->getVariable(), false, rep); if (interf == SameAccess) { switch (rep->getKindOf()) { default: break; case Expression::KindOfAssignmentExpression: { AssignmentExpressionPtr a = spc(AssignmentExpression, rep); ExpressionPtr value = a->getValue(); if (a->getValue()->getContext() & Expression::RefValue) { break; } } case Expression::KindOfUnaryOpExpression: case Expression::KindOfBinaryOpExpression: rep->setContext(Expression::DeadStore); break; } } add(m_bucketMap[0], e); break; } case Expression::KindOfConstantExpression: case Expression::KindOfSimpleVariable: case Expression::KindOfDynamicVariable: case Expression::KindOfArrayElementExpression: case Expression::KindOfObjectPropertyExpression: case Expression::KindOfStaticMemberExpression: if (!(e->getContext() & (Expression::AssignmentLHS| Expression::DeepAssignmentLHS| Expression::OprLValue))) { if (!(e->getContext() & (Expression::LValue| Expression::RefValue| Expression::RefParameter| Expression::UnsetContext))) { ExpressionPtr rep; int interf = findInterf(e, true, rep); if (interf == SameAccess) { if (rep->getKindOf() == e->getKindOf()) { e->setCanonID(rep->getCanonID()); e->setCanonPtr(rep); return ExpressionPtr(); } if (rep->getKindOf() == Expression::KindOfAssignmentExpression) { ExpressionPtr rhs = spc(AssignmentExpression,rep)->getValue(); if (rhs->is(Expression::KindOfScalarExpression)) { rhs = rhs->clone(); getCanonical(rhs); return rhs; } e->setCanonPtr(rhs); } } } add(m_bucketMap[0], e); } else { getCanonical(e); } break; case Expression::KindOfBinaryOpExpression: { BinaryOpExpressionPtr bop = spc(BinaryOpExpression, e); int rop = getOpForAssignmentOp(bop->getOp()); if (rop) { ExpressionPtr lhs = bop->getExp1(); ExpressionPtr rep; if (bop->getContext() & Expression::DeadStore) { Construct::recomputeEffects(); ExpressionPtr rhs = bop->getExp2()->clone(); lhs = lhs->clone(); lhs->clearContext(Expression::LValue); lhs->clearContext(Expression::NoLValueWrapper); lhs->clearContext(Expression::OprLValue); rep = ExpressionPtr (new BinaryOpExpression(e->getLocation(), Expression::KindOfBinaryOpExpression, lhs, rhs, rop)); } else { ExpressionPtr alt; int interf = findInterf(lhs, true, alt); if (interf == SameAccess && alt->is(Expression::KindOfAssignmentExpression)) { ExpressionPtr op0 = spc(AssignmentExpression,alt)->getValue(); if (op0->is(Expression::KindOfScalarExpression)) { ExpressionPtr op1 = bop->getExp2(); ExpressionPtr rhs (new BinaryOpExpression(e->getLocation(), Expression::KindOfBinaryOpExpression, op0->clone(), op1->clone(), rop)); lhs = lhs->clone(); lhs->clearContext(Expression::OprLValue); rep = ExpressionPtr (new AssignmentExpression(e->getLocation(), Expression::KindOfAssignmentExpression, lhs, rhs, false)); } } } if (rep) { ExpressionPtr c = canonicalizeRecur(rep); return c ? c : rep; } add(m_bucketMap[0], e); } else { getCanonical(e); } break; } case Expression::KindOfUnaryOpExpression: { UnaryOpExpressionPtr uop = spc(UnaryOpExpression, e); switch (uop->getOp()) { case T_INC: case T_DEC: if (uop->getContext() & Expression::DeadStore) { Construct::recomputeEffects(); ExpressionPtr val = uop->getExpression()->clone(); val->clearContext(Expression::LValue); val->clearContext(Expression::NoLValueWrapper); val->clearContext(Expression::OprLValue); if (uop->getFront()) { ExpressionPtr inc (new ScalarExpression(uop->getLocation(), Expression::KindOfScalarExpression, T_LNUMBER, string("1"))); val = ExpressionPtr (new BinaryOpExpression(uop->getLocation(), Expression::KindOfBinaryOpExpression, val, inc, uop->getOp() == T_INC ? '+' : '-')); } ExpressionPtr r = canonicalizeRecur(val); return r ? r : val; } add(m_bucketMap[0], e); break; default: getCanonical(e); break; } break; } default: getCanonical(e); break; } return ExpressionPtr(); }
int AliasManager::findInterf(ExpressionPtr rv, bool isLoad, ExpressionPtr &rep) { BucketMapEntry lvs = m_bucketMap[0]; rep = ExpressionPtr(); ExpressionPtrList::reverse_iterator it = lvs.rbegin(), end = lvs.rend(); int a; int depth = 0, min_depth = 0, max_depth = 0; while (it != end) { ExpressionPtr e = *it++; switch (e->getKindOf()) { case Expression::KindOfScalarExpression: { ScalarExpressionPtr se = spc(ScalarExpression, e); const std::string &s = se->getString(); if (s == "begin") { depth--; if (depth < min_depth) min_depth = depth; } else if (s == "end") { depth++; if (depth > max_depth) max_depth = depth; } else { assert(false); } } break; case Expression::KindOfObjectMethodExpression: case Expression::KindOfDynamicFunctionCall: case Expression::KindOfSimpleFunctionCall: case Expression::KindOfNewObjectExpression: return testAccesses(rv, e); case Expression::KindOfListAssignment: { ListAssignmentPtr la = spc(ListAssignment, e); ExpressionList &lhs = *la->getVariables().get(); for (int i = lhs.getCount(); i--; ) { ExpressionPtr ep = lhs[i]; if (ep && testAccesses(ep, rv) != DisjointAccess) { return InterfAccess; } } break; } case Expression::KindOfObjectPropertyExpression: case Expression::KindOfConstantExpression: case Expression::KindOfSimpleVariable: case Expression::KindOfDynamicVariable: case Expression::KindOfArrayElementExpression: case Expression::KindOfStaticMemberExpression: a = testAccesses(e, rv); if (a == DisjointAccess) { continue; } if (a == SameAccess) { if (isLoad) { // The value of an earlier load is available // if it dominates this one if (depth > min_depth) { a = InterfAccess; } } else { // The assignment definitely hits the load // if it post-dominates it. if (min_depth < 0) { a = InterfAccess; } } } if (a != SameAccess && isLoad && isReadOnlyAccess(e)) { continue; } rep = e; return a; case Expression::KindOfUnaryOpExpression: a = testAccesses(spc(UnaryOpExpression,e)->getExpression(), rv); goto handle_assign; case Expression::KindOfBinaryOpExpression: a = testAccesses(spc(BinaryOpExpression,e)->getExp1(), rv); goto handle_assign; case Expression::KindOfAssignmentExpression: a = testAccesses(spc(AssignmentExpression,e)->getVariable(), rv); goto handle_assign; handle_assign: if (a == DisjointAccess) { continue; } rep = e; if (a == SameAccess) { if (isLoad) { // we can propagate the value of an assignment // to a load, provided the assignment dominates // the load. if (depth > min_depth) { a = InterfAccess; } } else { // a later assignment kills an earlier one // provided the later one post-dominates the earlier if (min_depth < 0) { a = InterfAccess; } } } return a; default: break; } } return DisjointAccess; }
int AliasManager::testAccesses(ExpressionPtr e1, ExpressionPtr e2) { Expression::KindOf k1 = e1->getKindOf(), k2 = e2->getKindOf(); while (true) { switch (k1) { case Expression::KindOfConstantExpression: if (e1->canonCompare(e2)) return SameAccess; switch (k2) { case Expression::KindOfObjectMethodExpression: case Expression::KindOfDynamicFunctionCall: case Expression::KindOfSimpleFunctionCall: case Expression::KindOfNewObjectExpression: return InterfAccess; default: return DisjointAccess; } break; case Expression::KindOfArrayElementExpression: if (k2 == Expression::KindOfSimpleVariable || k2 == Expression::KindOfDynamicVariable || k2 == Expression::KindOfConstantExpression) { break; } return e1->canonCompare(e2) ? SameAccess : InterfAccess; case Expression::KindOfStaticMemberExpression: if (k2 == Expression::KindOfSimpleVariable || k2 == Expression::KindOfConstantExpression) { break; } return e1->canonCompare(e2) ? SameAccess : InterfAccess; case Expression::KindOfObjectPropertyExpression: if (k2 == Expression::KindOfSimpleVariable || k2 == Expression::KindOfConstantExpression) { break; } return InterfAccess; case Expression::KindOfDynamicVariable: if (k2 == Expression::KindOfSimpleVariable || k2 == Expression::KindOfConstantExpression) { break; } return e1->canonCompare(e2) ? SameAccess : InterfAccess; case Expression::KindOfSimpleVariable: { if (k2 == Expression::KindOfConstantExpression) { return DisjointAccess; } SimpleVariablePtr sv1 = spc(SimpleVariable, e1); AliasInfo &ai1 = m_aliasInfo[sv1->getName()]; switch (k2) { case Expression::KindOfSimpleVariable: { SimpleVariablePtr sv2 = spc(SimpleVariable, e2); if (sv1->getName() == sv2->getName()) { return SameAccess; } AliasInfo &ai2 = m_aliasInfo[sv2->getName()]; if (ai1.getIsRefTo() || ai1.getIsGlobal()) { return m_wildRefs || ai2.getIsGlobal() || ai2.checkRefLevel(0) ? InterfAccess : DisjointAccess; } if (ai2.getIsRefTo() || ai2.getIsGlobal()) { return m_wildRefs || ai1.getIsGlobal() || ai1.checkRefLevel(0) ? InterfAccess : DisjointAccess; } } return DisjointAccess; case Expression::KindOfDynamicVariable: return InterfAccess; case Expression::KindOfArrayElementExpression: if (ai1.getIsRefTo() || ai1.getIsGlobal() || m_wildRefs || ai1.checkRefLevel(0)) { return InterfAccess; } else { // $a = "foo"; $a[0] = "x"; ExpressionPtr var = spc(ArrayElementExpression, e2)->getVariable(); if (e1->canonCompare(var)) { return InterfAccess; } } return DisjointAccess; case Expression::KindOfStaticMemberExpression: case Expression::KindOfObjectPropertyExpression: default: if (ai1.getIsRefTo() || ai1.getIsGlobal() || m_wildRefs) { return InterfAccess; } return DisjointAccess; } // mustnt get here (we would loop forever). ASSERT(false); } default: return InterfAccess; } ExpressionPtr t = e1; e1 = e2; e2 = t; k1 = k2; k2 = e2->getKindOf(); } }