ExpressionPtr SimpleFunctionCall::preOptimize(AnalysisResultPtr ar) { if (m_class) ar->preOptimize(m_class); ar->preOptimize(m_nameExp); ar->preOptimize(m_params); if (ar->getPhase() != AnalysisResult::SecondPreOptimize) { return ExpressionPtr(); } // optimize away various "exists" functions, this may trigger // dead code elimination and improve type-inference. if (!m_class && m_className.empty() && (m_type == DefinedFunction || m_type == FunctionExistsFunction || m_type == ClassExistsFunction || m_type == InterfaceExistsFunction) && m_params && m_params->getCount() == 1) { ExpressionPtr value = (*m_params)[0]; if (value->isScalar()) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>(value); if (name && name->isLiteralString()) { string symbol = name->getLiteralString(); switch (m_type) { case DefinedFunction: { ConstantTablePtr constants = ar->getConstants(); // system constant if (constants->isPresent(symbol)) { return CONSTANT("true"); } // user constant BlockScopePtr block = ar->findConstantDeclarer(symbol); // not found (i.e., undefined) if (!block) { if (symbol.find("::") == std::string::npos) { return CONSTANT("false"); } else { // e.g., defined("self::ZERO") return ExpressionPtr(); } } constants = block->getConstants(); // already set to be dynamic if (constants->isDynamic(symbol)) return ExpressionPtr(); ConstructPtr decl = constants->getValue(symbol); ExpressionPtr constValue = dynamic_pointer_cast<Expression>(decl); if (constValue->isScalar()) { return CONSTANT("true"); } else { return ExpressionPtr(); } break; } case FunctionExistsFunction: { const std::string &lname = Util::toLower(symbol); if (Option::DynamicInvokeFunctions.find(lname) == Option::DynamicInvokeFunctions.end()) { FunctionScopePtr func = ar->findFunction(lname); if (!func) { return CONSTANT("false"); } else if (!func->isVolatile()) { return CONSTANT("true"); } } break; } case InterfaceExistsFunction: { ClassScopePtr cls = ar->findClass(Util::toLower(symbol)); if (!cls || !cls->isInterface()) { return CONSTANT("false"); } else if (!cls->isVolatile()) { return CONSTANT("true"); } break; } case ClassExistsFunction: { ClassScopePtr cls = ar->findClass(Util::toLower(symbol)); if (!cls || cls->isInterface()) { return CONSTANT("false"); } else if (!cls->isVolatile()) { return CONSTANT("true"); } break; } default: ASSERT(false); } } } } return ExpressionPtr(); }
void Construct::dumpNode(int spc, AnalysisResultConstPtr ar) { int nkid = getKidCount(); const char *name = 0; int type = 0; std::string scontext = ""; std::string value = ""; std::string type_info = ""; unsigned id = 0; ExpressionPtr idPtr = ExpressionPtr(); int ef = 0; if (Statement *s = dynamic_cast<Statement*>(this)) { Statement::KindOf stype = s->getKindOf(); name = Statement::Names[stype]; value = s->getName(); type = (int)stype; } else if (Expression *e = dynamic_cast<Expression*>(this)) { id = e->getCanonID(); idPtr = e->getCanonLVal(); ef = e->getLocalEffects(); Expression::KindOf etype = e->getKindOf(); name = Expression::Names[etype]; switch (etype) { case Expression::KindOfSimpleFunctionCall: value = static_cast<SimpleFunctionCall*>(e)->getName(); break; case Expression::KindOfSimpleVariable: value = static_cast<SimpleVariable*>(e)->getName(); break; case Expression::KindOfConstantExpression: value = e->getText(); break; case Expression::KindOfScalarExpression: value = e->getText(); break; default: break; } int c = e->getContext(); if ((c & Expression::Declaration) == Expression::Declaration) { scontext += "|Declaration"; } else if (c & Expression::LValue) { scontext += "|LValue"; } if (c & Expression::NoLValueWrapper) { scontext += "|NoLValueWrapper"; } if (c & Expression::RefValue) { scontext += "|RefValue"; } if (c & Expression::RefParameter) { scontext += "|RefParameter"; } if (c & Expression::DeepReference) { scontext += "|DeepReference"; } if (c & Expression::NoRefWrapper) { scontext += "|NoRefWrapper"; } if (c & Expression::ObjectContext) { scontext += "|ObjectContext"; } if (c & Expression::InParameterExpression) { scontext += "|InParameterExpression"; } if (c & Expression::ExistContext) { scontext += "|ExistContext"; } if (c & Expression::UnsetContext) { scontext += "|UnsetContext"; } if (c & Expression::AssignmentLHS) { scontext += "|AssignmentLHS"; } if (c & Expression::RefAssignmentLHS) { scontext += "|RefAssignmentLHS"; } if (c & Expression::DeepAssignmentLHS) { scontext += "|DeepAssignmentLHS"; } if (c & Expression::InvokeArgument) { scontext += "|InvokeArgument"; } if (c & Expression::OprLValue) { scontext += "|OprLValue"; } if (c & Expression::DeepOprLValue) { scontext += "|DeepOprLValue"; } if (c & Expression::AccessContext) { scontext += "|AccessContext"; } if (scontext != "") { scontext = " (" + scontext.substr(1) + ")"; } type = (int)etype; if (e->getActualType()) { type_info = e->getActualType()->toString(); if (e->getExpectedType()) { type_info += ":" + e->getExpectedType()->toString(); } if (e->getImplementedType()) { type_info += ";" + e->getImplementedType()->toString(); } type_info = "{" + type_info + "} "; } } else { ASSERT(FALSE); } int s = spc; while (s > 0) { int n = s > 10 ? 10 : s; std::cout << (" "+10-n); s -= n; } std::cout << "-> 0x" << hex << setfill('0') << setw(10) << (int64)this << dec; std::cout << " " << name << "(" << type << ") "; if (id) { std::cout << "id=" << id << " "; } if (idPtr) { std::cout << "idp=0x" << hex << setfill('0') << setw(10) << (int64)idPtr.get() << " "; } if (value != "") { std::cout << "[" << value << "] "; } string sef; if ((ef & UnknownEffect) == UnknownEffect) { sef = "|UnknownEffect"; } else { if (ef & IOEffect) sef += "|IOEffect"; if (ef & AssignEffect) sef += "|AssignEffect"; if (ef & GlobalEffect) sef += "|GlobalEffect"; if (ef & LocalEffect) sef += "|LocalEffect"; if (ef & ParamEffect) sef += "|ParamEffect"; if (ef & DeepParamEffect) sef += "|DeepParamEffect"; if (ef & DynamicParamEffect) sef += "|DynamicParamEffect"; if (ef & CanThrow) sef += "|CanThrow"; if (ef & AccessorEffect) sef += "|AccessorEffect"; if (ef & CreateEffect) sef += "|CreateEffect"; if (ef & DiagnosticEffect) sef += "|DiagnosticEffect"; if (ef & OtherEffect) sef += "|OtherEffect"; } if (sef != "") { sef = " (" + sef.substr(1) + ")"; } std::cout << type_info << nkid << scontext << sef; if (m_loc) { std::cout << " " << m_loc->file << ":" << m_loc->line1 << "@" << m_loc->char1; } std::cout << "\n"; }
void ExprDict::updateAccess(ExpressionPtr e) { int cls = e->getExprClass(); int eid = e->getCanonID(); e->clearAnticipated(); e->clearAvailable(); // bail on non-canonical expressions if (!isCanonicalStructure(eid)) { // but record we saw a type assertion belonging to this block m_avlTypeAsserts.push_back(eid); return; } if (m_anticipated && (cls & Expression::Update ? !BitOps::get_bit(eid, m_altered) : !e->getLocalEffects())) { /* Anticipated can be computed bottom up as we go. But note that we only know altered for Load/Store expressions. */ int i = e->getKidCount(); while (true) { if (!i--) { e->setAnticipated(); if (!e->hasContext(Expression::AssignmentLHS)) { setStructureOps(eid, m_anticipated, true); } break; } if (ExpressionPtr k = e->getNthExpr(i)) { if (!isCanonicalStructure(k->getCanonID())) continue; if (!k->isAnticipated()) { break; } } } } if (m_available) { /* Available has to be computed optimistically, because we dont yet know what is going to be altered between here and the end of the block So keep a list of the potentially-available accesses (avlAccess), and for each id, the last potentially-available expression (avlExpr). For each modifying expression that we process, we remove expressions from avlAccess, and at the end, we build up the available expressions bottom up. */ if ((cls & (Expression::Store|Expression::Call)) || (cls & Expression::Load && e->getContext() & (Expression::LValue| Expression::RefValue| Expression::UnsetContext| Expression::DeepReference))) { bool isLoad; int depth = 0, effects = 0; for (int i = 0, n = m_avlAccess.size(); i < n; ) { ExpressionRawPtr a = m_avlAccess[i]; if (m_am.checkAnyInterf(e, a, isLoad, depth, effects) != AliasManager::DisjointAccess) { int aid = a->getCanonID(); assert(isCanonicalStructure(aid)); if (eid != aid || cls == Expression::Load) { BitOps::set_bit(aid, m_altered, true); } if (!(cls & Expression::Store) || a != e->getStoreVariable()) { a->clearAvailable(); m_avlAccess[i] = m_avlAccess[--n]; m_avlAccess.resize(n); continue; } } i++; } } if (cls & Expression::Update || !e->getContainedEffects()) { int i = e->getKidCount(); while (true) { if (!i--) { e->setAvailable(); if (cls & Expression::Update) { m_avlAccess.push_back(e); } m_avlExpr[eid] = e; break; } if (ExpressionPtr k = e->getNthExpr(i)) { if (!isCanonicalStructure(k->getCanonID())) continue; if (!k->isAvailable()) { break; } } } } } if ((cls & (Expression::Store|Expression::Call)) || (cls & Expression::Load && e->getContext() & (Expression::LValue| Expression::RefValue| Expression::UnsetContext| Expression::DeepReference))) { ExpressionPtr cur = m_active, prev; bool isLoad; int depth = 0, effects = 0; while (cur) { ExpressionPtr next = cur->getCanonLVal(); int cid = cur->getCanonID(); assert(isCanonicalStructure(cid)); if ((cid != eid || cls == Expression::Load) && (BitOps::get_bit(cid, m_altered) || m_am.checkAnyInterf(e, cur, isLoad, depth, effects) != AliasManager::DisjointAccess)) { BitOps::set_bit(cid, m_altered, true); if (!prev) { m_active = next; } else { prev->setCanonPtr(next); } } else { prev = cur; } cur = next; } } }
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; if (v.isString()) { if (!o.isInteger() || o.toInt64Val() < 0 || o.toInt64Val() >= v.toCStrRef().size()) { // warnings should be raised... 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(); }
// foldConst() is callable from the parse phase as well as the analysis phase. // We take advantage of this during the parse phase to reduce very simple // expressions down to a single scalar and keep the parse tree smaller, // especially in cases of long chains of binary operators. However, we limit // the effectivness of this during parse to ensure that we eliminate only // very simple scalars that don't require analysis in later phases. For now, // that's just simply scalar values. ExpressionPtr BinaryOpExpression::foldConst(AnalysisResultConstPtr ar) { ExpressionPtr optExp; Variant v1; Variant v2; if (!m_exp2->getScalarValue(v2)) { if ((ar->getPhase() != AnalysisResult::ParseAllFiles) && m_exp1->isScalar() && m_exp1->getScalarValue(v1)) { switch (m_op) { case T_IS_IDENTICAL: case T_IS_NOT_IDENTICAL: if (v1.isNull()) { return makeIsNull(ar, getLocation(), m_exp2, m_op == T_IS_NOT_IDENTICAL); } break; case T_LOGICAL_AND: case T_BOOLEAN_AND: case T_LOGICAL_OR: case T_BOOLEAN_OR: { ExpressionPtr rep = v1.toBoolean() == (m_op == T_LOGICAL_AND || m_op == T_BOOLEAN_AND) ? m_exp2 : m_exp1; rep = ExpressionPtr( new UnaryOpExpression( getScope(), getLocation(), rep, T_BOOL_CAST, true)); rep->setActualType(Type::Boolean); return replaceValue(rep); } case '+': case '.': case '*': case '&': case '|': case '^': if (m_exp2->is(KindOfBinaryOpExpression)) { BinaryOpExpressionPtr binOpExp = dynamic_pointer_cast<BinaryOpExpression>(m_exp2); if (binOpExp->m_op == m_op && binOpExp->m_exp1->isScalar()) { ExpressionPtr aExp = m_exp1; ExpressionPtr bExp = binOpExp->m_exp1; ExpressionPtr cExp = binOpExp->m_exp2; m_exp1 = binOpExp = Clone(binOpExp); m_exp2 = cExp; binOpExp->m_exp1 = aExp; binOpExp->m_exp2 = bExp; if (ExpressionPtr optExp = binOpExp->foldConst(ar)) { m_exp1 = optExp; } return static_pointer_cast<Expression>(shared_from_this()); } } break; default: break; } } return ExpressionPtr(); } if (m_exp1->isScalar()) { if (!m_exp1->getScalarValue(v1)) return ExpressionPtr(); try { ScalarExpressionPtr scalar1 = dynamic_pointer_cast<ScalarExpression>(m_exp1); ScalarExpressionPtr scalar2 = dynamic_pointer_cast<ScalarExpression>(m_exp2); // Some data, like the values of __CLASS__ and friends, are not available // while we're still in the initial parse phase. if (ar->getPhase() == AnalysisResult::ParseAllFiles) { if ((scalar1 && scalar1->needsTranslation()) || (scalar2 && scalar2->needsTranslation())) { return ExpressionPtr(); } } if (!Option::WholeProgram || !Option::ParseTimeOpts) { // In the VM, don't optimize __CLASS__ if within a trait, since // __CLASS__ is not resolved yet. ClassScopeRawPtr clsScope = getOriginalClass(); if (clsScope && clsScope->isTrait()) { if ((scalar1 && scalar1->getType() == T_CLASS_C) || (scalar2 && scalar2->getType() == T_CLASS_C)) { return ExpressionPtr(); } } } Variant result; switch (m_op) { case T_LOGICAL_XOR: result = static_cast<bool>(v1.toBoolean() ^ v2.toBoolean()); break; case '|': *result.asCell() = cellBitOr(*v1.asCell(), *v2.asCell()); break; case '&': *result.asCell() = cellBitAnd(*v1.asCell(), *v2.asCell()); break; case '^': *result.asCell() = cellBitXor(*v1.asCell(), *v2.asCell()); break; case '.': result = concat(v1.toString(), v2.toString()); break; case T_IS_IDENTICAL: result = same(v1, v2); break; case T_IS_NOT_IDENTICAL: result = !same(v1, v2); break; case T_IS_EQUAL: result = equal(v1, v2); break; case T_IS_NOT_EQUAL: result = !equal(v1, v2); break; case '<': result = less(v1, v2); break; case T_IS_SMALLER_OR_EQUAL: result = cellLessOrEqual(*v1.asCell(), *v2.asCell()); break; case '>': result = more(v1, v2); break; case T_IS_GREATER_OR_EQUAL: result = cellGreaterOrEqual(*v1.asCell(), *v2.asCell()); break; case '+': *result.asCell() = cellAdd(*v1.asCell(), *v2.asCell()); break; case '-': *result.asCell() = cellSub(*v1.asCell(), *v2.asCell()); break; case '*': *result.asCell() = cellMul(*v1.asCell(), *v2.asCell()); break; case '/': if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) { return ExpressionPtr(); } *result.asCell() = cellDiv(*v1.asCell(), *v2.asCell()); break; case '%': if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) { return ExpressionPtr(); } *result.asCell() = cellMod(*v1.asCell(), *v2.asCell()); break; case T_SL: result = v1.toInt64() << v2.toInt64(); break; case T_SR: result = v1.toInt64() >> v2.toInt64(); break; case T_BOOLEAN_OR: result = v1.toBoolean() || v2.toBoolean(); break; case T_BOOLEAN_AND: result = v1.toBoolean() && v2.toBoolean(); break; case T_LOGICAL_OR: result = v1.toBoolean() || v2.toBoolean(); break; case T_LOGICAL_AND: result = v1.toBoolean() && v2.toBoolean(); break; case T_INSTANCEOF: { if (v2.isString()) { if (v1.isArray() && interface_supports_array(v2.getStringData())) { result = true; break; } if (v1.isString() && interface_supports_string(v2.getStringData())) { result = true; break; } if (v1.isInteger() && interface_supports_int(v2.getStringData())) { result = true; break; } if (v1.isDouble() && interface_supports_double(v2.getStringData())) { result = true; break; } } result = false; break; } default: return ExpressionPtr(); } return makeScalarExpression(ar, result); } catch (...) { } } else if (ar->getPhase() != AnalysisResult::ParseAllFiles) {
int FunctionScope::inferParamTypes(AnalysisResultPtr ar, ConstructPtr exp, ExpressionListPtr params, bool &valid) { if (!params) { if (m_minParam > 0) { if (exp->getScope()->isFirstPass()) { Compiler::Error(Compiler::TooFewArgument, exp, m_stmt); } valid = false; if (!Option::AllDynamic) setDynamic(); } return 0; } int ret = 0; if (params->getCount() < m_minParam) { if (exp->getScope()->isFirstPass()) { Compiler::Error(Compiler::TooFewArgument, exp, m_stmt); } valid = false; if (!Option::AllDynamic) setDynamic(); } if (params->getCount() > m_maxParam) { if (isVariableArgument()) { ret = params->getCount() - m_maxParam; } else { if (exp->getScope()->isFirstPass()) { Compiler::Error(Compiler::TooManyArgument, exp, m_stmt); } valid = false; if (!Option::AllDynamic) setDynamic(); } } bool canSetParamType = isUserFunction() && !m_overriding && !m_perfectVirtual; for (int i = 0; i < params->getCount(); i++) { ExpressionPtr param = (*params)[i]; if (i < m_maxParam && param->hasContext(Expression::RefParameter)) { /** * This should be very un-likely, since call time pass by ref is a * deprecated, not very widely used (at least in FB codebase) feature. */ TRY_LOCK_THIS(); Symbol *sym = getVariables()->addSymbol(m_paramNames[i]); sym->setLvalParam(); sym->setCallTimeRef(); } if (valid && param->hasContext(Expression::InvokeArgument)) { param->clearContext(Expression::InvokeArgument); param->clearContext(Expression::RefValue); param->clearContext(Expression::NoRefWrapper); } bool isRefVararg = (i >= m_maxParam && isReferenceVariableArgument()); if ((i < m_maxParam && isRefParam(i)) || isRefVararg) { param->setContext(Expression::LValue); param->setContext(Expression::RefValue); param->inferAndCheck(ar, Type::Variant, true); } else if (!(param->getContext() & Expression::RefParameter)) { param->clearContext(Expression::LValue); param->clearContext(Expression::RefValue); param->clearContext(Expression::InvokeArgument); param->clearContext(Expression::NoRefWrapper); } TypePtr expType; /** * Duplicate the logic of getParamType(i), w/o the mutation */ TypePtr paramType(i < m_maxParam && !isZendParamMode() ? m_paramTypes[i] : TypePtr()); if (!paramType) paramType = Type::Some; if (valid && !canSetParamType && i < m_maxParam && (!Option::HardTypeHints || !m_paramTypeSpecs[i])) { /** * What is this magic, you might ask? * * Here, we take advantage of implicit conversion from every type to * Variant. Essentially, we don't really care what type comes out of this * expression since it'll just get converted anyways. Doing it this way * allows us to generate less temporaries along the way. */ TypePtr optParamType(paramType->is(Type::KindOfVariant) ? Type::Some : paramType); expType = param->inferAndCheck(ar, optParamType, false); } else { expType = param->inferAndCheck(ar, Type::Some, false); } if (i < m_maxParam) { if (!Option::HardTypeHints || !m_paramTypeSpecs[i]) { if (canSetParamType) { if (!Type::SameType(paramType, expType) && !paramType->is(Type::KindOfVariant)) { TRY_LOCK_THIS(); paramType = setParamType(ar, i, expType); } else { // do nothing - how is this safe? well, if we ever observe // paramType == expType, then this means at some point in the past, // somebody called setParamType() with expType. thus, by calling // setParamType() again with expType, we contribute no "new" // information. this argument also still applies in the face of // concurrency } } // See note above. If we have an implemented type, however, we // should set the paramType to the implemented type to avoid an // un-necessary cast if (paramType->is(Type::KindOfVariant)) { TypePtr it(param->getImplementedType()); paramType = it ? it : expType; } if (valid) { if (!Type::IsLegalCast(ar, expType, paramType) && paramType->isNonConvertibleType()) { param->inferAndCheck(ar, paramType, true); } param->setExpectedType(paramType); } } } // we do a best-effort check for bad pass-by-reference and do not report // error for some vararg case (e.g., array_multisort can have either ref // or value for the same vararg). if (!isRefVararg || !isMixedVariableArgument()) { Expression::CheckPassByReference(ar, param); } } return ret; }
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); } } } } }
bool ExpressionList::preOutputCPP(CodeGenerator &cg, AnalysisResultPtr ar, int state) { if (m_kind == ListKindParam && !m_arrayElements) { return Expression::preOutputCPP(cg, ar, state|StashKidVars); } unsigned n = m_exps.size(); bool inExpression = cg.inExpression(); if (!inExpression && (state & FixOrder)) { return true; } cg.setInExpression(false); bool ret = false; if (m_arrayElements) { /* * would like to do: * ret = Expression::preOutputCPP(cg, ar, state); * but icc has problems with the generated code. */ ret = hasEffect(); } else if (n > 1 && m_kind == ListKindLeft) { ret = true; } else { for (unsigned int i = 0; i < n; i++) { if (m_exps[i]->preOutputCPP(cg, ar, 0)) { ret = true; break; } } } if (!inExpression) return ret; cg.setInExpression(true); if (!ret) { if (state & FixOrder) { preOutputStash(cg, ar, state); return true; } return false; } cg.wrapExpressionBegin(); if (m_arrayElements) { setCPPTemp(genCPPTemp(cg, ar)); outputCPPInternal(cg, ar, true, true); } else { unsigned ix = m_kind == ListKindLeft ? 0 : n - 1; for (unsigned int i = 0; i < n; i++) { ExpressionPtr e = m_exps[i]; e->preOutputCPP(cg, ar, 0); if (i != ix) { if (e->outputCPPUnneeded(cg, ar)) { cg_printf(";\n"); } e->setCPPTemp("/**/"); } else if (e->hasCPPTemp() && Type::SameType(e->getType(), getType())) { setCPPTemp(e->cppTemp()); } else if (!i && n > 1) { e->Expression::preOutputStash(cg, ar, state | FixOrder); if (!(state & FixOrder)) { cg_printf("id(%s);\n", e->cppTemp().c_str()); } setCPPTemp(e->cppTemp()); } } } return true; }
ExpressionPtr DepthFirstVisitor<Pre, OptVisitor>::visit(ExpressionPtr e) { return e->preOptimize(this->m_data.m_ar); }
TypePtr AssignmentExpression:: inferTypesImpl(AnalysisResultPtr ar, TypePtr type, bool coerce, ExpressionPtr variable, ExpressionPtr value /* = ExpressionPtr() */) { TypePtr ret = type; if (value) { if (coerce) { ret = value->inferAndCheck(ar, type, coerce); } else { ret = value->inferAndCheck(ar, NEW_TYPE(Some), coerce); } } BlockScopePtr scope = ar->getScope(); if (variable->is(Expression::KindOfConstantExpression)) { // ...as in ClassConstant statement ConstantExpressionPtr exp = dynamic_pointer_cast<ConstantExpression>(variable); bool p; scope->getConstants()->check(exp->getName(), ret, true, ar, variable, p); } else if (variable->is(Expression::KindOfDynamicVariable)) { // simptodo: not too sure about this ar->getFileScope()->setAttribute(FileScope::ContainsLDynamicVariable); } else if (variable->is(Expression::KindOfSimpleVariable)) { SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(variable); if (var->getName() == "this" && ar->getClassScope()) { if (ar->isFirstPass()) { ar->getCodeError()->record(variable, CodeError::ReassignThis, variable); } } if (ar->getPhase() == AnalysisResult::LastInference && value) { if (!value->getExpectedType()) { value->setExpectedType(variable->getActualType()); } } } // if the value may involve object, consider the variable as "referenced" // so that objects are not destructed prematurely. bool referenced = true; if (value && value->isScalar()) referenced = false; if (ret && ret->isNoObjectInvolved()) referenced = false; if (referenced && variable->is(Expression::KindOfSimpleVariable)) { SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(variable); const std::string &name = var->getName(); VariableTablePtr variables = ar->getScope()->getVariables(); variables->addReferenced(name); } TypePtr vt = variable->inferAndCheck(ar, ret, true); if (!coerce && type->is(Type::KindOfAny)) { ret = vt; } return ret; }
void test(ExpressionPtr expr) { std::cout << "Before DNF: " << expr->toString() << std::endl; std::cout << "After DNF : " << expr->getDNF()->toString() << std::endl << std::endl; }
void SimpleFunctionCall::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { bool linemap = outputLineMap(cg, ar, true); if (!m_lambda.empty()) { cg.printf("\"%s\"", m_lambda.c_str()); if (linemap) cg.printf(")"); return; } if (!m_class && m_className.empty()) { if (m_type == DefineFunction && m_params && m_params->getCount() >= 2) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>((*m_params)[0]); string varName; if (name) { varName = name->getIdentifier(); ExpressionPtr value = (*m_params)[1]; if (varName.empty()) { cg.printf("throw_fatal(\"bad define\")"); } else if (m_dynamicConstant) { cg.printf("g->declareConstant(\"%s\", g->%s%s, ", varName.c_str(), Option::ConstantPrefix, varName.c_str()); value->outputCPP(cg, ar); cg.printf(")"); } else { bool needAssignment = true; bool isSystem = ar->getConstants()->isSystem(varName); if (isSystem || ((!ar->isConstantRedeclared(varName)) && value->isScalar())) { needAssignment = false; } if (needAssignment) { cg.printf("%s%s = ", Option::ConstantPrefix, varName.c_str()); value->outputCPP(cg, ar); } } } else { cg.printf("throw_fatal(\"bad define\")"); } if (linemap) cg.printf(")"); return; } if (m_name == "func_num_args") { cg.printf("num_args"); if (linemap) cg.printf(")"); return; } switch (m_type) { case VariableArgumentFunction: { FunctionScopePtr func = dynamic_pointer_cast<FunctionScope>(ar->getScope()); if (func) { cg.printf("%s(", m_name.c_str()); func->outputCPPParamsCall(cg, ar, true); if (m_params) { cg.printf(","); m_params->outputCPP(cg, ar); } cg.printf(")"); if (linemap) cg.printf(")"); return; } } break; case FunctionExistsFunction: case ClassExistsFunction: case InterfaceExistsFunction: { bool literalString = false; string symbol; if (m_params && m_params->getCount() == 1) { ExpressionPtr value = (*m_params)[0]; if (value->isScalar()) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>(value); if (name && name->isLiteralString()) { literalString = true; symbol = name->getLiteralString(); } } } if (literalString) { switch (m_type) { case FunctionExistsFunction: { const std::string &lname = Util::toLower(symbol); bool dynInvoke = Option::DynamicInvokeFunctions.find(lname) != Option::DynamicInvokeFunctions.end(); if (!dynInvoke) { FunctionScopePtr func = ar->findFunction(lname); if (func) { if (!func->isDynamic()) { if (func->isRedeclaring()) { const char *name = func->getName().c_str(); cg.printf("(%s->%s%s != invoke_failed_%s)", cg.getGlobals(ar), Option::InvokePrefix, name, name); break; } cg.printf("true"); break; } } else { cg.printf("false"); break; } } cg.printf("f_function_exists(\"%s\")", lname.c_str()); } break; case ClassExistsFunction: { ClassScopePtr cls = ar->findClass(Util::toLower(symbol)); if (cls && !cls->isInterface()) { const char *name = cls->getName().c_str(); cg.printf("f_class_exists(\"%s\")", name); } else { cg.printf("false"); } } break; case InterfaceExistsFunction: { ClassScopePtr cls = ar->findClass(Util::toLower(symbol)); if (cls && cls->isInterface()) { const char *name = cls->getName().c_str(); cg.printf("f_interface_exists(\"%s\")", name); } else { cg.printf("false"); } } break; default: break; } if (linemap) cg.printf(")"); return; } } break; case GetDefinedVarsFunction: cg.printf("get_defined_vars(variables)"); if (linemap) cg.printf(")"); return; default: break; } } outputCPPParamOrderControlled(cg, ar); if (linemap) cg.printf(")"); }
void SimpleFunctionCall::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) { outputLineMap(cg, ar); if (m_class || !m_className.empty()) { if (m_class) { m_class->outputPHP(cg, ar); cg.printf("::%s(", m_name.c_str()); } else { cg.printf("%s::%s(", m_className.c_str(), m_name.c_str()); } } else { if (cg.getOutput() == CodeGenerator::InlinedPHP || cg.getOutput() == CodeGenerator::TrimmedPHP) { if (cg.getOutput() == CodeGenerator::TrimmedPHP && cg.usingStream(CodeGenerator::PrimaryStream) && Option::DynamicFunctionCalls.find(m_name) != Option::DynamicFunctionCalls.end()) { int funcNamePos = Option::DynamicFunctionCalls[m_name]; if (m_params && m_params->getCount() && m_params->getCount() >= funcNamePos + 1) { if (funcNamePos == -1) funcNamePos = m_params->getCount() - 1; ExpressionPtr funcName = (*m_params)[funcNamePos]; if (!funcName->is(Expression::KindOfScalarExpression)) { cg.printf("%s(", m_name.c_str()); for (int i = 0; i < m_params->getCount(); i++) { if (i > 0) cg.printf(", "); if (i == funcNamePos) { cg.printf("%sdynamic_load(", Option::IdPrefix.c_str()); funcName->outputPHP(cg, ar); cg.printf(")"); } else { ExpressionPtr param = (*m_params)[i]; if (param) param->outputPHP(cg, ar); } } cg.printf(")"); return; } } } /* simptodo: I dunno if (m_type == RenderTemplateFunction && !m_template.empty()) { cg.printf("%s_%s(", m_name.c_str(), Util::getIdentifier(m_template).c_str()); } else if (m_type == RenderTemplateIncludeFunction) { string templateName = ar->getProgram()->getCurrentTemplate(); cg.printf("%s_%s(", m_name.c_str(), Util::getIdentifier(templateName).c_str()); } else { */ cg.printf("%s(", m_name.c_str()); //} } else { cg.printf("%s(", m_name.c_str()); } } if (m_params) m_params->outputPHP(cg, ar); cg.printf(")"); }
TypePtr SimpleFunctionCall::inferAndCheck(AnalysisResultPtr ar, TypePtr type, bool coerce) { reset(); ConstructPtr self = shared_from_this(); // handling define("CONSTANT", ...); if (!m_class && m_className.empty()) { if (m_type == DefineFunction && m_params && m_params->getCount() >= 2) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>((*m_params)[0]); string varName; if (name) { varName = name->getIdentifier(); if (!varName.empty()) { ExpressionPtr value = (*m_params)[1]; TypePtr varType = value->inferAndCheck(ar, NEW_TYPE(Some), false); ar->getDependencyGraph()-> addParent(DependencyGraph::KindOfConstant, ar->getName(), varName, self); ConstantTablePtr constants = ar->findConstantDeclarer(varName)->getConstants(); if (constants != ar->getConstants()) { if (value && !value->isScalar()) { constants->setDynamic(ar, varName); varType = Type::Variant; } if (constants->isDynamic(varName)) { m_dynamicConstant = true; ar->getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); } else { constants->setType(ar, varName, varType, true); } // in case the old 'value' has been optimized constants->setValue(ar, varName, value); } return checkTypesImpl(ar, type, Type::Boolean, coerce); } } if (varName.empty() && ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::BadDefine, self); } } else if (m_type == ExtractFunction) { ar->getScope()->getVariables()->forceVariants(ar); } } FunctionScopePtr func; // avoid raising both MissingObjectContext and UnknownFunction bool errorFlagged = false; if (!m_class && m_className.empty()) { func = ar->findFunction(m_name); } else { ClassScopePtr cls = ar->resolveClass(m_className); if (cls && cls->isVolatile()) { ar->getScope()->getVariables() ->setAttribute(VariableTable::NeedGlobalPointer); } if (!cls || cls->isRedeclaring()) { if (cls) { m_redeclaredClass = true; } if (!cls && ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::UnknownClass, self); } if (m_params) { m_params->inferAndCheck(ar, NEW_TYPE(Some), false); } return checkTypesImpl(ar, type, Type::Variant, coerce); } m_derivedFromRedeclaring = cls->derivesFromRedeclaring(); m_validClass = true; if (m_name == "__construct") { // if the class is known, php will try to identify class-name ctor func = cls->findConstructor(ar, true); } else { func = cls->findFunction(ar, m_name, true, true); } if (func && !func->isStatic()) { ClassScopePtr clsThis = ar->getClassScope(); FunctionScopePtr funcThis = ar->getFunctionScope(); if (!clsThis || (clsThis->getName() != m_className && !clsThis->derivesFrom(ar, m_className)) || funcThis->isStatic()) { // set the method static to avoid "unknown method" runtime exception if (Option::StaticMethodAutoFix && !func->containsThis()) { func->setStaticMethodAutoFixed(); } if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::MissingObjectContext, self); errorFlagged = true; } func.reset(); } } } if (!func || func->isRedeclaring()) { if (func) { m_redeclared = true; ar->getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); } if (!func && !errorFlagged && ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::UnknownFunction, self); } if (m_params) { if (func) { FunctionScope::RefParamInfoPtr info = FunctionScope::GetRefParamInfo(m_name); ASSERT(info); for (int i = m_params->getCount(); i--; ) { if (info->isRefParam(i)) { m_params->markParam(i, canInvokeFewArgs()); } } } m_params->inferAndCheck(ar, NEW_TYPE(Some), false); } return checkTypesImpl(ar, type, Type::Variant, coerce); } m_builtinFunction = !func->isUserFunction(); if (m_redeclared) { if (m_params) { m_params->inferAndCheck(ar, NEW_TYPE(Some), false); } return checkTypesImpl(ar, type, type, coerce); } CHECK_HOOK(beforeSimpleFunctionCallCheck); m_valid = true; type = checkParamsAndReturn(ar, type, coerce, func); if (!m_valid && m_params) { m_params->markParams(false); } CHECK_HOOK(afterSimpleFunctionCallCheck); return type; }
void Type::Dump(ExpressionPtr exp) { Dump(exp->getExpectedType(), "Expected: %s\t"); Dump(exp->getActualType(), "Actual: %s\n"); }
void Construct::dumpNode(int spc, AnalysisResultPtr ar) { int nkid = getKidCount(); std::string name; int type = 0; std::string scontext = ""; std::string value = ""; std::string type_info = ""; unsigned id = 0; ExpressionPtr idPtr = ExpressionPtr(); int ef = 0; if (Statement *s = dynamic_cast<Statement*>(this)) { Statement::KindOf stype = s->getKindOf(); switch (stype) { case Statement::KindOfFunctionStatement: name="FunctionStatement"; break; case Statement::KindOfClassStatement: name="ClassStatement"; break; case Statement::KindOfInterfaceStatement: name="InterfaceStatement"; break; case Statement::KindOfClassVariable: name="ClassVariable"; break; case Statement::KindOfClassConstant: name="ClassConstant"; break; case Statement::KindOfMethodStatement: name="MethodStatement"; break; case Statement::KindOfStatementList: name="StatementList"; break; case Statement::KindOfBlockStatement: name="BlockStatement"; break; case Statement::KindOfIfBranchStatement: name="IfBranchStatement"; break; case Statement::KindOfIfStatement: name="IfStatement"; break; case Statement::KindOfWhileStatement: name="WhileStatement"; break; case Statement::KindOfDoStatement: name="DoStatement"; break; case Statement::KindOfForStatement: name="ForStatement"; break; case Statement::KindOfSwitchStatement: name="SwitchStatement"; break; case Statement::KindOfCaseStatement: name="CaseStatement"; break; case Statement::KindOfBreakStatement: name="BreakStatement"; break; case Statement::KindOfContinueStatement: name="ContinueStatement"; break; case Statement::KindOfReturnStatement: name="ReturnStatement"; break; case Statement::KindOfGlobalStatement: name="GlobalStatement"; break; case Statement::KindOfStaticStatement: name="StaticStatement"; break; case Statement::KindOfEchoStatement: name="EchoStatement"; break; case Statement::KindOfUnsetStatement: name="UnsetStatement"; break; case Statement::KindOfExpStatement: name="ExpStatement"; break; case Statement::KindOfForEachStatement: name="ForEachStatement"; break; case Statement::KindOfCatchStatement: name="CatchStatement"; break; case Statement::KindOfTryStatement: name="TryStatement"; break; case Statement::KindOfThrowStatement: name="ThrowStatement"; break; } type = (int)stype; } else if (Expression *e = dynamic_cast<Expression*>(this)) { id = e->getCanonID(); idPtr = e->getCanonLVal(); ef = e->getLocalEffects(); Expression::KindOf etype = e->getKindOf(); switch (etype) { case Expression::KindOfSimpleFunctionCall: name="SimpleFunctionCall"; value = static_cast<SimpleFunctionCall*>(e)->getName(); break; case Expression::KindOfSimpleVariable: name="SimpleVariable"; value = static_cast<SimpleVariable*>(e)->getName(); break; case Expression::KindOfConstantExpression: name="ConstantExpression"; value = e->getText(); break; case Expression::KindOfScalarExpression: name="ScalarExpression"; value = e->getText(); break; case Expression::KindOfExpressionList: name="ExpressionList"; break; case Expression::KindOfAssignmentExpression: name="AssignmentExpression"; break; case Expression::KindOfDynamicVariable: name="DynamicVariable"; break; case Expression::KindOfStaticMemberExpression: name="StaticMemberExpression"; break; case Expression::KindOfArrayElementExpression: name="ArrayElementExpression"; break; case Expression::KindOfDynamicFunctionCall: name="DynamicFunctionCall"; break; case Expression::KindOfObjectPropertyExpression: name="ObjectPropertyExpression"; break; case Expression::KindOfObjectMethodExpression: name="ObjectMethodExpression"; break; case Expression::KindOfListAssignment: name="ListAssignment"; break; case Expression::KindOfNewObjectExpression: name="NewObjectExpression"; break; case Expression::KindOfUnaryOpExpression: name="UnaryOpExpression"; break; case Expression::KindOfIncludeExpression: name="IncludeExpression"; break; case Expression::KindOfBinaryOpExpression: name="BinaryOpExpression"; break; case Expression::KindOfQOpExpression: name="QOpExpression"; break; case Expression::KindOfArrayPairExpression: name="ArrayPairExpression"; break; case Expression::KindOfClassConstantExpression: name="ClassConstantExpression"; break; case Expression::KindOfParameterExpression: name="ParameterExpression"; break; case Expression::KindOfModifierExpression: name="ModifierExpression"; break; case Expression::KindOfEncapsListExpression: name="EncapsListExpression"; break; } int c = e->getContext(); if ((c & Expression::Declaration) == Expression::Declaration) { scontext += "|Declaration"; } else if (c & Expression::LValue) { scontext += "|LValue"; } if (c & Expression::NoLValueWrapper) { scontext += "|NoLValueWrapper"; } if (c & Expression::RefValue) { scontext += "|RefValue"; } if (c & Expression::RefParameter) { scontext += "|RefParameter"; } if (c & Expression::DeepReference) { scontext += "|DeepReference"; } if (c & Expression::NoRefWrapper) { scontext += "|NoRefWrapper"; } if (c & Expression::ObjectContext) { scontext += "|ObjectContext"; } if (c & Expression::InParameterExpression) { scontext += "|InParameterExpression"; } if (c & Expression::ExistContext) { scontext += "|ExistContext"; } if (c & Expression::UnsetContext) { scontext += "|UnsetContext"; } if (c & Expression::AssignmentLHS) { scontext += "|AssignmentLHS"; } if (c & Expression::DeepAssignmentLHS) { scontext += "|DeepAssignmentLHS"; } if (c & Expression::InvokeArgument) { scontext += "|InvokeArgument"; } if (c & Expression::OprLValue) { scontext += "|OprLValue"; } if (c & Expression::DeepOprLValue) { scontext += "|DeepOprLValue"; } if (c & Expression::AccessContext) { scontext += "|AccessContext"; } if (scontext != "") { scontext = " (" + scontext.substr(1) + ")"; } type = (int)etype; if (e->getActualType()) { type_info = e->getActualType()->toString(); if (e->getExpectedType()) { type_info += ":" + e->getExpectedType()->toString(); } if (e->getImplementedType()) { type_info += ";" + e->getImplementedType()->toString(); } type_info = "{" + type_info + "} "; } } else { ASSERT(FALSE); } int s = spc; while (s > 0) { int n = s > 10 ? 10 : s; std::cout << (" "+10-n); s -= n; } std::cout << "-> 0x" << hex << setfill('0') << setw(10) << (int64)this << dec; std::cout << " " << name << "(" << type << ") "; if (id) { std::cout << "id=" << id << " "; } if (idPtr) { std::cout << "idp=0x" << hex << setfill('0') << setw(10) << (int64)idPtr.get() << " "; } if (value != "") { std::cout << "[" << value << "] "; } string sef; if ((ef & UnknownEffect) == UnknownEffect) { sef = "|UnknownEffect"; } else { if (ef & IOEffect) sef += "|IOEffect"; if (ef & AssignEffect) sef += "|AssignEffect"; if (ef & GlobalEffect) sef += "|GlobalEffect"; if (ef & LocalEffect) sef += "|LocalEffect"; if (ef & ParamEffect) sef += "|ParamEffect"; if (ef & DeepParamEffect) sef += "|DeepParamEffect"; if (ef & DynamicParamEffect) sef += "|DynamicParamEffect"; if (ef & CanThrow) sef += "|CanThrow"; if (ef & AccessorEffect) sef += "|AccessorEffect"; if (ef & CreateEffect) sef += "|CreateEffect"; if (ef & DiagnosticEffect) sef += "|DiagnosticEffect"; if (ef & OtherEffect) sef += "|OtherEffect"; } if (sef != "") { sef = " (" + sef.substr(1) + ")"; } std::cout << type_info << nkid << scontext << sef; if (m_loc) { std::cout << " " << m_loc->file << ":" << m_loc->line1 << "@" << m_loc->char1; } std::cout << "\n"; }
void FunctionScope::init(AnalysisResultConstPtr ar) { m_dynamicInvoke = false; bool canInline = true; if (m_pseudoMain) { canInline = false; m_variables->forceVariants(ar, VariableTable::AnyVars); setReturnType(ar, Type::Variant); } if (m_refReturn) { m_returnType = Type::Variant; } if (!strcasecmp(m_name.c_str(), "__autoload")) { setVolatile(); } // FileScope's flags are from parser, but VariableTable has more flags // coming from type inference phase. So we are tranferring these flags // just for better modularization between FileScope and VariableTable. if (m_attribute & FileScope::ContainsDynamicVariable) { m_variables->setAttribute(VariableTable::ContainsDynamicVariable); } if (m_attribute & FileScope::ContainsLDynamicVariable) { m_variables->setAttribute(VariableTable::ContainsLDynamicVariable); } if (m_attribute & FileScope::ContainsExtract) { m_variables->setAttribute(VariableTable::ContainsExtract); } if (m_attribute & FileScope::ContainsCompact) { m_variables->setAttribute(VariableTable::ContainsCompact); } if (m_attribute & FileScope::ContainsUnset) { m_variables->setAttribute(VariableTable::ContainsUnset); } if (m_attribute & FileScope::ContainsGetDefinedVars) { m_variables->setAttribute(VariableTable::ContainsGetDefinedVars); } if (m_stmt && Option::AllVolatile && !m_pseudoMain && !m_method) { m_volatile = true; } m_dynamic = Option::IsDynamicFunction(m_method, m_name) || Option::EnableEval == Option::FullEval || Option::AllDynamic; if (!m_method && Option::DynamicInvokeFunctions.find(m_name) != Option::DynamicInvokeFunctions.end()) { setDynamicInvoke(); } if (m_modifiers) { m_virtual = m_modifiers->isAbstract(); } if (m_stmt) { MethodStatementPtr stmt = dynamic_pointer_cast<MethodStatement>(m_stmt); StatementListPtr stmts = stmt->getStmts(); if (stmts) { if (stmts->getRecursiveCount() > Option::InlineFunctionThreshold) canInline = false; for (int i = 0; i < stmts->getCount(); i++) { StatementPtr stmt = (*stmts)[i]; stmt->setFileLevel(); if (stmt->is(Statement::KindOfExpStatement)) { ExpStatementPtr expStmt = dynamic_pointer_cast<ExpStatement>(stmt); ExpressionPtr exp = expStmt->getExpression(); exp->setTopLevel(); } } } } else { canInline = false; } m_inlineable = canInline; }
/* 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::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); }
StatementPtr IfStatement::preOptimize(AnalysisResultConstPtr ar) { if (ar->getPhase() < AnalysisResult::FirstPreOptimize) { return StatementPtr(); } // we cannot optimize away the code inside if statement, because // there may be a goto that goes into if statement. if (hasReachableLabel()) { return StatementPtr(); } bool changed = false; int i; int j; Variant value; bool hoist = false; for (i = 0; i < m_stmts->getCount(); i++) { IfBranchStatementPtr branch = dynamic_pointer_cast<IfBranchStatement>((*m_stmts)[i]); ExpressionPtr condition = branch->getCondition(); if (!condition) { StatementPtr stmt = branch->getStmt(); if (stmt) { if (!i && ((getFunctionScope() && !getFunctionScope()->inPseudoMain()) || !stmt->hasDecl())) { hoist = true; break; } if (stmt->is(KindOfIfStatement)) { StatementListPtr sub_stmts = dynamic_pointer_cast<IfStatement>(stmt)->m_stmts; m_stmts->removeElement(i); changed = true; for (j = 0; j < sub_stmts->getCount(); j++) { m_stmts->insertElement((*sub_stmts)[j], i++); } } } break; } else if (condition->getEffectiveScalar(value)) { if (value.toBoolean()) { hoist = !i && ((getFunctionScope() && !getFunctionScope()->inPseudoMain()) || !branch->hasDecl()); break; } else if (!condition->hasEffect()) { m_stmts->removeElement(i--); changed = true; } else if (branch->getStmt()) { branch->clearStmt(); changed = true; } } } if (!changed && i && i == m_stmts->getCount()) return StatementPtr(); // either else branch or if (true) branch without further declarations i++; while (i < m_stmts->getCount()) { m_stmts->removeElement(i); changed = true; } // if there is only one branch left, return stmt. if (hoist) { IfBranchStatementPtr branch = dynamic_pointer_cast<IfBranchStatement>((*m_stmts)[0]); return branch->getStmt() ? branch->getStmt() : NULL_STATEMENT(); } else if (m_stmts->getCount() == 0) { return NULL_STATEMENT(); } else { return changed ? static_pointer_cast<Statement>(shared_from_this()) : StatementPtr(); } }
void ClassVariable::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { // bail out early if possible switch (cg.getContext()) { case CodeGenerator::CppConstructor: case CodeGenerator::CppInitializer: if (m_modifiers->isStatic()) return; break; default: return; } ClassScopePtr scope = getClassScope(); bool derivFromRedec = scope->derivesFromRedeclaring() && !m_modifiers->isPrivate(); for (int i = 0; i < m_declaration->getCount(); i++) { ExpressionPtr exp = (*m_declaration)[i]; SimpleVariablePtr var; TypePtr type; Symbol *sym; ExpressionPtr value; bool initInCtor = false; bool initInInit = false; getCtorAndInitInfo(exp, initInCtor, initInInit, var, type, sym, value); bool isAssign = exp->is(Expression::KindOfAssignmentExpression); bool isValueNull = isAssign ? value->isLiteralNull() : false; switch (cg.getContext()) { case CodeGenerator::CppConstructor: if (initInCtor) { if (!cg.hasInitListFirstElem()) { cg.setInitListFirstElem(); } else { cg_printf(", "); } if (isAssign) { if (isValueNull) { cg_printf("%s%s(%s)", Option::PropertyPrefix, var->getName().c_str(), Type::IsMappedToVariant(type) ? "Variant::nullInit" : ""); } else { assert(value); assert(value->is(Expression::KindOfScalarExpression)); cg_printf("%s%s(", Option::PropertyPrefix, var->getName().c_str()); value->outputCPP(cg, ar); cg_printf(")"); } } else { if (type->is(Type::KindOfVariant)) { cg_printf("%s%s(Variant::nullInit)", Option::PropertyPrefix, var->getName().c_str()); } else { const char *initializer = type->getCPPInitializer(); assert(initializer); cg_printf("%s%s(%s)", Option::PropertyPrefix, var->getName().c_str(), initializer); } } } break; case CodeGenerator::CppInitializer: if (initInInit) { if (isAssign) { value->outputCPPBegin(cg, ar); if (derivFromRedec) { cg_printf("%sset(", Option::ObjectPrefix); cg_printString(var->getName(), ar, shared_from_this()); cg_printf(", "); value->outputCPP(cg, ar); cg_printf(")"); } else if (isValueNull) { cg_printf("setNull(%s%s)", Option::PropertyPrefix, var->getName().c_str()); } else { cg_printf("%s%s = ", Option::PropertyPrefix, var->getName().c_str()); value->outputCPP(cg, ar); } cg_printf(";\n"); value->outputCPPEnd(cg, ar); } else { if (derivFromRedec) { cg_printf("%sset(", Option::ObjectPrefix); cg_printString(var->getName(), ar, shared_from_this()); cg_printf(", null_variant);\n"); } else { if (type->is(Type::KindOfVariant)) { cg_printf("setNull(%s%s);\n", Option::PropertyPrefix, var->getName().c_str()); } else { const char *initializer = type->getCPPInitializer(); assert(initializer); cg_printf("%s%s = %s;\n", Option::PropertyPrefix, var->getName().c_str(), initializer); } } } } break; default: break; } } }
ReturnStatement::ReturnStatement (STATEMENT_CONSTRUCTOR_PARAMETERS, ExpressionPtr exp) : Statement(STATEMENT_CONSTRUCTOR_PARAMETER_VALUES(ReturnStatement)), m_exp(exp) { if (exp) exp->setContext(Expression::ReturnContext); }
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; }
bool BinaryOpExpression::canonCompare(ExpressionPtr e) const { return Expression::canonCompare(e) && getOp() == static_cast<BinaryOpExpression*>(e.get())->getOp(); }
bool SimpleVariable::canonCompare(ExpressionPtr e) const { return Expression::canonCompare(e) && getName() == static_cast<SimpleVariable*>(e.get())->getName(); }
bool StatementList::mergeConcatAssign(AnalysisResultPtr ar) { 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(getLocation(), Expression::KindOfBinaryOpExpression, exp1, exp2, '.')); } if (isAssignment) { exp = AssignmentExpressionPtr (new AssignmentExpression(exp->getLocation(), Expression::KindOfAssignmentExpression, var, exp1, false)); } else { exp = BinaryOpExpressionPtr (new BinaryOpExpression(getLocation(), Expression::KindOfBinaryOpExpression, var, exp1, T_CONCAT_EQUAL)); } expStmt = ExpStatementPtr (new ExpStatement(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; } }
void Dictionary::record(ExpressionPtr e) { e->setCanonID(m_idMap.size()); m_idMap.push_back(e); }
void ExprDict::visit(ExpressionPtr e) { assert(m_canonIdMap.size() == m_canonTypeMap.size()); if (m_am.insertForDict(e)) { // we've never seen e's structure before, so record it record(e); if (e->getCanonID() >= m_canonTypeMap.size()) { m_canonTypeMap.resize(e->getCanonID() + 1); m_canonIdMap.resize(e->getCanonID() + 1); } m_canonTypeMap[e->getCanonID()] = TypePtrIdxPair(extractTypeAssertion(e), e->getCanonID()); m_canonIdMap[e->getCanonID()] = e->getCanonID(); } else if (e->isTypeAssertion()) { TypePtrIdxPairVec types; assert(isCanonicalStructure(e->getCanonID())); getTypes(e->getCanonID(), types); TypePtrIdxPair entry; if (containsAssertion(e->getAssertedType(), types, entry)) { e->setCanonID(entry.second); } else { // new type assertion seen, record it int oldId = e->getCanonID(); assert(isCanonicalStructure(oldId)); record(e); // insert it into the list if (e->getCanonID() >= m_canonTypeMap.size()) { m_canonTypeMap.resize(e->getCanonID() + 1); m_canonIdMap.resize(e->getCanonID() + 1); } m_canonIdMap[e->getCanonID()] = oldId; TypePtrIdxPair &head = m_canonTypeMap[oldId]; int oldSecond = head.second; head.second = e->getCanonID(); m_canonTypeMap[e->getCanonID()] = TypePtrIdxPair(e->getAssertedType(), oldSecond); } } }
/* 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 ExprDict::propagate(ExpressionPtr e) { return m_avlExpr[e->getCanonID()]; }
void SimpleFunctionCall::analyzeProgram(AnalysisResultPtr ar) { if (m_class) { m_class->analyzeProgram(ar); setDynamicByIdentifier(ar, m_name); } else { if (m_className.empty()) { addUserFunction(ar, m_name); } else if (m_className != "parent") { addUserClass(ar, m_className); } else { m_parentClass = true; } } if (ar->getPhase() == AnalysisResult::AnalyzeInclude) { CHECK_HOOK(onSimpleFunctionCallAnalyzeInclude); ConstructPtr self = shared_from_this(); // We need to know the name of the constant so that we can associate it // with this file before we do type inference. if (!m_class && m_className.empty() && m_type == DefineFunction) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>((*m_params)[0]); string varName; if (name) { varName = name->getIdentifier(); if (!varName.empty()) { ar->getFileScope()->declareConstant(ar, varName); } } // handling define("CONSTANT", ...); if (m_params && m_params->getCount() >= 2) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>((*m_params)[0]); string varName; if (name) { varName = name->getIdentifier(); if (!varName.empty()) { ExpressionPtr value = (*m_params)[1]; ConstantTablePtr constants = ar->findConstantDeclarer(varName)->getConstants(); if (constants != ar->getConstants()) { constants->add(varName, NEW_TYPE(Some), value, ar, self); if (name->hasHphpNote("Dynamic")) { constants->setDynamic(ar, varName); } } } } } } if (m_type == UnserializeFunction) { ar->forceClassVariants(); } } if (ar->getPhase() == AnalysisResult::AnalyzeAll) { // Look up the corresponding FunctionScope and ClassScope // for this function call { FunctionScopePtr func; ClassScopePtr cls; if (!m_class && m_className.empty()) { func = ar->findFunction(m_name); } else { cls = ar->resolveClass(m_className); if (cls) { if (m_name == "__construct") { func = cls->findConstructor(ar, true); } else { func = cls->findFunction(ar, m_name, true, true); } } } if (func && !func->isRedeclaring()) { if (m_funcScope != func) { m_funcScope = func; Construct::recomputeEffects(); } } if (cls && !cls->isRedeclaring()) m_classScope = cls; } // check for dynamic constant and volatile function/class if (!m_class && m_className.empty() && (m_type == DefinedFunction || m_type == FunctionExistsFunction || m_type == ClassExistsFunction || m_type == InterfaceExistsFunction) && m_params && m_params->getCount() >= 1) { ExpressionPtr value = (*m_params)[0]; if (value->isScalar()) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>(value); if (name && name->isLiteralString()) { string symbol = name->getLiteralString(); switch (m_type) { case DefinedFunction: { ConstantTablePtr constants = ar->getConstants(); if (!constants->isPresent(symbol)) { // user constant BlockScopePtr block = ar->findConstantDeclarer(symbol); if (block) { // found the constant constants = block->getConstants(); // set to be dynamic constants->setDynamic(ar, symbol); } } break; } case FunctionExistsFunction: { FunctionScopePtr func = ar->findFunction(Util::toLower(symbol)); if (func && func->isUserFunction()) { func->setVolatile(); } break; } case InterfaceExistsFunction: case ClassExistsFunction: { ClassScopePtr cls = ar->findClass(Util::toLower(symbol)); if (cls && cls->isUserClass()) { cls->setVolatile(); } break; } default: ASSERT(false); } } } } } if (m_params) { if (ar->getPhase() == AnalysisResult::AnalyzeAll) { if (m_funcScope) { ExpressionList ¶ms = *m_params; int mpc = m_funcScope->getMaxParamCount(); for (int i = params.getCount(); i--; ) { ExpressionPtr p = params[i]; if (i < mpc ? m_funcScope->isRefParam(i) : m_funcScope->isReferenceVariableArgument()) { p->setContext(Expression::RefValue); } else if (!(p->getContext() & Expression::RefParameter)) { p->clearContext(Expression::RefValue); } } } else { FunctionScopePtr func = ar->findFunction(m_name); if (func && func->isRedeclaring()) { FunctionScope::RefParamInfoPtr info = FunctionScope::GetRefParamInfo(m_name); if (info) { for (int i = m_params->getCount(); i--; ) { if (info->isRefParam(i)) { m_params->markParam(i, canInvokeFewArgs()); } } } } else { m_params->markParams(false); } } } m_params->analyzeProgram(ar); } }