static ExpressionPtr makeIsNull(AnalysisResultConstPtr ar, LocationPtr loc, ExpressionPtr exp, bool invert) { /* Replace "$x === null" with an is_null call; this requires slightly * less work at runtime. */ ExpressionListPtr expList = ExpressionListPtr(new ExpressionList(exp->getScope(), loc)); expList->insertElement(exp); SimpleFunctionCallPtr call (new SimpleFunctionCall(exp->getScope(), loc, "is_null", false, expList, ExpressionPtr())); call->setValid(); call->setActualType(Type::Boolean); call->setupScopes(ar); ExpressionPtr result(call); if (invert) { result = ExpressionPtr(new UnaryOpExpression( exp->getScope(), loc, result, '!', true)); } return result; }
ExpressionPtr Expression::replaceValue(ExpressionPtr rep) { if (hasContext(Expression::RefValue) && isRefable(true) && !rep->isRefable(true)) { /* An assignment isRefable, but the rhs may not be. Need this to prevent "bad pass by reference" errors. */ ExpressionListPtr el(new ExpressionList(getScope(), getRange(), ExpressionList::ListKindWrapped)); el->addElement(rep); rep->clearContext(AssignmentRHS); rep = el; } if (rep->is(KindOfSimpleVariable) && !is(KindOfSimpleVariable)) { static_pointer_cast<SimpleVariable>(rep)->setAlwaysStash(); } rep->copyContext(m_context & ~(DeadStore|AccessContext)); if (TypePtr t1 = getType()) { if (TypePtr t2 = rep->getType()) { if (!Type::SameType(t1, t2)) { rep->setExpectedType(t1); } } } if (rep->getScope() != getScope()) { rep->resetScope(getScope()); } return rep; }
ExpressionPtr Expression::replaceValue(ExpressionPtr rep, bool noWarn) { if (hasContext(Expression::RefValue) && isRefable(true) && !rep->isRefable(true)) { /* An assignment isRefable, but the rhs may not be. Need this to prevent "bad pass by reference" errors. */ auto el = std::make_shared<ExpressionList>( getScope(), getRange(), noWarn ? ExpressionList::ListKindWrappedNoWarn : ExpressionList::ListKindWrapped); el->addElement(rep); rep->clearContext(AssignmentRHS); rep = el; } if (rep->is(KindOfSimpleVariable) && !is(KindOfSimpleVariable)) { static_pointer_cast<SimpleVariable>(rep)->setAlwaysStash(); } rep->copyContext(m_context & ~(DeadStore|AccessContext)); if (rep->getScope() != getScope()) { rep->resetScope(getScope()); } return rep; }
void RefDict::visit(ExpressionPtr e) { if (!first_pass) { // only need to record expressions once return; } if (e->getScope()->inPseudoMain()) { // bail out for psuedomain return; } if (!e->is(Expression::KindOfSimpleVariable)) { // only need to record simple variables return; } SimpleVariablePtr ptr(static_pointer_cast<SimpleVariable>(e)); if (ptr->isSuperGlobal() || ptr->isThis()) { // don't both recording for super globals or this return; } // Good to go if (m_am.insertForDict(e)) { record(e); } }
void StaticStatement::analyzeProgram(AnalysisResultPtr ar) { m_exp->analyzeProgram(ar); if (ar->getPhase() == AnalysisResult::AnalyzeAll) { BlockScopePtr scope = getScope(); for (int i = 0; i < m_exp->getCount(); i++) { ExpressionPtr exp = (*m_exp)[i]; ExpressionPtr variable; ExpressionPtr value; // turn static $a; into static $a = null; if (exp->is(Expression::KindOfSimpleVariable)) { variable = dynamic_pointer_cast<SimpleVariable>(exp); exp = AssignmentExpressionPtr (new AssignmentExpression(exp->getScope(), exp->getLocation(), variable, CONSTANT("null"), false)); (*m_exp)[i] = exp; } always_assert(exp->is(Expression::KindOfAssignmentExpression)); AssignmentExpressionPtr assignment_exp = dynamic_pointer_cast<AssignmentExpression>(exp); variable = assignment_exp->getVariable(); value = assignment_exp->getValue(); SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(variable); // set the Declaration context here instead of all over this file - this phase // is the first to run var->setContext(Expression::Declaration); Symbol *sym = var->getSymbol(); sym->setStaticInitVal(value); } } }
/** * Appends the given expression to end of the m_capturedExpressions list * and creates a new expression with the same scope and source location * that represents a reference to a query parameter. Query parameters do * not have a source code equivalent, but inform the query processor that * this expression represents the ith argument value, where i is zero based * and forms the last character of the special string @query_param_i. */ SimpleVariablePtr CaptureExtractor::newQueryParamRef(ExpressionPtr ae) { assert(ae != nullptr); char count = '0' + m_capturedExpressions.size(); std::string pname = "@query_param_"; pname.push_back(count); auto param = std::make_shared<SimpleVariable>(ae->getScope(), ae->getRange(), pname); m_capturedExpressions.push_back(ae); return param; }
static ExpressionPtr makeIsNull(AnalysisResultConstPtr ar, const Location::Range& r, ExpressionPtr exp, bool invert) { /* Replace "$x === null" with an is_null call; this requires slightly * less work at runtime. */ auto expList = std::make_shared<ExpressionList>(exp->getScope(), r); expList->insertElement(exp); auto call = std::make_shared<SimpleFunctionCall>( exp->getScope(), r, "is_null", false, expList, ExpressionPtr()); call->setValid(); call->setupScopes(ar); if (!invert) return call; return std::make_shared<UnaryOpExpression>( exp->getScope(), r, call, '!', true); }
bool StatementList::mergeConcatAssign() { if (Option::LocalCopyProp) { return false; } else { // check for vector string concat assignment such as // $space = " "; // $a .= "hello"; // $a .= $space; // $a .= "world!"; // turn into (for constant folding and concat sequence) // $a .= " " . "hello " . $space . "world!"; unsigned int i = 0; bool merged = false; do { std::string lhsName; int length = 0; for (; i < m_stmts.size(); i++) { StatementPtr stmt = m_stmts[i]; if (!stmt->is(Statement::KindOfExpStatement)) break; ExpStatementPtr expStmt = dynamic_pointer_cast<ExpStatement>(stmt); ExpressionPtr exp = expStmt->getExpression(); // check the first assignment if (exp->is(Expression::KindOfAssignmentExpression)) { AssignmentExpressionPtr assignment_exp = dynamic_pointer_cast<AssignmentExpression>(exp); ExpressionPtr variable = assignment_exp->getVariable(); ExpressionPtr value = assignment_exp->getValue(); std::string variableName = variable->getText(); if (variableName.find("->") != std::string::npos) break; if (value->hasEffect()) break; // cannot turn $a .= $b; a .= $a into $a .= $b . $a; if (value->getText().find(variableName) != std::string::npos) break; if (lhsName.empty()) { lhsName = variable->getText(); length++; continue; } else { break; } } else if (!exp->is(Expression::KindOfBinaryOpExpression)) { break; } BinaryOpExpressionPtr binaryOpExp = dynamic_pointer_cast<BinaryOpExpression>(exp); if (binaryOpExp->getOp() != T_CONCAT_EQUAL) break; ExpressionPtr exp1 = binaryOpExp->getExp1(); std::string exp1Text = exp1->getText(); if (exp1Text.find("->") != std::string::npos) break; ExpressionPtr exp2 = binaryOpExp->getExp2(); if (exp2->hasEffect()) break; if (exp2->getText().find(exp1Text) != std::string::npos) break; if (lhsName.empty()) { lhsName = exp1Text; length++; } else if (lhsName == exp1Text) { length++; } else { break; } } if (length > 1) { // replace m_stmts[j] to m_stmts[i - 1] with a new statement unsigned j = i - length; ExpStatementPtr expStmt; ExpressionPtr exp; BinaryOpExpressionPtr binaryOpExp; ExpressionPtr var; ExpressionPtr exp1; ExpressionPtr exp2; bool isAssignment = false; expStmt = dynamic_pointer_cast<ExpStatement>(m_stmts[j++]); exp = expStmt->getExpression(); if (exp->is(Expression::KindOfAssignmentExpression)) { isAssignment = true; AssignmentExpressionPtr assignment_exp = dynamic_pointer_cast<AssignmentExpression>(exp); var = assignment_exp->getVariable(); exp1 = assignment_exp->getValue(); } else { binaryOpExp = dynamic_pointer_cast<BinaryOpExpression>(exp); var = binaryOpExp->getExp1(); exp1 = binaryOpExp->getExp2(); } for (; j < i; j++) { expStmt = dynamic_pointer_cast<ExpStatement>(m_stmts[j]); exp = expStmt->getExpression(); binaryOpExp = dynamic_pointer_cast<BinaryOpExpression>(exp); exp2 = binaryOpExp->getExp2(); exp1 = BinaryOpExpressionPtr (new BinaryOpExpression(getScope(), getLocation(), Expression::KindOfBinaryOpExpression, exp1, exp2, '.')); } if (isAssignment) { exp = AssignmentExpressionPtr (new AssignmentExpression(exp->getScope(), exp->getLocation(), Expression::KindOfAssignmentExpression, var, exp1, false)); } else { exp = BinaryOpExpressionPtr (new BinaryOpExpression(getScope(), getLocation(), Expression::KindOfBinaryOpExpression, var, exp1, T_CONCAT_EQUAL)); } expStmt = ExpStatementPtr (new ExpStatement(getScope(), getLocation(), Statement::KindOfExpStatement, exp)); m_stmts[i - length] = expStmt; for (j = i - (length - 1); i > j; i--) removeElement(j); merged = true; } else if (length == 0) { i++; } } while (i < m_stmts.size()); return merged; } }
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; }
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); } } } } }